/* The shell's own components. The tokens, faces and base they are written
   against live in common/static/design.css, which every module serves too.

   Layout is concept 1a: pill chrome, a greeting, an anchor grid. Colour lives
   on tokens in design.css: Light, Dim, Black and Sepia (design guide §2). */

html, body {
  height: 100%;
  overflow-x: clip;
}
@supports not (overflow-x: clip) {
  html, body {
    overflow-x: hidden;
  }
}

body:has(.home) {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  max-height: 100dvh;
  overflow: hidden;
  overflow-y: hidden;
}

/* Decorative living timeline. Never captures clicks (Ticket G). Width is
   the viewport, not 116%, so it cannot drag the page sideways (B41a-UI). */
.ground-drift {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  max-width: 100vw;
  height: 46%;
  opacity: var(--ground);
  animation: drift 52s linear infinite alternate;
  pointer-events: none;
  z-index: 0;
}

@keyframes drift {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-6%, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .ground-drift {
    animation: none !important;
  }
}

/* Queue depth never changes this budget inside the 100dvh home (R-71). */
.queue-strip {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  height: 6.5rem;
  box-sizing: border-box;
  padding: .625rem var(--space-3);
  margin: 0 0 clamp(8px, 1.2vh, 14px);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--pane);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  box-shadow: var(--panel-shadow);
  overflow: hidden;
}

.queue-strip__head,
.queue-strip__actions,
.qcell__marks,
.qmark,
.qmark__bars {
  display: flex;
  align-items: center;
}

.queue-strip__head {
  gap: var(--space-3);
  min-width: 0;
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: var(--lh-small);
}

.queue-strip__head h2 {
  margin: 0;
  color: var(--text-2);
  font: inherit;
  font-weight: 600;
}

.queue-strip__actions {
  gap: .875rem;
  margin-left: auto;
}

.queue-strip__open,
.queue-strip__mine {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.queue-strip__mine { color: var(--crit); }
.queue-strip__open:hover,
.queue-strip__mine:hover { border-bottom-color: currentColor; }
.queue-strip__open:focus-visible,
.queue-strip__mine:focus-visible,
.queue-strip__tasks:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: .1875rem;
}

.queue-strip__tasks {
  display: flex;
  flex: 1;
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-control);
  background: transparent;
}
.queue-strip__tasks::-webkit-scrollbar { display: none; }

.qcell {
  flex: 1 0 10rem;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .1875rem;
  padding: .375rem .625rem;
  border-right: 1px solid var(--border-soft);
}
.qcell:last-child { border-right: 0; }

.qcell__marks { gap: var(--space-2); min-width: 0; }
.qmark { gap: .3125rem; flex: none; }
.qmark__bars { align-items: flex-end; gap: .125rem; height: .8125rem; }
.qmark__bar {
  width: .1875rem;
  border-radius: .0625rem;
  background: var(--border-soft);
}
.qmark__bar:nth-child(1) { height: .4375rem; }
.qmark__bar:nth-child(2) { height: .625rem; }
.qmark__bar:nth-child(3) { height: .8125rem; }
.qmark--low .qmark__bar:nth-child(1) { background: var(--text-2); }
.qmark--medium .qmark__bar:nth-child(-n + 2) { background: var(--warn); }
.qmark--high .qmark__bar { background: var(--crit); }

.qmark__label,
.qcell__meta,
.qcell__state {
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: var(--lh-small);
}
.qmark--high .qmark__label { color: var(--crit); }
.qmark--medium .qmark__label { color: var(--warn); }
.qmark--low .qmark__label,
.qcell__meta { color: var(--text-2); }
.qcell__state { color: var(--text-2); }

.qcell__title {
  display: -webkit-box;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  color: var(--text);
  font-size: var(--t-small);
  line-height: 1.25;
  overflow-wrap: anywhere;
}

.qcell--overflow {
  flex: 0 0 4.25rem;
  align-items: center;
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  font-variant-numeric: tabular-nums;
}

.queue-strip__state {
  display: flex;
  align-items: center;
  flex: 1;
  margin: 0;
  padding: 0 .625rem;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-control);
  color: var(--text-2);
  font-size: var(--t-small);
}
.queue-strip__state strong { margin-right: var(--space-1); color: var(--warn); }

.doc-prompts {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0 0 clamp(8px, 1.2vh, 14px);
}

.doc-prompt {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto auto;
  column-gap: var(--space-3);
  row-gap: 2px;
  align-items: baseline;
  padding: 10px 14px;
  border: 1px solid var(--warn);
  border-left-width: 4px;
  border-radius: var(--radius-control);
  background: var(--warn-soft);
}

.doc-prompt--overdue {
  border-color: var(--crit);
  background: var(--crit-soft);
}

.doc-prompt__kicker {
  grid-column: 1;
  grid-row: 1;
  margin: 0;
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--warn);
  font-family: var(--font-mono);
}

.doc-prompt--overdue .doc-prompt__kicker {
  color: var(--crit);
}

.doc-prompt__name {
  grid-column: 2;
  grid-row: 1 / span 2;
  margin: 0;
  font-size: var(--t-lead);
  line-height: var(--lh-lead);
  font-weight: 600;
  color: var(--text);
  min-width: 0;
}

.doc-prompt__when {
  grid-column: 1;
  grid-row: 2;
  margin: 0;
  font-size: var(--t-small);
  font-family: var(--font-mono);
  color: var(--text-2);
}

.doc-prompt__action {
  grid-column: 3;
  grid-row: 1 / span 2;
  align-self: center;
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.doc-prompt__action:hover {
  border-bottom-color: var(--accent);
}

.doc-prompt__action:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 3px;
}

@media (max-width: 719px) {
  .doc-prompt {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
  }
  .doc-prompt__kicker,
  .doc-prompt__name,
  .doc-prompt__when,
  .doc-prompt__action {
    grid-column: 1;
    grid-row: auto;
  }
}

.home {
  /* The room under the grid and the gap between the page's parts, named so the
     footer, which sits inside the home, can reproduce the one by the other. */
  --home-pad: clamp(0.75rem, 1.5vh, 1.25rem);
  --home-gap: clamp(0.375rem, 1vh, 0.75rem);
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  overflow: hidden;
  max-width: 90rem;
  width: 100%;
  margin: 0 auto;
  padding: 0 clamp(1rem, 2.5vw, 1.75rem) var(--home-pad);
  gap: var(--home-gap);
  position: relative;
  z-index: 1;
}

/* The deck and the front slide are only there for a phone to
   lay out (the max-width 44rem block below). Everywhere else they are not
   boxes, so the greeting, the grid, the pager and the footer are the home's
   own flex items, as they were before the deck. */
.home__deck,
.home__front { display: contents; }
.home__deck > .deck__clone,
.queue-strip__more,
.doc-due { display: none; }

/* The footer is the front's, so it is drawn inside the home. Ordered last, it
   sits where the page put it before: it cancels the gap a flex item has above
   it and brings the bottom padding the home had, as its own. Two terms rather
   than one difference, because each is rounded to the layout grid on its own
   and a difference of them is not the same as the two. */
body:has(.home) .home .site-foot {
  order: 1;
  margin-top: calc(-1 * var(--home-gap));
  padding-top: var(--home-pad);
  padding-inline: 0;
}
.home:has(.site-foot) { padding-bottom: 0; }

.greeting {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-5);
  flex: none;
  padding: 4px 0 clamp(4px, 1vh, 10px);
}

.greeting__main {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.greeting h1,
.greeting__headline {
  margin: 0;
  font-size: var(--t-display);
  font-weight: 400;
  line-height: var(--lh-display);
  letter-spacing: -.035em;
  text-wrap: balance;
  /* Large type on a photograph: a soft halo in the ground colour holds 3:1
     where the picture is busy. */
  text-shadow: 0 0 12px var(--bg);
}

/* The date and the one-line status sit on a pane, not on the photograph:
   14px text needs 4.5:1, and no text colour can promise that over a picture
   (AGENTS.md §5 rule 5). */
.greeting__meta {
  margin: 0 0 var(--space-1);
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: var(--lh-small);
  color: var(--text);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  text-align: right;
  white-space: nowrap;
}

/* Four equal columns and three rows to a page on desktop (ADR-0046). The
   server places every tile (host/layout.py), so rows scale within the
   viewport budget and nothing here decides where a tile goes. */
.grid {
  /* The column count, for the script that re-pages to fit (gallery.js):
     computed tracks would also count implicit ones. */
  --grid-columns: 4;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-flow: row dense;
  grid-template-rows: repeat(3, minmax(min-content, 1fr));
  gap: clamp(10px, 1.5vh, 16px);
  flex: 1;
  min-height: 0;
  max-width: none;
  margin: 0;
  padding: 0;
  /* The rows were `minmax(0, 1fr)`, so a short viewport crushed them below
     their own content and the content painted straight out of the card and
     over the row beneath -- a tile is a flex column whose items cannot shrink
     past their text. `min-content` is that floor stated as what it actually
     is, rather than as a pixel number somebody has to remember to raise when
     a tile gains a line: the row never goes below what its tallest tile needs,
     and still takes 1fr of whatever is left over. Containment is unchanged and
     still the common case -- on a viewport tall enough there is no overflow to
     scroll. When there is, this scrolls rather than overlapping. */
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
  /* Reserve the gutter whether or not a scrollbar is showing. Without it the
     scrollbar's arrival narrows the grid, which rewraps the text in the tiles,
     which changes their height, which can take the scrollbar away again --
     a layout that oscillates instead of settling. It also makes the measured
     width the same on a platform with overlay scrollbars and one without,
     which is what keeps `test_ui_layout` giving the same answer on a laptop
     and in CI. */
  scrollbar-gutter: stable;
}

/* Multi-page desktop gallery (Batch B36) */
.gallery {
  display: flex;
  width: 100%;
  height: 100%;
  flex: 1;
  min-height: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  scroll-behavior: smooth;
}
.gallery::-webkit-scrollbar {
  display: none;
}

.gallery__page {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  min-width: 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Rows the script fitted to the window (gallery.js, ADR-0046). The server's
   layout is three rows; a shorter window gets fewer, and the rest go to the
   next page rather than under a scrollbar. */
.gallery[data-rows="1"] .grid { grid-template-rows: repeat(1, minmax(min-content, 1fr)); }
.gallery[data-rows="2"] .grid { grid-template-rows: repeat(2, minmax(min-content, 1fr)); }
.gallery[data-rows="3"] .grid { grid-template-rows: repeat(3, minmax(min-content, 1fr)); }
.gallery[data-rows="4"] .grid { grid-template-rows: repeat(4, minmax(min-content, 1fr)); }
.gallery[data-rows="1"] .tile[data-size] { grid-row-end: span 1; }

/* -- the pager -----------------------------------------------------------
   One stop per page on a pill track, and a thumb that slides with the
   scroll, so where you are is a position, not a number. An arrow that has
   somewhere to go is filled in the accent: the direction there is more. */
.pager {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding-top: var(--space-1);
}
.pager[data-pages="1"] { visibility: hidden; }

/* An arrow is a glyph with an accessible name, but the button still takes
   the type floor, not the browser's 13.3px button default (issue 169). The
   box stays 2.5rem. */
.pager__arrow {
  font: inherit;
  font-size: var(--t-small);
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  color: var(--text-2);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out),
    opacity var(--duration-fast) var(--ease-out),
    transform var(--duration-fast) var(--ease-out);
}
.pager__arrow::after { content: ""; position: absolute; inset: -2px; }
.pager__arrow svg {
  width: 1.25rem;
  height: 1.25rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pager__arrow:not(:disabled) {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-accent);
}
.pager__arrow:not(:disabled):hover { filter: brightness(1.06); }
.pager__arrow:not(:disabled):active { transform: scale(0.94); }
.pager__arrow:disabled { opacity: 0.45; cursor: default; }

.pager__track {
  position: relative;
  display: flex;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
}
/* A segment per page. The gutter between segments is padding, not gap, so
   the thumb moves exactly one segment per page. */
/* A stop is a link with only an accessible name; it still takes the text
   tokens and the floor, not the browser's link blue (issue 169). */
.pager__stop {
  color: var(--text);
  font-size: var(--t-small);
  position: relative;
  width: 2.75rem;
  height: 8px;
  padding: 0 2px;
  box-sizing: border-box;
  background: var(--border-strong);
  background-clip: content-box;
  border-radius: var(--radius-pill);
}
.pager__stop::before { content: ""; position: absolute; inset: -16px 0; }
.pager__stop:hover { background-color: var(--text-2); }
.pager__thumb {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 2.75rem;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--accent-fill);
  box-shadow: 0 0 10px var(--accent-glow);
  pointer-events: none;
}
.pager__arrow:focus-visible,
.pager__stop:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

/* ---------- chrome ---------- */

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.chrome {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--pane);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  border-bottom: 1px solid var(--border);
}

.chrome__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  max-width: 90rem;
  margin: 0 auto;
  min-height: clamp(3.25rem, 5vh, 4rem);
  padding: clamp(8px, 1.2vh, 14px) clamp(16px, 2.5vw, 28px);
}

.chrome__end {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
  padding: 6px 14px;
  background: var(--pane-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text);
  text-decoration: none;
}

.brand__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}

.brand__name {
  font-size: var(--t-lead);
  font-weight: 600;
  letter-spacing: -.02em;
}

.brand__sub {
  font-family: var(--font-mono);
  font-size: var(--t-small);
  color: var(--text-2);
}

.brand:hover { border-color: var(--accent); }

.nav {
  position: relative;
  display: flex;
  min-width: 0;
  padding: var(--space-1);
  background: var(--pane-strong);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

/* The pills scroll inside the nav's ground, so its ends stay drawn. A pill
   an edge cuts is hidden until it is whole (nav.js, issue 183), so every
   label on screen is a whole word; it stays in the tab order and is shown
   the moment it scrolls in. The space it leaves carries the cue: a shade
   on each side that has more. */
.nav__strip {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
  overflow-x: auto;
  border-radius: var(--radius-pill);
  scrollbar-width: none;
}

.nav__item[data-cut] {
  opacity: 0;
  pointer-events: none;
}

.nav::before,
.nav::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 1;
  width: var(--space-6);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-out);
}

.nav::before { left: 0; background: linear-gradient(to left, transparent, color-mix(in srgb, var(--text) 18%, transparent)); }
.nav::after { right: 0; background: linear-gradient(to right, transparent, color-mix(in srgb, var(--text) 18%, transparent)); }
.nav[data-more~="start"]::before,
.nav[data-more~="end"]::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .nav::before, .nav::after { transition: none; }
}

.nav__strip::-webkit-scrollbar { display: none; }

.nav__item {
  flex: none;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  font-size: var(--t-small);
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
}

.nav__item:hover { color: var(--text); }

.nav__item--on {
  background: var(--ink);
  color: var(--ink-text);
  font-weight: 500;
}

/* ---------- site footer (B65 §3.5) ----------
   Lattice has no footer, so this is the top nav's pill turned into one: the
   same pane, edge and radius, `small` type in `text-2`, and 48px targets. It
   sits on a pane because text straight on a photograph fails contrast. */

.site-foot {
  flex: none;
  display: flex;
  justify-content: center;
  padding: var(--space-5) clamp(16px, 2.5vw, 28px) var(--space-6);
}

body:has(.home) .site-foot { padding-block: 0 var(--space-2); }

/* The home never scrolls, so on a short screen the footer is paid for in tile
   height. There it takes Lattice's `sm` size (40px, for dense rows), loses the
   pill's inner padding, and replaces the home's own bottom padding. */
@media (max-height: 760px) {
  body:has(.home) .home { --home-pad: 0px; }
  body:has(.home) .site-foot { padding-block: 0 var(--space-1); }
  body:has(.home) .site-foot__nav { padding: 0; }
  body:has(.home) .site-foot__link { min-height: 2.5rem; }
}

.site-foot__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-1);
  max-width: 100%;
  padding: var(--space-1);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
}

.site-foot__link {
  display: inline-flex;
  align-items: center;
  min-height: 3rem;
  padding: 0 var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--t-small);
  color: var(--text-2);
  text-decoration: none;
}

.site-foot__link:hover { color: var(--text); }

.appearance {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 3px;
  background: var(--pane-strong);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
}

.appearance__opt {
  font: inherit;
  font-size: var(--t-small);
  color: var(--text);
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.appearance__swatch {
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  transition: transform var(--duration-fast) var(--ease-out);
}
.appearance__swatch--light { background: linear-gradient(135deg, var(--swatch-light) 0 55%, var(--swatch-light-accent) 55%); }
.appearance__swatch--dim   { background: linear-gradient(135deg, var(--swatch-dim) 0 55%, var(--swatch-dim-accent) 55%); }
.appearance__swatch--black { background: linear-gradient(135deg, var(--swatch-black) 0 55%, var(--swatch-black-accent) 55%); }
.appearance__swatch--sepia { background: linear-gradient(135deg, var(--swatch-sepia) 0 55%, var(--swatch-sepia-accent) 55%); }
.appearance__swatch--system { background: linear-gradient(135deg, var(--swatch-light) 0 50%, var(--swatch-dim) 50%); }

.appearance__opt:hover .appearance__swatch { transform: scale(1.1); }

.appearance__opt--on .appearance__swatch {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

.appearance__opt:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* ---------- profile menu ---------- */

.profile { position: relative; flex: none; }
.profile__button { list-style: none; cursor: pointer; }
.profile__button::-webkit-details-marker { display: none; }

.profile__monogram {
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--ink);
  border: 1px solid var(--ink);
  color: var(--ink-text);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  font-weight: 500;
  letter-spacing: .02em;
}

.profile__button:hover .profile__monogram { border-color: var(--accent); }

.profile__button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 50%;
}

.menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 20;
  min-width: 240px;
  padding: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-overlay);
}

.menu__who { margin: var(--space-1) var(--space-2) var(--space-2); display: flex; flex-direction: column; gap: 2px; }

.menu__kind {
  font-size: var(--t-small);
  color: var(--text-2);
}

.menu__id {
  font-family: var(--font-mono);
  font-size: var(--t-small);
  color: var(--text);
  overflow-wrap: anywhere;
}

.menu__note {
  margin: var(--space-1) var(--space-2) var(--space-2);
  font-size: var(--t-small);
  line-height: 1.45;
  color: var(--warn);
}

.menu__item {
  display: block;
  padding: var(--space-2);
  border-radius: 2px;
  font-size: var(--t-small);
  color: var(--text);
  text-decoration: none;
}

.menu__item:hover { background: var(--surface-2); }
.menu__item:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

/* ---------- module frame ---------- */

.frame {
  position: relative;
  z-index: 1;
  height: calc(100dvh - var(--chrome-height));
}

.frame__pane {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  /* Not `var(--bg)`. This is the second of the two grounds that used to cover
     the shell's background: the iframe element paints one, and the document
     inside it paints another. Both have to go for the photograph to reach a
     module page -- `common/static/embedded.js` handles the document. */
  background: transparent;
}

/* ---------- page ---------- */

.page {
  max-width: 60rem;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5) var(--space-8);
}

/* Settings holds a background picker beside the themes; it earns more
   width than prose pages, and no more than reads comfortably. */
.page--settings { max-width: 68rem; }

.page h1 {
  margin: 0 0 var(--space-5);
  font-size: var(--t-metric);
  font-weight: 400;
  letter-spacing: -.03em;
}

.card {
  padding: 20px;
  margin-bottom: var(--space-4);
  background: var(--pane-strong);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
}

.card h2 {
  margin: 0 0 var(--space-1);
  font-size: var(--t-body);
  font-weight: 600;
}

/* A card that is one control: its title and the control share a row, and
   wrap onto two on a phone. */
.card--inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
}
.card--inline h2 { margin: 0; }
.card--inline .field { margin: 0; flex: 0 1 22rem; }
.card--inline .field select { max-width: none; }
.card--inline > .muted { flex: 1 1 100%; margin: 0; }

.card p { margin: 0 0 var(--space-3); }
.card p:last-child { margin-bottom: 0; }
.muted { color: var(--text-2); font-size: var(--t-small); }

/* ---------- privacy (B65, R-23g) ----------
   A notice is read, so it gets one reading column (AGENTS.md §5 rule 7). What
   a person has agreed to is a state, drawn: a dot, then the date. */
/* The title sits on the card and the card in the middle of the page, so
   the two read as one column at any width (issue 181). A phone's card is
   already the page's width. */
.privacy__text, .privacy__action { margin-inline: auto; }
.privacy__text { max-width: 65ch; }
.privacy__text h1 { text-wrap: balance; }
.privacy__text section + section { margin-top: var(--space-5); }
.privacy__text h2 { text-wrap: balance; }
.privacy__text ul { margin: 0 0 var(--space-3); padding-left: 1.25em; }
.privacy__text li + li { margin-top: var(--space-2); }
.privacy__action { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); max-width: 65ch; }
.privacy__action form { margin: 0; }
.privacy__decline { flex: 1 1 100%; }
.privacy__decline summary { min-height: 3rem; display: flex; align-items: center; cursor: pointer; color: var(--text-2); }
.privacy__leave { margin-top: var(--space-3); }

.agreed {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--t-small);
}
.agreed__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); flex: none; }

.privacy__warning { max-width: 65ch; border-color: var(--crit); }
.privacy__warning h2 { display: flex; align-items: center; gap: var(--space-2); text-wrap: balance; }
.privacy__glyph {
  display: inline-grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  border: 1.75px solid var(--crit);
  border-radius: 50%;
  color: var(--crit);
  font-family: var(--font-mono);
  font-size: var(--t-small);
}
.privacy__warning ul { margin: var(--space-2) 0 var(--space-3); padding-left: 1.25em; }
.check {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 3rem;
  margin-bottom: var(--space-3);
  cursor: pointer;
}
.check input { width: 1.25rem; height: 1.25rem; flex: none; accent-color: var(--crit); }
.privacy__buttons { display: flex; flex-wrap: wrap; gap: var(--space-3); }

.privacy-card__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-block: var(--space-2);
}
.privacy-card__row + .privacy-card__row { border-top: 1px solid var(--border-soft); }
.privacy-card__row .settings__sub { margin-top: 0; }

/* ---------- connect (R-23h) ---------- */
.connect code { overflow-wrap: anywhere; }
.connect__steps { margin: var(--space-2) 0 var(--space-3); padding-left: 1.25em; }
.connect__steps li + li { margin-top: 6px; }

.choices {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: var(--space-2);
  align-content: start;
}

/* Each palette is shown as itself (AGENTS.md §5): its swatch, its name. The
   radio is still what is operated; it is hidden, and the card carries the
   selected state and the focus ring. */
.choice {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 2.75rem;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  font-size: var(--t-body);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-out);
}
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice:hover { border-color: var(--border-strong); }
.choice:has(input:checked) { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.choice:has(input:checked) .choice__name { font-weight: 600; }
.choice:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; }
.choice .appearance__swatch { flex: none; }

.appearance-grid { display: grid; gap: var(--space-5); margin-top: var(--space-3); }
.appearance-grid .settings__sub { margin-top: 0; }
@media (min-width: 60rem) {
  /* Theme as a list beside the backgrounds it chooses between. */
  .appearance-grid { grid-template-columns: 13rem 1fr; align-items: start; }
  .appearance-grid__grounds { min-width: 0; }
  .appearance-grid .choices { grid-template-columns: 1fr; }
}

.button {
  min-height: 2.75rem;
  padding: 10px 20px;
  background: var(--accent-fill);
  border: 1px solid var(--accent-fill);
  border-radius: var(--radius-pill);
  color: var(--on-accent);
  font-family: var(--font-ui);
  font-size: var(--t-body);
  font-weight: 600;
  cursor: pointer;
}

.button:hover { filter: brightness(1.08); }
.button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.notice {
  margin: 0 0 var(--space-4);
  padding: 10px 14px;
  border-radius: var(--radius-control);
  font-size: var(--t-small);
}

.notice--ok { background: var(--accent-soft); color: var(--accent); }
.notice--error { background: var(--surface-2); color: var(--crit); }

/* ---------- tile ---------- */

.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 0.8vh, 8px);
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  padding: clamp(12px, 1.8vh, 18px);
  background: var(--pane);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--panel-shadow);
  color: inherit;
  text-decoration: none;
  transition: background 140ms ease-out, border-color 140ms ease-out;
}

/* Deliberately NOT `overflow: hidden` on `.tile`, tempting as a clip is: a
   scroll container's automatic minimum size is zero, so hiding the overflow
   also tells the grid the tile can be any height at all -- which collapsed the
   rows to an equal share of the viewport and put *more* content outside the
   card than before. The row floor is the fix; clipping would have hidden the
   evidence that it was not working. */

.tile:hover {
  background: var(--pane-strong);
  border-color: var(--accent);
}

/* Glass is one token per palette (--pane, design guide §2), tuned so text
   holds contrast over every photograph in that palette's family. The old
   per-theme 32% mix is gone: it failed --text-2 over the Dim photographs. */

.tile__hit {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  color: inherit;
  text-decoration: none;
}

.tile > :not(.tile__hit) {
  position: relative;
  z-index: 1;
  pointer-events: none;
}

.tile:has(.tile__hit:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.tile__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  flex: none;
}

.tile__body {
  display: flex;
  flex-direction: column;
  gap: inherit;
  min-width: 0;
  min-height: 0;
  flex: 1;
  justify-content: flex-start;
}

/* Motif: the instrument fills leftover space, centred in its well; type is
   a left-aligned readout under or beside it. A two-column hero made the
   ring a square of the *narrow* column, so leftover height sat empty.
   Slack goes into the graphic, not into padding. */

.tile__name {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--t-body);
  font-weight: 600;
  color: var(--text);
}

.tile__badge {
  font-family: var(--font-mono);
  font-size: var(--t-small);
  font-weight: 500;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 1px 6px;
  color: var(--text-2);
}

.tile__state {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  color: var(--good);
}

.tile--stale .tile__state,
.tile--warn .tile__state { color: var(--warn); }
.tile--down .tile__state { color: var(--crit); }

/* The word beside the dot on every tile, not only the large ones: a dot
   alone is colour alone, and a 1px clip hid the word (issue 170). When the
   head runs out of room the pair wraps under the name rather than clipping. */
.tile__head { flex-wrap: wrap; row-gap: var(--space-1); }
.tile__state { white-space: nowrap; }

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--good);
}

.dot--stale { background: var(--warn); }
.dot--warn  { background: var(--warn); }
.dot--down  { background: var(--crit); }

.tile__metric {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.tile__value {
  font-size: var(--t-metric);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -.03em;
}

.tile--standard.tile--large .tile__value {
  font-size: var(--t-metric-hero);
  line-height: .88;
  letter-spacing: -.04em;
}

.tile__unit {
  font-size: var(--t-small);
  color: var(--text-2);
}

.tile__metric--absent {
  font-size: var(--t-h3);
  color: var(--text-2);
}

.tile__label,
.tile__context {
  margin: 0;
  font-size: var(--t-small);
  color: var(--text-2);
}

.tile__label { margin-top: 2px; }
.tile__context { margin: 0; color: var(--text-2); }

/* Today's entries under the metric (R-12c). Compact so a phone can keep them
   and a short desktop window can drop them without overflowing the card. */
.tile__log {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}
.tile__log li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
  font-size: var(--t-small);
}
.tile__log-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-2);
}
.tile__log-value {
  flex: none;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.tile__log-unit { color: var(--text-2); }
@media (max-height: 740px) {
  .tile__log { display: none; }
}

/* Ring gauge: the number sits in the shape. The well takes leftover
   column height (`flex: 1`); the SVG is the largest square that fits that
   well, capped so a very tall tile does not grow a dinner-plate ring.
   `minmax(4.5rem, 1fr)` is the floor that keeps the circle ≥48px (B33
   forbids `minmax(0, 1fr)`). `.meter` is a 10px clipped bar — this wrap
   opts out of that geometry. */
.tile__gauge {
  display: grid;
  grid-template-columns: minmax(4.5rem, 1fr);
  grid-template-rows: minmax(4.5rem, 1fr) auto;
  justify-items: center;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 4.5rem;
  margin: 0;
  width: 100%;
  max-width: 100%;
  align-self: stretch;
  overflow: visible;
}
.tile--small .tile__gauge {
  /* Span is compact; the graphic still fills the cell it was given. */
  width: 100%;
  min-height: 4.5rem;
}
.tile__gauge .gauge-wrap {
  grid-row: 1;
  grid-column: 1;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  overflow: visible;
  margin-top: 0;
  background: none;
  border-radius: 0;
  container-type: size;
  display: grid;
  place-items: center;
}
.tile__gauge .gauge {
  display: block;
  width: auto;
  height: auto;
  min-width: 4.5rem;
  min-height: 4.5rem;
  max-width: min(100%, 24rem);
  max-height: min(100%, 24rem);
  aspect-ratio: 1;
  transform: rotate(-90deg);
}
@supports (width: 1cqh) {
  .tile__gauge .gauge {
    width: min(100cqw, 100cqh, 24rem);
    height: min(100cqw, 100cqh, 24rem);
    min-width: 4.5rem;
    min-height: 4.5rem;
    max-width: 100%;
    max-height: 100%;
  }
}
.gauge__track {
  stroke: var(--border-soft);
}
.gauge__band {
  stroke: var(--good);
  opacity: 0.35;
}
.gauge__fill {
  stroke: var(--good);
  stroke-linecap: round;
}
.meter--warn .gauge__fill { stroke: var(--warn); }
.meter--crit .gauge__fill { stroke: var(--crit); }
.tile__gauge .tile__metric {
  grid-row: 1;
  grid-column: 1;
  z-index: 1;
  margin: 0;
  align-self: center;
  justify-self: center;
  flex-direction: column;
  gap: 0;
  pointer-events: none;
}
.tile__gauge .tile__value {
  font-size: var(--t-metric);
}
.tile--standard.tile--large .tile__gauge .tile__value {
  font-size: var(--t-metric-hero);
  letter-spacing: -.04em;
}
/* The ring and its number share one box, a size container, so the number
   is sized to the ring: never above its token, never below the 14px floor,
   and small enough that its characters fit inside the stroke. Plex Mono is
   0.6em a character and the ring's inner edge is 0.67 of its diameter, so a
   figure of n characters fits at about 0.9 / n of the diameter. A short one
   is bound by height instead: it and the unit line under it stack inside. */
.tile__dial {
  grid-row: 1;
  grid-column: 1;
  width: 100%;
  height: 100%;
  min-width: 4.5rem;
  min-height: 4.5rem;
  container-type: size;
  display: grid;
  place-items: center;
}
.tile__dial > * { grid-area: 1 / 1; }
.tile__dial .tile__value {
  --ring: min(100cqmin, 24rem);
  --fit: calc(var(--ring) * 0.34);
  font-size: max(var(--t-small), min(var(--t-metric), var(--fit)));
}
.tile--standard.tile--large .tile__dial .tile__value {
  font-size: max(var(--t-small), min(var(--t-metric-hero), var(--fit)));
}
.tile__dial .tile__value[data-len="3"] { --fit: calc(var(--ring) * 0.3); }
.tile__dial .tile__value[data-len="4"] { --fit: calc(var(--ring) * 0.22); }
.tile__dial .tile__value[data-len="5"] { --fit: calc(var(--ring) * 0.18); }
.tile__dial .tile__value[data-len="6"] { --fit: calc(var(--ring) * 0.15); }
.tile__dial .tile__value[data-len="7"] { --fit: calc(var(--ring) * 0.13); }
.tile__gauge .tile__label {
  grid-row: 2;
  grid-column: 1;
  margin: var(--space-1) 0 0;
  text-align: center;
}

/* Secondary figures under the metric (R-12b). They sit at --t-lead, a step
   below --t-metric, so the tile still has exactly one primary number. */
/* Flex, not an auto-fit grid: the split bar spans the row, and a spanning
   item keeps every auto-fit track open, which squeezed three figures into
   five narrow columns and wrapped their labels. */
.tile__details {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: 0;
}
.tile__details > .tile__detail { flex: 1 1 4rem; min-width: 0; }
.tile__details > .tile__split { flex: 1 0 100%; }

.tile__split {
  grid-column: 1 / -1;
  display: block;
  width: 100%;
  height: 8px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--border-soft);
}
/* `<rect>`s in an `<svg>` since B45; `fill` replaces `background`. The hue
   order is deliberate and unchanged -- 1, 3, 4, 5, 2 -- so adjacent segments
   stay distinguishable. */
.tile__split-seg--1 { fill: var(--series-1); }
.tile__split-seg--2 { fill: var(--series-3); }
.tile__split-seg--3 { fill: var(--series-4); }
.tile__split-seg--4 { fill: var(--series-5); }
.tile__split-seg--5 { fill: var(--series-2); }

.tile__detail-label {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: var(--lh-small);
  color: var(--text-2);
}

.tile__detail-value {
  margin: 3px 0 0;
  font-family: var(--font-mono);
  font-size: var(--t-lead);
  line-height: var(--lh-lead);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.tile__detail-unit {
  font-family: var(--font-ui);
  font-size: var(--t-small);
  color: var(--text-2);
}

.tile--stale .tile__detail-value { color: var(--text-2); }

.tile--stale .tile__value,
.tile--stale .tile__unit { color: var(--text-2); }

.tile__collections {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 0;
  margin-top: clamp(4px, 1vh, 12px);
  border-top: 1px solid var(--border-soft);
}

.tile__collection {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
  padding: var(--space-3) var(--space-3) var(--space-2) 14px;
  border-bottom: 1px solid var(--border-soft);
}

.tile__cover {
  flex: none;
  width: 1.75rem;
  height: 2.25rem;
  display: block;
}

.tile__cover-back {
  fill: var(--border);
}

.tile__cover-front {
  fill: color-mix(in srgb, var(--series-1) 28%, var(--surface));
  stroke: var(--border-strong);
  stroke-width: 1;
}

.tile__collection:nth-child(2) .tile__cover-front {
  fill: color-mix(in srgb, var(--series-3) 32%, var(--surface));
}

.tile__collection:nth-child(3) .tile__cover-front {
  fill: color-mix(in srgb, var(--series-4) 32%, var(--surface));
}

.tile__collection:nth-child(4) .tile__cover-front {
  fill: color-mix(in srgb, var(--series-5) 32%, var(--surface));
}

.tile__cover--empty .tile__cover-back { fill: transparent; }

.tile__cover--empty .tile__cover-front {
  fill: transparent;
  stroke: var(--border);
  stroke-dasharray: 3 2;
}

.tile__collection-copy {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.tile__collection h2 {
  margin: 0;
  color: var(--text-2);
  font-size: var(--t-small);
  font-weight: 600;
}

.tile__collection-metric {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: var(--t-lead);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

.tile__collection-unit,
.tile__collection-context {
  color: var(--text-2);
  font-size: var(--t-small);
}

.tile__collection-context {
  margin: var(--space-1) 0 0;
  overflow-wrap: anywhere;
}

/* Open-gap count rides the context line as a muted action (R-101a). The
   bordered pill is reserved for the one primary path. */
.tile__collection-gaps {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-1);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--text-2);
  font-weight: 500;
  font-size: var(--t-small);
  pointer-events: auto;
  text-decoration: none;
}
.tile__collection-gaps:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
.tile__collection-gaps--primary {
  padding: 0 var(--space-3);
  margin-top: var(--space-1);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.tile__collection--attention {
  box-shadow: inset 3px 0 0 var(--accent);
  padding-left: 14px;
}

.tile__collection .meter {
  height: 6px;
  margin-top: var(--space-2);
}

.tile__collection--degraded,
.tile__collection--empty { color: var(--text-2); }

.tile__collection--empty {
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: var(--radius-control);
}

.tile__review {
  grid-column: 1 / -1;
  margin: var(--space-1) 0 0;
}

.tile__cta {
  color: var(--accent);
  font-weight: 600;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

/* ---------- composition tile (ADR-0020) ---------- */

/* Parts of one whole, so one accent hue with varied lightness — never a
   categorical palette. Defined on bare :root via the tokens below. */
.tile__donut {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
  flex: 1 1 auto;
  min-width: 0;
  /* Sized by its content (the legend) and then by the leftover height it is
     given; never size-contained, so it cannot shrink under the legend and push
     the ring into the heading. */
  min-height: 4.5rem;
  overflow: visible;
}

/* The ring fills the well's height and stays square; the legend keeps the
   rest of the row. */
.tile__ring {
  flex: none;
  align-self: stretch;
  /* Size-contained so the ring takes the well's height rather than setting
     it: the tile keeps the grid's row height, the ring grows into what is
     left, and the legend decides the floor. */
  contain: size;
  aspect-ratio: 1;
  min-height: 4.5rem;
  max-height: 20rem;
  max-width: 52%;
}

.tile__ring svg {
  display: block;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.tile__legend {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* The legend is the composition tile's floor, so it is set tight: a default
   tile keeps its row height and the ring still never meets the heading. */
.tile--composition .tile__slices { gap: 2px; }
.tile--composition .tile__slice,
.tile--composition .tile__liability { line-height: 1.3; }

.tile__slices {
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 0;
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  text-align: left;
}

.tile__slice {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--t-small);
  min-width: 0;
}

.tile__swatch {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  flex: none;
  align-self: center;
}

.tile__slice-name {
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tile__slice-pct {
  margin-left: auto;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* A stale component greys and is labelled, rather than being dropped or
   zeroed (R-287). */
.tile__slice--stale .tile__slice-name,
.tile__slice--stale .tile__slice-pct { color: var(--text-2); }
.tile__slice--stale .tile__slice-name::after {
  content: " · stale";
  font-size: var(--t-small);
}

/* An `<svg>` since B45: segment widths are continuous, and a continuous value
   in a `style=` attribute is what `style-src 'self'` blocks. The box is
   unchanged -- same height, same pill, same track -- only the segments inside
   it are `<rect>`s positioned by geometry attributes instead of spans sized by
   an inline width. `fill` on a rect is what `background` was on a span. */
.tile__stack {
  display: block;
  width: 100%;
  height: 8px;
  margin-top: 10px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--border-soft);
}
.tile__stack-seg--1 { fill: var(--slice-1); }
.tile__stack-seg--2 { fill: var(--slice-2); }
.tile__stack-seg--3 { fill: var(--slice-3); }
.tile__stack-seg--4 { fill: var(--slice-4); }
.tile__stack-seg--5 { fill: var(--slice-5); }

.tile__swatch--1 { background: var(--slice-1); }
.tile__swatch--2 { background: var(--slice-2); }
.tile__swatch--3 { background: var(--slice-3); }
.tile__swatch--4 { background: var(--slice-4); }
.tile__swatch--5 { background: var(--slice-5); }

.tile__liability {
  margin: 0;
  font-size: var(--t-small);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.tile__obligations {
  margin-top: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-soft);
}

.tile__obligations ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.tile__obligation {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--t-small);
  min-width: 0;
}

.tile__inst {
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tile__due {
  margin-left: auto;
  font-size: var(--t-small);
  flex: none;
}

/* Status pairs colour with a word, never colour alone. */
.tile__due--due { color: var(--text-2); }
.tile__due--overdue { color: var(--crit); font-weight: 600; }

.tile__more,
.tile__obligations--clear {
  margin: 6px 0 0;
  font-size: var(--t-small);
  color: var(--text-2);
}

.tile__obligations--clear {
  margin-top: 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--border-soft);
}

/* -- tile sizes (ADR-0046) ----------------------------------------------
   Size is the room a person gives a tile; kind (standard, collections,
   composition, series) is what the manifest says it draws. Spans are set as
   longhands so they compose with the placement classes below instead of
   either one resetting the other. */
.tile--small  { grid-column-end: span 1; grid-row-end: span 1; }
.tile--medium { grid-column-end: span 2; grid-row-end: span 1; }
.tile--large  { grid-column-end: span 2; grid-row-end: span 2; }
.tile--xl     { grid-column-end: span 4; grid-row-end: span 2; }

.tile--small .tile__collections {
  grid-template-columns: 1fr;
}
.tile--small .tile__collection + .tile__collection {
  padding-top: var(--space-2);
  border-top: 1px solid var(--border-soft);
}

/* The server placed each tile on the four-column grid; pin it to that cell.
   Below 1100px the same tiles flow into two columns in the same order. */
@media (min-width: 1100px) {
  .tile--col-1 { grid-column-start: 1; }
  .tile--col-2 { grid-column-start: 2; }
  .tile--col-3 { grid-column-start: 3; }
  .tile--col-4 { grid-column-start: 4; }
  .tile--row-1 { grid-row-start: 1; }
  .tile--row-2 { grid-row-start: 2; }
  .tile--row-3 { grid-row-start: 3; }
}
/* Once the script has re-packed for the window's columns, its placement
   holds at any width that is a grid. */
@media (min-width: 44.0625rem) {
  .gallery[data-packed] .tile--col-1 { grid-column-start: 1; }
  .gallery[data-packed] .tile--col-2 { grid-column-start: 2; }
  .gallery[data-packed] .tile--col-3 { grid-column-start: 3; }
  .gallery[data-packed] .tile--col-4 { grid-column-start: 4; }
  .gallery[data-packed] .tile--row-1 { grid-row-start: 1; }
  .gallery[data-packed] .tile--row-2 { grid-row-start: 2; }
  .gallery[data-packed] .tile--row-3 { grid-row-start: 3; }
  .gallery[data-packed] .tile--row-4 { grid-row-start: 4; }
}

/* -- content by size (ADR-0046) ------------------------------------------
   The markup always carries everything the summary sent; size decides how
   much of it shows, so a tile can change size without a round trip. Small is
   the reading alone: name, status and the one number (or ring). Medium adds
   the context line, the details and the obligations. Large adds the list and
   the second encodings. A phone card has a whole screen, so below 44rem
   every card shows everything, whatever its size. */
@media (min-width: 44.0625rem) {
  .tile--small .tile__context,
  .tile--small .tile__details,
  .tile--small .tile__log,
  .tile--small .tile__chart,
  .tile--small .tile__cover,
  .tile--medium .tile__cover,
  .tile--small .tile__collection-copy .meter,
  .tile--small .tile__collection-context,
  .tile--small .tile__review,
  .tile--small .tile__liability,
  .tile--small .tile__stack,
  .tile--small .tile__obligations,
  .tile--medium .tile__log,
  .tile--medium .tile__stack,
  .tile--medium .tile__review,
  .tile--small .tile__collection-items,
  .tile--medium .tile__collection-items,
  .tile--medium .tile__range {
    display: none;
  }

  /* A small tile is one reading, so the reading carries it: set large and
     seated at the foot of the card, where a widget keeps its number. A ring
     still fills the well instead. */
  .tile--small .tile__body { justify-content: flex-end; }
  .tile--small .tile__body > .tile__metric .tile__value,
  .tile--small .tile__lead {
    font-size: var(--t-metric-hero);
    line-height: .88;
    letter-spacing: -.04em;
  }

  /* Medium and large standard tiles read across: the reading (ring, or
     number and label) on the left, the context and details beside it, and on
     a large tile the list underneath at full width. Stacked, a medium tile's
     content needs a row and a half, and a large tile's ring is squeezed by
     everything piled under it. */
  .tile--standard:is(.tile--medium, .tile--large) .tile__body {
    display: grid;
    grid-template-columns: 2fr 3fr;
    grid-template-rows: 1fr auto auto 1fr;
    column-gap: var(--space-5);
    row-gap: var(--space-2);
    align-items: start;
  }
  .tile--standard.tile--large .tile__body:has(> .tile__log) {
    grid-template-rows: 1fr auto auto 1fr auto;
  }
  .tile--standard:is(.tile--medium, .tile--large) .tile__body > * { min-width: 0; }
  .tile--standard:is(.tile--medium, .tile--large) .tile__body > .tile__gauge {
    grid-column: 1;
    grid-row: 1 / 5;
    align-self: stretch;
  }
  .tile--standard:is(.tile--medium, .tile--large) .tile__body > .tile__metric { grid-column: 1; grid-row: 2; }
  .tile--standard:is(.tile--medium, .tile--large) .tile__body > .tile__label { grid-column: 1; grid-row: 3; }
  .tile--standard:is(.tile--medium, .tile--large) .tile__body > .tile__context { grid-column: 2; grid-row: 2; }
  .tile--standard:is(.tile--medium, .tile--large) .tile__body > .tile__details { grid-column: 2; grid-row: 3; }
  .tile--standard.tile--large .tile__body > .tile__log {
    grid-column: 1 / -1;
    grid-row: 5;
    padding-top: var(--space-3);
    border-top: 1px solid var(--border-soft);
  }

  /* Collections on a medium tile keep to one row where they can: narrower
     columns, and no cover marks to take their width. */
  .tile--collections.tile--medium .tile__collections {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 7rem), 1fr));
  }
  .tile--collections.tile--medium .tile__collection { min-width: 0; }

  /* Composition reads across too: ring and legend, then the statements due. */
  .tile--composition.tile--medium .tile__body:has(> .tile__obligations) {
    display: grid;
    grid-template-columns: 3fr 2fr;
    column-gap: var(--space-5);
    align-items: center;
  }
  .tile--composition.tile--medium .tile__body > .tile__donut { align-self: stretch; }
  .tile--composition.tile--medium .tile__body > .tile__obligations {
    padding: 0 0 0 var(--space-4);
    border-top: 0;
    border-left: 1px solid var(--border-soft);
  }
}

@media (max-width: 460px) {
  .tile__donut { flex-direction: column; align-items: stretch; }
  .tile__ring {
    align-self: center;
    width: min(12rem, 70%);
    max-width: none;
  }
  .tile__legend { width: 100%; }
}

/* ---------- scheduled activity ---------- */

.activity-wrap {
  margin-top: var(--space-3);
  overflow-x: auto;
}

.activity {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-small);
}

.activity th {
  padding: var(--space-2) var(--space-3) var(--space-2) 0;
  border-bottom: 1px solid var(--border);
  color: var(--text-2);
  font-size: var(--t-small);
  font-weight: 600;
  text-align: left;
}

.activity td {
  min-width: 96px;
  padding: 10px var(--space-3) 10px 0;
  border-bottom: 1px solid var(--border-soft);
  vertical-align: top;
}

.activity tr:last-child td { border-bottom: 0; }

.activity__job,
.activity__schedule,
.activity__error { display: block; }

.activity__job { font-weight: 600; }
.activity__others { margin: 20px 0 var(--space-1); font-size: var(--t-body); }

.activity__schedule,
.activity__error,
.activity-empty {
  color: var(--text-2);
  font-size: var(--t-small);
}

.activity__schedule,
.activity__figure {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.activity__error {
  max-width: 28ch;
  margin-top: var(--space-1);
  color: var(--crit);
}

.activity-empty {
  margin-top: var(--space-3);
  padding: var(--space-4);
  border: 1px dashed var(--border);
  border-radius: 4px;
}

.status {
  display: inline-block;
  padding: 2px 6px;
  border-radius: 2px;
  font-size: var(--t-small);
  font-weight: 600;
}

.status--healthy { background: var(--good-soft); color: var(--good); }
.status--pending,
.status--retired { background: var(--surface-2); color: var(--text-2); }
.status--failed { background: var(--crit-soft); color: var(--crit); }
.status--stale { background: var(--warn-soft); color: var(--warn); }

/* ---------- meter ---------- */

.meter {
  position: relative;
  height: 10px;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  margin-top: var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--border-soft);
  overflow: hidden;
}

/* The fill is a `<rect>` inside a full-bleed `<svg>` since B45 -- a continuous
   width cannot be a class, and SVG geometry attributes are outside CSP's
   reach. `.meter` already clips to the pill, so the rect needs no radius of
   its own; `fill` here is what `background` was. */
.meter__box { display: block; width: 100%; height: 100%; }

.meter__fill { fill: var(--good); }

.meter--warn .meter__fill { fill: var(--warn); }
.meter--crit .meter__fill { fill: var(--crit); }

/* ---------- footer ---------- */

/* `margin-top: auto` pins the timestamp to the foot of the tile. A tile is a
   flex column in a grid cell it does not always fill -- the hero especially,
   spanning two rows -- and without this the slack collects underneath the
   footer, reading as a tile that failed to finish rendering. */
.tile__foot {
  display: flex;
  gap: var(--space-2);
  justify-content: space-between;
  margin: auto 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-soft);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  color: var(--text-2);
}

.tile__foot--warn { color: var(--warn); }
.tile__foot--crit { color: var(--crit); }

.tile__retry { color: var(--text-2); text-transform: none; letter-spacing: 0; }

/* ---------- empty ---------- */

.empty {
  grid-column: 1 / -1;
  padding: var(--space-6);
  background: var(--pane-strong);
  border: 1px dashed var(--border);
  border-radius: var(--radius-card);
}

.empty h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--t-h3);
  font-weight: 600;
  letter-spacing: -.01em;
}

.empty p {
  margin: 0;
  max-width: 60ch;
  color: var(--text-2);
}

code {
  font-family: var(--font-mono);
  font-size: var(--t-small);
  padding: .1em .35em;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 2px;
}

/* ---------- people ---------- */

.link { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.link:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.field { display: block; margin-bottom: var(--space-3); }

/* The label sits above the input and stays there. A placeholder that vanishes
   on focus is not a label. */
.field__label {
  display: block;
  margin-bottom: 6px;
  font-size: var(--t-small);
  font-weight: 500;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  max-width: 360px;
  min-height: 2.75rem;
  padding: 9px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--t-body);
}

.field textarea { min-height: 5.5rem; resize: vertical; }

.field input[readonly] {
  color: var(--text-2);
  background: var(--surface-2);
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* Sits under the input it explains, not beside it: at 375px there is no
   beside. */
.field__help {
  display: block;
  max-width: 360px;
  margin-top: 6px;
  color: var(--text-2);
  font-size: var(--t-small);
  line-height: 1.45;
}

/* What adding somebody actually gets them, said before the button. Each
   line reads without its mark and without colour -- the mark is a second
   encoding of the same fact, not the only one (design guide S2). */
.reach-list {
  list-style: none;
  margin: var(--space-3) 0 var(--space-4);
  padding: var(--space-3) 14px;
  background: var(--surface-2);
  border-radius: 3px;
}

.reach {
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: var(--t-small);
  line-height: 1.5;
}

.reach + .reach { margin-top: var(--space-2); }

.reach__mark {
  flex: none;
  width: 1em;
  font-family: var(--font-mono);
  text-align: center;
}

.reach--now .reach__mark { color: var(--good); }
.reach--later .reach__mark { color: var(--warn); }
.reach--later { color: var(--text-2); }

.people { list-style: none; margin: var(--space-3) 0 0; padding: 0; }

.person {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 14px 0;
  border-bottom: 1px solid var(--border-soft);
}

.person:last-child { border-bottom: none; }

.person__mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid var(--border);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  font-weight: 500;
}

/* Retired is a state, not decoration — it reads without colour too, from the
   mark's shape and the sentence beside it. */
.person__mark--retired {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text-2);
}

.person__who { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.person__email { font-size: var(--t-small); font-weight: 500; overflow-wrap: anywhere; }
.person__meta { color: var(--text-2); font-size: var(--t-small); line-height: 1.45; }

.person--retired .person__email { color: var(--text-2); font-weight: 400; }

.person--request { flex-wrap: wrap; align-items: flex-start; }

.person--empty { border-bottom: none; }

.person__decide,
.person__approve {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: flex-end;
}

.person__decide { width: 100%; margin-top: var(--space-2); }

.person__approve { flex: 1 1 12rem; }

.person__approve .field { margin-bottom: 0; flex: 1 1 10rem; }
.person__approve .field input { max-width: none; }

.person__slot {
  flex: none;
  padding: 3px var(--space-2);
  border: 1px solid var(--border);
  border-radius: 2px;
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--t-small);
}

/* Sits apart from the page's own submit, and never beside it (design guide). */
.person__revoke { flex: none; margin-left: var(--space-2); }

.button--danger {
  padding: 7px 14px;
  background: transparent;
  border-color: var(--crit);
  color: var(--crit);
  font-weight: 500;
}

.button--danger:hover { background: var(--crit-soft); border-color: var(--crit); }

/* Save closes the form it belongs to, on the right where the eye ends. It
   does not float: a floating button sat on top of the rows it saves. */
.settings__save {
  display: flex;
  justify-content: flex-end;
  margin: calc(-1 * var(--space-2)) 0 var(--space-6);
}
.settings__save .button { min-width: 12rem; }
@media (max-width: 40rem) {
  .settings__save .button { width: 100%; }
}

.settings__release { margin: var(--space-4) 0 0; color: var(--text-2); font-size: var(--t-small); }
.settings__sha { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.settings__pair { display: grid; gap: 0 var(--space-4); align-items: start; }
@media (min-width: 48rem) {
  .settings__pair { grid-template-columns: 1fr 1fr; }
}

.button--quiet {
  display: inline-flex;
  align-items: center;
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--text);
  font-weight: 500;
  text-decoration: none;
}
.button--quiet:hover { filter: none; background: var(--surface-3); }

.account__who { display: flex; flex-direction: column; gap: 2px; }
.account__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-3) 0; }
/* A form's button beside the links: the same height, on the same line. */
.account__export { display: contents; }
.account__export .button { line-height: inherit; }

.account__leave {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

@media (max-width: 480px) {
  .chrome__inner { padding: 14px var(--space-4); gap: var(--space-3); }

  /* Capture happens on a phone, and so does adding someone (R-05). */
  .person { flex-wrap: wrap; }
  .person__who { flex: 1 1 100%; order: 1; }
  .person__mark { order: 0; }
  .person__slot { order: 0; margin-left: auto; }
  .person__revoke { order: 2; margin-left: 46px; }
  .person__decide { order: 3; margin-left: 46px; }
  .page { padding: var(--space-5) var(--space-4) var(--space-7); }

  .menu {
    max-width: calc(100vw - 24px);
  }

  .menu__item { padding: var(--space-3) var(--space-2); font-size: var(--t-body); }
}

/* -- the series tile (ADR-0036) ------------------------------------------
   Chart-first, for a module whose subject is a value observed repeatedly.
   The interaction is two selectors: the home page ships no JavaScript. */


.tile__lead {
  font-family: var(--font-mono);
  font-size: var(--t-lead);
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  margin: 0;
}
.tile__unit {
  font-family: var(--font-ui);
  font-size: var(--t-small);
  color: var(--text-2);
}
.tile__serieslabel {
  font-size: var(--t-small);
  line-height: 1.4;
  color: var(--text-2);
  margin: 2px 0 0;
}
.tile__chart {
  display: block;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 5.5rem;
  max-height: 18rem;
  height: 100%;
  margin-top: 0;
}
.tile__chart .tile__plot { overflow: visible; }
.tile__chart .line {
  /* The plot stretches to the tile; the stroke keeps its width. */
  vector-effect: non-scaling-stroke;
  fill: none;
  stroke: var(--series-1);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.tile__chart .area {
  fill: color-mix(in srgb, var(--series-1) 16%, transparent);
  stroke: none;
}
.tile__chart .dot { fill: var(--series-1); }
.tile__chart .hit { fill: transparent; }
.tile__chart .readout {
  font-family: var(--font-mono);
  font-size: var(--t-small);
  fill: var(--text);
  opacity: 0;
  pointer-events: none;
}
.tile__chart .pt:hover .readout { opacity: 1; }
.tile__chart .pt:hover .dot { r: 4; fill: var(--accent); }

@media (max-height: 740px) {
  .tile__chart { min-height: 4.5rem; max-height: 8rem; }
}

/* -- large tile ----------------------------------------------------------
   Same frost as every other tile. Light used to invert Media onto ink;
   that checkerboard is what made the modules look like a different product
   from the chrome they sit under. */

.tile--collections .tile__collections {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: var(--space-4) var(--space-5);
  margin-top: 0;
  border-top: 0;
  flex: 1 1 auto;
  align-content: stretch;
  min-height: 0;
}
.tile--collections .tile__collection {
  border-bottom: 0;
  /* 12px left is the rail slot. Attention must use the same value so the
     stripe cannot steal gutter from the neighbouring column. */
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  min-height: 0;
}
.tile--collections .tile__collection-copy .meter {
  margin-top: auto;
  height: 8px;
}
.tile--collections .tile__collection--attention {
  box-shadow: none;
  padding-left: var(--space-3);
}
.tile--collections .tile__collection--attention::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 3px;
  border-radius: 2px;
  background: var(--accent);
  pointer-events: none;
}
/* What a collection's count is made of, named on a large tile (R-02a). */
.tile__collection-items {
  list-style: none;
  margin: var(--space-1) 0 0;
  padding: var(--space-2) 0 0;
  border-top: 1px solid var(--border-soft);
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: var(--t-small);
  line-height: var(--lh-small);
  color: var(--text);
  min-width: 0;
}
.tile__collection-items li {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tile--collections .tile__collection--empty {
  padding: 10px var(--space-3);
}

/* -- breakpoints --------------------------------------------------------- */

@media (max-width: 1099px) {
  .grid {
    --grid-columns: 2;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: none;
  }
  .tile--xl { grid-column-end: span 2; grid-row-end: span 2; }
}

@media (max-width: 44rem) {
  html, body {
    overflow-x: clip;
  }
  @supports not (overflow-x: clip) {
    html, body {
      overflow-x: hidden;
    }
  }

  .chrome__inner {
    padding: clamp(8px, 1.2vh, 14px) clamp(0.75rem, 3vw, 1rem);
    gap: var(--space-3);
  }

  .home,
  .home:has(.site-foot) {
    --home-gutter: clamp(0.75rem, 3vw, 1rem);
    max-width: 100vw;
    padding: 0.5rem var(--home-gutter) 0.75rem;
  }

  .appearance, .brand__sub { display: none; }

  .nav { flex: 1 1 auto; }
  .nav__strip { -webkit-overflow-scrolling: touch; }

  .nav__item {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .greeting {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding: 2px 0 var(--space-2);
    flex: none;
  }

  .greeting__meta {
    text-align: left;
    white-space: normal;
  }

  .queue-strip { width: 100%; }
  .queue-strip__head { gap: var(--space-2); }
  .queue-strip__count { display: none; }
  .queue-strip__actions { gap: .625rem; }

  /* The queue on a phone is two lines and a count. The front swipes, so
     nothing inside it scrolls sideways; the strip keeps one fixed height
     whatever the depth (R-71). The bars carry the priority by shape, and its
     word stays for a screen reader. */
  .queue-strip:has(.queue-strip__tasks) {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto 1fr;
    column-gap: var(--space-3);
  }
  .queue-strip__head { grid-column: 1 / -1; }
  .queue-strip__tasks {
    flex-direction: column;
    justify-content: center;
    gap: var(--space-2);
    overflow: hidden;
    border: 0;
  }
  .qcell {
    flex: none;
    flex-direction: row;
    align-items: center;
    gap: var(--space-2);
    padding: 0;
    border: 0;
  }
  .qcell:nth-child(n + 3),
  .qcell__meta,
  .qcell__state { display: none; }
  .qcell__marks { flex: none; }
  .qmark__label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .qcell__title {
    display: block;
    flex: 1;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .queue-strip__more {
    display: block;
    align-self: end;
    margin: 0;
    color: var(--text-2);
    font-family: var(--font-mono);
    font-size: var(--t-small);
    line-height: 1.25;
    font-variant-numeric: tabular-nums;
  }

  /* Documents due are one line on a phone: a chip for the state and the
     document, or how many, linked to where the first is uploaded. The cards
     would push the front past the screen, which never scrolls. */
  .doc-prompt { display: none; }
  .doc-due {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 2.75rem;
    padding: 0 var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-control);
    background: var(--pane-strong);
    backdrop-filter: var(--panel-blur);
    -webkit-backdrop-filter: var(--panel-blur);
    color: var(--text);
    font-size: var(--t-small);
    text-decoration: none;
  }
  .doc-due:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
  }
  .doc-due__what {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .notice__more { display: none; }

  .pager { display: none; }

  /* A phone's home is a deck: the front slide, then one full-width card per
     slide, one swipe to a slide (R-05). It never scrolls down, which is the
     owner's call (Michael, 2026-10-04) and replaces issue 223's page that
     scrolled; a card that outgrows its slide gives up its list rows instead
     (deck.js). The deck is the only scroller. It goes full-bleed and takes the
     page gutter back as its own padding, so the gutter scrolls with the slides
     and one gutter between them puts the next slide's leading edge exactly on
     the viewport's. The gallery's pages and grids are not boxes here, so the
     tiles are the deck's slides; arranging, which has no deck, keeps them as
     the list it lays out below. */
  .gallery__page,
  .gallery__page .grid,
  .home__deck > .gallery,
  .home__deck > .grid { display: contents; }

  .home__deck {
    position: relative;
    display: flex;
    flex: 1;
    min-height: 0;
    margin-inline: calc(-1 * var(--home-gutter));
    padding: var(--space-1) var(--home-gutter) var(--space-2);
    gap: var(--home-gutter);
    scroll-padding-inline: var(--home-gutter);
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
  }
  .home__deck::-webkit-scrollbar { display: none; }

  .home__front,
  .home__deck .tile,
  .home__deck .empty {
    flex: 0 0 100%;
    min-width: 0;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }
  .home__deck > .deck__clone { display: flex; }

  /* What the person sees first: who, the queue, anything due. The footer
     sits against the bottom, above the dots. */
  .home__front {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }
  .home__front > * { margin: 0; }

  body:has(.home) .home .site-foot {
    margin-top: auto;
    padding: 0;
  }
  .home__front .site-foot__nav { padding: 0; }
  .home__front .site-foot__link { min-height: 2.5rem; }

  /* A card that outgrows its slide gives up the rows under its figures. What
     it outgrows has to show: a collection's rows may not shrink under their
     own text, where they paint over each other and nothing overflows to say
     so; they overflow, and deck.js reads that. */
  .tile--tight .tile__log,
  .tile--tight .tile__collection-items { display: none; }
  .home__deck .tile--collections .tile__collection { min-height: auto; }

  /* Where the deck is, one dot to a slide: the current one a pill. The deck
     draws them itself so the count cannot fall out of step with its slides. */
  .deck__dots {
    flex: none;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.375rem;
  }
  .deck__dot {
    width: 0.375rem;
    height: 0.375rem;
    border-radius: var(--radius-pill);
    background: var(--border-strong);
    transition: width 160ms ease-out, background-color 160ms ease-out;
  }
  .deck__dot[data-current] {
    width: 1.375rem;
    background: var(--text);
  }
  @media (prefers-reduced-motion: reduce) {
    .deck__dot { transition: none; }
  }

  /* The landing page's sample is still its own row of full-width cards. */
  .landing__sample .grid {
    display: flex;
    gap: var(--space-3);
    margin: 0;
    padding: 0 0 var(--space-2);
  }
  .landing__sample .tile {
    flex: 0 0 100%;
    width: 100%;
    min-width: 100%;
    max-width: 100%;
  }

  .tile--collections .tile__collections { grid-template-columns: 1fr; }
  .tile--collections .tile__collection + .tile__collection {
    padding-top: var(--space-3);
    border-top: 1px solid var(--border-soft);
  }
  .tile--collections .tile__collection--empty + .tile__collection,
  .tile--collections .tile__collection + .tile__collection--empty {
    border-top: 0;
  }
}


/* ---- /queue ------------------------------------------------------------
   Its own page, not a tile. The body is contained at 100dvh for the home
   grid; this page scrolls, because a queue is a list and a list has no
   fixed length. */
.queue {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  width: 100%;
  max-width: 90rem;
  min-height: calc(100dvh - var(--chrome-height));
  margin: 0 auto;
  padding: clamp(1.5rem, 3vw, 3rem) clamp(1rem, 3vw, 2rem) 4rem;
}

.queue__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  flex-wrap: wrap;
  padding: clamp(1rem, 2vw, 1.5rem);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--pane);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  box-shadow: var(--panel-shadow);
}

.queue__intro {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 42rem;
}

.queue__eyebrow,
.queue__kicker {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: var(--lh-small);
  color: var(--text-2);
}

.queue__eyebrow { color: var(--accent); }

.queue__head h1 {
  margin: 0;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  font-weight: 400;
  letter-spacing: -.03em;
  text-wrap: balance;
}

.queue__lede,
.queue__description {
  margin: 0;
  color: var(--text-2);
}

.queue__lede { max-width: 65ch; }
.queue__description { font-size: var(--t-small); }

.queue__boards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
}

.queue__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-width: 0;
  padding: clamp(1rem, 2vw, 1.5rem);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--pane);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  box-shadow: var(--panel-shadow);
}

.queue__section--active { min-height: 17rem; }
.queue__section--history { margin-top: 0; }

.queue__section-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
}

.queue__section-head > div {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.queue__section h2 {
  margin: 0;
  font-size: var(--t-h3);
  line-height: var(--lh-h3);
  font-weight: 600;
  text-wrap: balance;
}

.queue__count {
  flex: none;
  padding: .3rem .65rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  font-variant-numeric: tabular-nums;
}

.queue__empty {
  display: grid;
  flex: 1;
  place-items: center;
  min-height: 8rem;
  margin: 0;
  padding: var(--space-5);
  border: 1px dashed var(--border);
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text-2);
  text-align: center;
  text-wrap: balance;
}

.queue__section--history .queue__empty { min-height: 6rem; }

.queue__paused {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-control);
  border: 1px solid var(--warn);
  background: var(--warn-soft);
  color: var(--text);
}

.task-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }

.task {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: .875rem var(--space-4);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-control);
  background: var(--pane-strong);
}

.task__body { min-width: 0; flex: 1; }
.task__title { margin: 0; color: var(--text); overflow-wrap: anywhere; }
.task__note {
  margin: 6px 0 0;
  color: var(--text-2);
  font-size: var(--t-small);
  overflow-wrap: anywhere;
}

.task__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  color: var(--text-2);
}

/* Shape and text, then colour -- the same three channels the rail uses. */
.task__priority {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: .2rem var(--space-2);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono);
  font-size: var(--t-small);
}
.task__priority--high { color: var(--crit); background: var(--crit-soft); }
.task__priority--medium { color: var(--warn); background: var(--warn-soft); }
.task__priority--low { color: var(--text-2); background: var(--surface-2); }

.queue__pause, .queue__purge { margin: 0; }
.queue__purge {
  margin-top: 0;
  padding: .875rem var(--space-4);
  border: 1px dashed var(--border);
  border-radius: var(--radius-control);
  color: var(--text-2);
  font-size: var(--t-small);
}
.queue__purge p { margin: 0 0 var(--space-3); }

.queue__purge-empty {
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-soft);
  color: var(--text-2);
  font-size: var(--t-small);
}

@media (max-width: 56rem) {
  .queue__boards { grid-template-columns: 1fr; }
  .queue__section--active { min-height: 14rem; }
}

@media (max-width: 40rem) {
  .queue__head { flex-direction: column; align-items: stretch; }
  .queue__pause .button { width: 100%; }
  .task { flex-direction: column; gap: 6px; }
}

/* ---- background picker (settings) -------------------------------------- */
.settings__sub {
  margin: 18px 0 var(--space-1);
  font-size: var(--t-small);
  font-family: var(--font-mono);
  color: var(--text-2);
}

.grounds {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: var(--space-4) var(--space-5);
  margin-top: var(--space-2);
}

.ground { border: 0; margin: 0; padding: 0; }
.ground legend {
  padding: 0;
  font-size: var(--t-small);
  font-family: var(--font-mono);
  color: var(--text-2);
}

.ground__opts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.ground__opt {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
  cursor: pointer;
  font-size: var(--t-small);
  color: var(--text-2);
}
.ground__opt input { position: absolute; opacity: 0; pointer-events: none; }

/* The swatch is the choice, so it carries the selected state and the focus
   ring -- the radio itself is visually hidden but still what is operated. */
.ground__swatch {
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-inner);
  border: 2px solid var(--border);
  background-image: var(--swatch);
  background-size: cover;
  background-position: center;
}
.ground__opt input:checked + .ground__swatch {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}
.ground__opt input:focus-visible + .ground__swatch {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
/* One rule per ground rather than a `--swatch` custom property written into a
   `style=` attribute: `style-src 'self'` blocks the attribute, and a custom
   property set that way is blocked with it (B45). `host.store.BACKGROUNDS` is
   a closed set of nine, and `test_every_ground_has_a_swatch_rule` fails if a
   tenth is declared without one here -- the failure mode otherwise is a
   swatch that silently renders empty. */
/* "gradient" is each palette's painted wash, shown as that palette's wash
   rather than whatever ground the settings page itself is wearing. */
.ground__swatch[data-ground="gradient"] { background: var(--wash-light); }
.ground__opt input[name="bg_dim"][value="gradient"] + .ground__swatch { background: var(--wash-dim); }
.ground__opt input[name="bg_sepia"][value="gradient"] + .ground__swatch { background: var(--wash-sepia); }
/* Black's "Plain" is pure black whichever palette shows the settings page. */
.ground__opt input[name="bg_black"][value="gradient"] + .ground__swatch { background: var(--swatch-black); }
.ground__swatch[data-ground="light-1"] { background-image: url("/static/images/light-1.jpg"); }
.ground__swatch[data-ground="light-2"] { background-image: url("/static/images/light-2.jpg"); }
.ground__swatch[data-ground="light-3"] { background-image: url("/static/images/light-3.jpg"); }
.ground__swatch[data-ground="dim-1"] { background-image: url("/static/images/dim-1.jpg"); }
.ground__swatch[data-ground="dim-2"] { background-image: url("/static/images/dim-2.jpg"); }
.ground__swatch[data-ground="dim-3"] { background-image: url("/static/images/dim-3.jpg"); }
.ground__swatch[data-ground="sepia-1"] { background-image: url("/static/images/sepia-1.jpg"); }
.ground__swatch[data-ground="sepia-2"] { background-image: url("/static/images/sepia-2.jpg"); }
.ground__swatch[data-ground="sepia-3"] { background-image: url("/static/images/sepia-3.jpg"); }
.ground__swatch[data-ground="black-1"] { background-image: url("/static/images/black-1.jpg"); }
.ground__swatch[data-ground="black-2"] { background-image: url("/static/images/black-2.jpg"); }
.ground__swatch[data-ground="black-3"] { background-image: url("/static/images/black-3.jpg"); }

.ground__name { font-family: var(--font-mono); }
.ground__opt input:checked ~ .ground__name { color: var(--text); }

/* A link that acts rather than navigates, such as offering the device's zone. */
button.link { background: none; border: 0; padding: 0; font: inherit; cursor: pointer; }

/* -- arranging the home (ADR-0046) ---------------------------------------
   /?arrange: the same grid, packed the same way, with a tool on every tile.
   Pages stack and scroll so a tile can be dragged to any of them; hidden
   tiles wait in the tray, which is also where a tile is dropped to hide it. */

.greeting__side {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  flex: none;
}
.greeting__arrange {
  display: inline-grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-bottom: var(--space-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  color: var(--text);
  transition: border-color var(--duration-fast) var(--ease-out);
}
.greeting__arrange:hover { border-color: var(--accent); }
.greeting__arrange:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.greeting__arrange svg,
.tile__tool svg,
.tile__sizes svg,
.tray__mark svg {
  width: 1.25rem;
  height: 1.25rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tile__tool .icon-dot,
.tile__sizes .icon-fill { fill: currentColor; stroke: none; }

.button:disabled { opacity: 0.45; cursor: default; }

.arrange-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
  padding: var(--space-2) 0;
}
.arrange-bar__status {
  flex: 1;
  margin: 0 var(--space-3);
  min-width: 0;
  font-size: var(--t-small);
  color: var(--text-2);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.home--arranging .gallery {
  flex-direction: column;
  gap: var(--space-4);
  overflow-x: hidden;
  overflow-y: auto;
  scroll-snap-type: none;
  scroll-behavior: auto;
}
.home--arranging .gallery__page {
  flex: 0 0 100%;
  box-sizing: border-box;
  padding: var(--space-2);
  border: 1px dashed var(--border-strong);
  border-radius: calc(var(--radius-card) + var(--space-2));
}
.home--arranging .pager { display: none; }

.home--arranging .tile { cursor: grab; user-select: none; -webkit-user-select: none; }
.home--arranging .tile a { pointer-events: none; }
.home--arranging .tile__state { visibility: hidden; }

.tile__edit,
.tile__sizes { display: none; }
/* A tile's children ignore the pointer so the whole card is its link
   (.tile > :not(.tile__hit)); its tools must take it back. */
.home--arranging .tile__edit,
.home--arranging .tile__sizes { pointer-events: auto; }
.home--arranging .tile__edit {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  z-index: 2;
  display: flex;
  gap: var(--space-1);
}
.home--arranging .tile__sizes {
  position: absolute;
  left: 50%;
  bottom: var(--space-2);
  z-index: 2;
  display: flex;
  padding: 3px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  translate: -50% 0;
}
.tile__tool,
.tile__sizes button {
  display: inline-grid;
  place-items: center;
  padding: 0;
  border-radius: var(--radius-pill);
  color: var(--text);
  cursor: pointer;
}
.tile__tool {
  width: 2.25rem;
  height: 2.25rem;
  border: 1px solid var(--border-strong);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
}
.tile__sizes button {
  width: 2.25rem;
  height: 2rem;
  border: 0;
  background: none;
  color: var(--text-2);
}
.tile__sizes button[aria-checked="true"] { background: var(--accent-fill); color: var(--on-accent); }
.tile__grip { cursor: grab; }
.tile__grip[aria-pressed="true"] { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); }
.tile__show { display: none; }
.tile__tool:focus-visible,
.tile__sizes button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Where the dragged tile will land, and the tile itself in hand. */
.tile--placeholder {
  border-style: dashed;
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: none;
}
.tile--placeholder > * { visibility: hidden; }
.tile--lifted { outline: 2px solid var(--accent); outline-offset: 3px; }
.tile--ghost {
  position: fixed;
  z-index: 50;
  margin: 0;
  pointer-events: none;
  background: var(--pane-strong);
  box-shadow: var(--shadow-overlay);
  scale: 1.02;
}
body.is-dragging,
body.is-dragging .tile { cursor: grabbing; }
.home--arranging .tile[data-key] {
  transition: transform var(--duration-base) var(--ease-out),
    background var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out);
}

.tray {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 3.5rem;
  padding: var(--space-2) var(--space-3);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-card);
  background: var(--pane);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  transition: background var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out);
}
.tray--hot { border-color: var(--accent); background: var(--accent-soft); }
.tray__mark { display: inline-grid; color: var(--text-2); }
.tray__tiles { display: flex; flex-wrap: wrap; gap: var(--space-2); min-width: 0; }
.tray .tile {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  padding: 4px 4px 4px var(--space-3);
  border-radius: var(--radius-pill);
  cursor: default;
}
.tray .tile__body,
.tray .tile__foot,
.tray .tile__sizes,
.tray .tile__grip,
.tray .tile__hide,
.tray .tile__state { display: none; }
.home--arranging .tray .tile__edit { position: static; }
.tray .tile__show { display: inline-grid; }

@media (prefers-reduced-motion: reduce) {
  .home--arranging .tile[data-key] { transition: none; }
}

/* A phone arranges a list: its cards are one run in the saved order, so the
   list is the honest picture, and size is chosen for the wider screens. */
@media (max-width: 44rem) {
  .home--arranging .gallery {
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
    scroll-snap-type: none;
    gap: var(--space-2);
  }
  .home--arranging .tile {
    flex: none;
    height: auto;
    min-height: 3.75rem;
    flex-direction: row;
    align-items: center;
    padding-right: 13rem;
  }
  .home--arranging .tile__body,
  .home--arranging .tile__foot { display: none; }
  .home--arranging .tile__edit { top: 50%; translate: 0 -50%; }
  .home--arranging .tile__sizes { left: auto; right: 5.5rem; bottom: auto; top: 50%; translate: 0 -50%; }
  .home--arranging .tray .tile { padding-right: 4px; }
  .home--arranging .tray .tile__edit { translate: none; }
  /* The bar keeps its four actions on one line; what it says goes under. */
  .arrange-bar { flex-wrap: wrap; row-gap: var(--space-1); }
  .arrange-bar .button { padding: 8px 14px; }
  .arrange-bar [data-arrange="done"] { margin-left: auto; }
  .arrange-bar__status { order: 1; flex-basis: 100%; margin: 0; }
  .arrange-bar__status:empty { display: none; }
}

/* ---------- landing (R-23i, ADR-0047) ----------
   The public front page. Unlike home it scrolls. It reuses the home's tiles,
   the pill chrome and cards; what is here is only its own rhythm. Text never
   sits bare on the photograph: prose is on a pane, and the few headings that
   are not carry the greeting's halo (AGENTS.md §5 rule 5). */

.landing {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  max-width: 90rem;
  margin: 0 auto;
  padding: var(--space-6) clamp(1rem, 2.5vw, 1.75rem) var(--space-5);
}

.landing__hero {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  max-width: 48rem;
  padding: var(--space-6);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
}

.landing__title {
  margin: 0;
  font-size: var(--t-display);
  font-weight: 400;
  line-height: var(--lh-display);
  letter-spacing: -.035em;
  text-wrap: balance;
}

.landing__lead {
  margin: 0;
  max-width: 65ch;
  font-size: var(--t-lead);
  line-height: var(--lh-lead);
}

.landing__actions,
.landing__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: 0;
}


.landing__palette[hidden] { display: none; }

/* The sample grid: the home's own grid, given the room home takes from the
   window, and marked as made-up. */
.landing__sample {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}


.landing__sample .grid {
  flex: none;
  width: 100%;
  height: clamp(46rem, 100vh, 60rem);
  grid-template-rows: repeat(4, minmax(min-content, 1fr));
  overflow: visible;
}

@media (min-width: 1100px) {
  .landing .tile--row-4 { grid-row-start: 4; }
}

/* A sample is not a link: no hover lift that promises a destination. */
.landing .tile:hover {
  background: var(--pane);
  border-color: var(--border);
}

.landing__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  scroll-margin-top: 5rem;
}

.landing__heading {
  margin: 0;
  font-size: var(--t-h2);
  font-weight: 400;
  line-height: var(--lh-h2);
  letter-spacing: -.02em;
  text-wrap: balance;
}

.landing__section > .landing__heading { text-shadow: 0 0 12px var(--bg); }
.card .landing__heading { font-size: var(--t-h2); font-weight: 400; }

.landing__modules,
.landing__day,
.landing__tools,
.landing__facts,
.landing__steps {
  margin: 0;
  padding: 0;
  list-style: none;
}

.landing__modules,
.landing__day {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
  gap: var(--space-4);
}

.landing .card { margin: 0; }

.landing__module h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--t-h3);
  font-weight: 600;
  line-height: var(--lh-h3);
}

.landing__step {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.landing__step p { margin: 0; }

.landing__time {
  font-family: var(--font-mono);
  font-size: var(--t-lead);
  font-variant-numeric: tabular-nums;
}


.landing__split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  gap: var(--space-4);
}

.landing__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.landing__panel p { margin: 0; max-width: 65ch; }

.landing__tools {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}


.landing__facts {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.landing__facts li {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  align-items: baseline;
}

.landing__check {
  color: var(--good);
  font-weight: 600;
}

.landing__steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  counter-reset: step;
}

.landing__steps li {
  display: grid;
  grid-template-columns: 2rem 1fr;
  align-items: baseline;
  counter-increment: step;
}

.landing__steps li::before {
  content: counter(step);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
}

.landing__request {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  margin: 0;
}

.landing__asked {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.landing__asked p { margin: 0; max-width: 65ch; overflow-wrap: anywhere; }

.landing__request .notice,
.landing__request .lt-field,
.landing__request .field {
  width: 100%;
  max-width: 32rem;
  margin: 0;
}

.landing__request .field textarea { max-width: none; }

.landing__steps a {
  color: var(--accent);
  text-decoration: none;
  overflow-wrap: anywhere;
}

.landing__steps a:hover { color: var(--accent-hover); }

.landing__steps a:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

@media (max-width: 60rem) {
  .landing__anchors { display: none; }
}

@media (max-width: 1099px) {
  .landing__sample .grid { height: auto; }
}

@media (max-width: 44rem) {
  .landing { gap: var(--space-7); padding-top: var(--space-4); }
  .landing__hero { padding: var(--space-5); }
  .landing__sample .grid { height: 34rem; }
}

/* ---------- setup (R-23j, R-15a, ADR-0048) ----------
   Choosing modules and setting them up. Each module is a card the whole of
   which is the checkbox's label, so the target is the card, not a 20px box.
   What a module is sits in `text`; what it keeps qualifies it, in `text-2`. */

.setup { display: flex; flex-direction: column; gap: var(--space-4); }
.setup h1 { margin: 0; text-wrap: balance; text-shadow: 0 0 12px var(--bg); }
.setup .card { margin: 0; }

.setup__form { display: flex; flex-direction: column; gap: var(--space-4); margin: 0; }

.setup__modules {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.setup__module {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  gap: var(--space-3);
  align-items: start;
  height: 100%;
  box-sizing: border-box;
  cursor: pointer;
}

.setup__module:hover { border-color: var(--accent); }
.setup__module:has(.setup__box:checked) { border-color: var(--accent); background: var(--pane-strong); }
.setup__module:has(.setup__box:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }

.setup__box {
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.125rem 0 0;
  accent-color: var(--accent-fill);
}

.setup__copy { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }

/* Ticking a health-data module asks for its policy on the spot (issue 154). */
.setup__choice { display: flex; flex-direction: column; gap: var(--space-2); }
.setup__choice > .setup__module { height: auto; flex: 1 0 auto; }
.setup__consent { display: none; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.setup__choice:has(.setup__box:checked) .setup__consent { display: flex; }
.setup__consent .check { margin: 0; }
.setup__consent .check input { accent-color: var(--accent-fill); }
.setup__name { font-size: var(--t-lead); font-weight: 600; }
.setup__purpose { max-width: 65ch; }

/* A turned-off module opened by its address: what it is, and Turn on (issue 162). */
.module-off { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-4); }
.module-off p, .module-off form { margin: 0; }
.setup__keeps { font-size: var(--t-small); color: var(--text-2); max-width: 65ch; }


.setup__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0; }

.setup__ways {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: var(--space-3);
}

.setup__way {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  color: var(--text);
  text-decoration: none;
}
.setup__way:hover { border-color: var(--accent); }
.setup__way .lt-chip { align-self: flex-start; }
.setup__way:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.setup__progress,
.modules { margin: 0; padding: 0; list-style: none; }

.setup__row,
.modules__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  min-height: 3rem;
  padding: var(--space-2) 0;
  border-top: 1px solid var(--border-soft);
}
.setup__row:first-child,
.modules__row:first-child { border-top: 0; }

.setup__row-link { color: var(--accent); text-decoration: none; font-weight: 500; min-width: 8rem; }
.setup__row-link:hover { color: var(--accent-hover); }
.setup__row-link:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.modules__row form { margin: 0; }
.modules__delete { flex-basis: 100%; }
.modules__delete summary { font-size: var(--t-small); color: var(--text-2); cursor: pointer; min-height: 2.75rem; display: flex; align-items: center; }
.modules__delete .check input { accent-color: var(--crit); }


/* The fields tile across the card rather than stacking in its left half on
   a desk (issue 185); a phone's card holds one column, as before. Their own
   grid under the title, and auto-fit: a section with fewer fields than the
   card has room for (Dietician's two goal choices) spreads them across it
   instead of leaving empty columns on the right. */
.setup__section h2 { margin: 0 0 var(--space-3); }
.setup__fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  align-items: start;
  gap: var(--space-3);
}
.setup__field { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: 0; min-width: 0; }
.setup__field .lt-field { flex: 1 1 100%; max-width: none; }
.setup__by { display: inline-flex; align-items: center; gap: var(--space-2); }

/* The connect page (R-23h): one client open at a time is enough to read. */
.connect__client summary { display: flex; align-items: center; min-height: 3rem; cursor: pointer; list-style: none; }
.connect__client summary::-webkit-details-marker { display: none; }
.connect__client summary h2 { margin: 0; }
.connect__client summary::after { content: "+"; margin-left: auto; font-family: var(--font-mono); color: var(--text-2); }
.connect__client[open] summary::after { content: "\2212"; }
.connect__client summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.connect__body { padding-top: var(--space-3); }
.connect__body code { overflow-wrap: anywhere; }

.landing__tag { margin: 0; }
.landing__effect { align-self: flex-start; margin-top: auto; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.landing__tools .lt-chip { font-family: var(--font-mono); }

/* Home's "setup left" (R-23j): a pane like the greeting's meta, holding a
   Lattice chip and a small button. State and action, no sentence. */
.setup-left {
  flex: none;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-1) var(--space-1) var(--space-1) var(--space-2);
  background: var(--pane-strong);
  backdrop-filter: var(--panel-blur);
  -webkit-backdrop-filter: var(--panel-blur);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}
