/*
  Mica — a small UI kit for club tools read on a phone outdoors.

  The tokens are the API. A project changes the look by overriding custom
  properties in its own stylesheet, loaded after this one; it never edits
  this file. Everything here sits in cascade layers, so any unlayered rule in
  a project wins without a specificity fight.

  docs/style.md explains the rules behind the values.
*/

@layer mica.fonts, mica.tokens, mica.reset, mica.base, mica.layout, mica.components, mica.utilities;

/* ---------------------------------------------------------------- fonts */

@layer mica.fonts {
  @font-face {
    font-family: "Atkinson Hyperlegible Next";
    src: url("fonts/atkinson-hyperlegible-next-latin-wght-normal.825fabef695f.woff2") format("woff2");
    font-weight: 200 800;
    font-style: normal;
    font-display: swap;
  }
  @font-face {
    font-family: "Atkinson Hyperlegible Next";
    src: url("fonts/atkinson-hyperlegible-next-latin-wght-italic.740db8084ae9.woff2") format("woff2");
    font-weight: 200 800;
    font-style: italic;
    font-display: swap;
  }
  @font-face {
    font-family: "Atkinson Hyperlegible Mono";
    src: url("fonts/atkinson-hyperlegible-mono-latin-wght-normal.387c8dcba3be.woff2") format("woff2");
    font-weight: 200 800;
    font-style: normal;
    font-display: swap;
  }
}

/* --------------------------------------------------------------- tokens */

@layer mica.tokens {
  :root {
    color-scheme: light;

    /* colour: light */
    --paper: #fbfbf8;
    --surface: #ffffff;
    --sunk: #f1f2ec;
    --ink: #1a1d17;
    --muted: #596052;
    --rule: #dfe2d8;
    --rule-strong: #858d7b;
    --accent: #3d5c2c;
    --on-accent: #ffffff;
    --tint: #e8eee1;
    --marker: #f6e27a;
    --on-marker: #1a1d17;
    --late: #a63f18;
    --progress: #28638a;
    --done: #2b7340;
    --danger: #b3261e;
    --on-danger: #ffffff;

    /* people: a dot each, never picked by hand */
    --person-1: #3d7a3a;
    --person-2: #2a6a93;
    --person-3: #b0521f;
    --person-4: #a07a0c;
    --person-5: #1f7a78;
    --person-6: #a8323a;
    --person-7: #56606b;
    --person-8: #7a5a2e;

    --scrim: rgb(20 24 16 / .42);
    --lift: 0 1px 2px rgb(20 24 16 / .08), 0 12px 32px rgb(20 24 16 / .14);

    /* weight and line: the sun theme turns these up */
    --w-text: 400;
    --w-medium: 550;
    --w-strong: 700;
    --w-heavy: 800;
    --rule-w: 1px;
    --field-w: 1.5px;

    /* type: major third on a 17px body */
    --font-text: "Atkinson Hyperlegible Next", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-mono: "Atkinson Hyperlegible Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    --t-xs: .8rem;
    --t-sm: .875rem;
    --t-md: 1rem;
    --t-lg: 1.25rem;
    --t-xl: 1.5625rem;
    --t-2xl: 1.953rem;
    --t-3xl: 2.441rem;

    /* space: a quarter-rem scale */
    --s-1: .25rem;
    --s-2: .5rem;
    --s-3: .75rem;
    --s-4: 1rem;
    --s-5: 1.5rem;
    --s-6: 2rem;
    --s-7: 3rem;
    --gutter: 1rem;

    /* shape: three radii, by what the thing is */
    --radius-tag: 5px;
    --radius-control: 10px;
    --radius-sheet: 18px;

    --tap: 48px;
    --bar-h: 3.25rem;
    --tabbar-h: 4rem;
    --rail-w: 16.5rem;
    /* folded away: a strip holding only the button that brings it back */
    --rail-folded-w: calc(var(--tap) + 2 * var(--s-2));
    --measure: 40rem;
    --page-max: 46rem;

    --quick: 120ms;
    --settle: 240ms;
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]):not([data-theme="sun"]) {
      color-scheme: dark;
      --paper: #121410;
      --surface: #1c1f19;
      --sunk: #181b15;
      --ink: #e8ebe2;
      --muted: #a0a896;
      --rule: #2c3128;
      --rule-strong: #6b7462;
      --accent: #a3c586;
      --on-accent: #10140c;
      --tint: #232b1d;
      --marker: #4a3f0e;
      --on-marker: #f6e7a0;
      --late: #e38a5c;
      --progress: #82b6dc;
      --done: #6cc28a;
      --danger: #ef8272;
      --on-danger: #1a0805;
      --person-1: #7cc07a;
      --person-2: #7fb4dc;
      --person-3: #e59263;
      --person-4: #d9b24a;
      --person-5: #5cc2bf;
      --person-6: #e5757c;
      --person-7: #a3adb8;
      --person-8: #c29a66;
      --scrim: rgb(0 0 0 / .6);
      --lift: 0 1px 2px rgb(0 0 0 / .4), 0 16px 40px rgb(0 0 0 / .55);
    }
  }

  /* The same as above, chosen by hand. Kept identical by test_tokens. */
  :root[data-theme="dark"] {
    color-scheme: dark;
    --paper: #121410;
    --surface: #1c1f19;
    --sunk: #181b15;
    --ink: #e8ebe2;
    --muted: #a0a896;
    --rule: #2c3128;
    --rule-strong: #6b7462;
    --accent: #a3c586;
    --on-accent: #10140c;
    --tint: #232b1d;
    --marker: #4a3f0e;
    --on-marker: #f6e7a0;
    --late: #e38a5c;
    --progress: #82b6dc;
    --done: #6cc28a;
    --danger: #ef8272;
    --on-danger: #1a0805;
    --person-1: #7cc07a;
    --person-2: #7fb4dc;
    --person-3: #e59263;
    --person-4: #d9b24a;
    --person-5: #5cc2bf;
    --person-6: #e5757c;
    --person-7: #a3adb8;
    --person-8: #c29a66;
    --scrim: rgb(0 0 0 / .6);
    --lift: 0 1px 2px rgb(0 0 0 / .4), 0 16px 40px rgb(0 0 0 / .55);
  }

  /* Asked for more contrast by the system: the sun theme's weights and lines. */
  @media (prefers-contrast: more) {
    :root {
      --w-text: 500;
      --w-medium: 650;
      --w-strong: 800;
      --rule-w: 2px;
      --field-w: 2px;
    }
  }

  /* Direct sunlight: white, black, heavier strokes. Only ever chosen by hand. */
  :root[data-theme="sun"] {
    color-scheme: light;
    --paper: #ffffff;
    --surface: #ffffff;
    --sunk: #eef0ea;
    --ink: #000000;
    --muted: #2f342a;
    --rule: #7d8573;
    --rule-strong: #3b4135;
    --accent: #2a4a1b;
    --on-accent: #ffffff;
    --tint: #dfe8d5;
    --marker: #ffe14d;
    --on-marker: #000000;
    --late: #8f3000;
    --progress: #0f4f7a;
    --done: #145c2a;
    --danger: #9b1111;
    --on-danger: #ffffff;
    --scrim: rgb(0 0 0 / .5);
    --w-text: 500;
    --w-medium: 650;
    --w-strong: 800;
    --w-heavy: 800;
    --rule-w: 2px;
    --field-w: 2px;
  }
}

/* ---------------------------------------------------------------- reset */

@layer mica.reset {
  *, *::before, *::after { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
  body, h1, h2, h3, h4, p, ul, ol, dl, dd, figure, blockquote { margin: 0; }
  ul[class], ol[class] { list-style: none; padding: 0; }
  img, svg { display: block; max-width: 100%; }
  input, button, textarea, select { font: inherit; color: inherit; }
  button { cursor: pointer; }
  [hidden] { display: none !important; }
}

/* ----------------------------------------------------------------- base */

@layer mica.base {
  html {
    font-size: 106.25%; /* 17px */
    background: var(--paper);
    scroll-padding-top: calc(var(--bar-h) + 3rem);
  }
  /* the reader's own text size, in steps */
  html[data-text-size="90"] { font-size: 95.625%; }
  html[data-text-size="110"] { font-size: 116.875%; }
  html[data-text-size="120"] { font-size: 127.5%; }
  html[data-text-size="130"] { font-size: 138.125%; }
  body {
    min-height: 100dvh;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--font-text);
    font-weight: var(--w-text);
    font-size: var(--t-md);
    line-height: 1.5;
    font-variant-numeric: tabular-nums;
    -webkit-font-smoothing: antialiased;
    -webkit-tap-highlight-color: transparent;
  }
  h1, h2, h3 { line-height: 1.15; text-wrap: balance; font-weight: var(--w-strong); }
  /* Long words (spesialkallesignal) break rather than push the page wider. */
  h1, h2, h3, p, li, dd, dt, legend, .row-title { overflow-wrap: anywhere; }
  h2 { font-size: var(--t-lg); letter-spacing: -.01em; }
  h3 { font-size: var(--t-md); }
  p, li, dd { text-wrap: pretty; }
  strong, b { font-weight: var(--w-strong); }
  a { color: var(--accent); text-decoration-thickness: .08em; text-underline-offset: .18em; }
  a:hover { text-decoration-thickness: .14em; }
  :focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
  ::selection { background: var(--marker); color: var(--on-marker); }
  /* the parts the browser draws: native controls, the caret, scrollbars */
  :root { accent-color: var(--accent); caret-color: var(--accent); scrollbar-color: var(--rule-strong) transparent; }

  /* moving between pages cross-fades, where the browser can */
  @view-transition { navigation: auto; }
  .bar { view-transition-name: bar; }
  .tabbar { view-transition-name: tabbar; }
  .rail { view-transition-name: rail; }
  hr { border: 0; border-top: var(--rule-w) solid var(--rule); margin: var(--s-5) 0; }
}

/* --------------------------------------------------------------- layout */

@layer mica.layout {
  .skip {
    position: absolute; left: var(--s-2); top: -4rem; z-index: 100;
    padding: var(--s-2) var(--s-4); background: var(--ink); color: var(--paper);
    border-radius: var(--radius-control); font-weight: var(--w-strong);
  }
  .skip:focus { top: var(--s-2); }

  /* The small bar at the top of a phone screen: where you are, not what. */
  .bar {
    position: sticky; top: 0; z-index: 20;
    display: flex; align-items: center; gap: var(--s-2);
    height: calc(var(--bar-h) + env(safe-area-inset-top));
    padding: env(safe-area-inset-top) var(--s-2) 0 var(--gutter);
    background: var(--paper);
    border-bottom: var(--rule-w) solid transparent;
  }
  .bar.is-stuck { border-bottom-color: var(--rule); }
  .brand {
    display: flex; align-items: baseline; gap: .4em;
    color: var(--ink); text-decoration: none;
    font-weight: var(--w-heavy); font-size: var(--t-sm); letter-spacing: .01em;
  }
  .brand-year { color: var(--muted); font-weight: var(--w-medium); }
  .bar-end { margin-left: auto; display: flex; align-items: center; gap: var(--s-1); }
  .bar-end .is-add { color: var(--accent); }

  .main {
    width: 100%; max-width: var(--page-max);
    padding: var(--s-2) var(--gutter) calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--s-7));
  }

  /* Bottom tab bar: five places, the word always shown under the icon. */
  .tabbar {
    position: fixed; inset: auto 0 0 0; z-index: 30;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--surface);
    border-top: var(--rule-w) solid var(--rule);
  }
  .tabbar a, .tabbar button {
    position: relative;
    display: grid; justify-items: center; align-content: center; gap: 2px;
    height: var(--tabbar-h); padding: 0; border: 0; background: none;
    color: var(--muted); text-decoration: none;
    font-size: var(--t-xs); font-weight: var(--w-medium); line-height: 1.1;
  }
  .tabbar .icon { width: 1.5rem; height: 1.5rem; }
  .tabbar [aria-current="page"] { color: var(--accent); }
  .tabbar [aria-current="page"]::before {
    content: ""; position: absolute; top: calc(-1 * var(--rule-w)); left: 22%; right: 22%;
    height: 3px; border-radius: 0 0 3px 3px; background: var(--accent);
  }
  .tabbar .count { position: absolute; top: .45rem; left: calc(50% + .45rem); }

  .rail { display: none; }

  /* A page outside the app (waiting to be let in, a wall screen) empties the
     rail, bar and tab bar blocks and says so with body.is-bare. */
  body.is-bare .main { margin-inline: auto; padding-bottom: var(--s-7); }

  /* Desktop: a fixed rail on the left takes over from both bars. */
  @media (min-width: 60rem) {
    :root { --gutter: 2.5rem; --rail-col: var(--rail-w); }
    .shell {
      display: grid; grid-template-columns: var(--rail-col) minmax(0, 1fr);
      transition: grid-template-columns var(--settle) ease;
    }
    .bar, .tabbar { display: none; }
    .rail {
      position: sticky; top: 0; height: 100dvh; overflow: hidden auto; overscroll-behavior: contain;
      display: flex; flex-direction: column; gap: var(--s-4);
      padding: var(--s-3) var(--s-4) var(--s-5);
      border-right: var(--rule-w) solid var(--rule);
      transition: padding var(--settle) ease;
    }
    .rail-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; min-height: var(--tap); }
    .rail .brand { font-size: var(--t-md); padding: 0 var(--s-3); overflow: hidden; }
    .rail-fold [aria-expanded="true"] .when-folded,
    .rail-fold [aria-expanded="false"] .when-open { display: none; }
    /* What folds away keeps its width and is cut off by the closing edge,
       rather than re-wrapping every name on the way. */
    .rail-body {
      display: flex; flex-direction: column; gap: var(--s-5); flex: 1 0 auto;
      min-width: calc(var(--rail-w) - 2 * var(--s-4) - var(--rule-w));
      transition: opacity var(--settle) ease, visibility 0s;
    }

    /* Folded: the column narrows to a strip holding the button, and the rest
       goes out of sight and out of the tab order once it has gone. The
       server writes data-rail from the reader's cookie, so a page is drawn
       folded from its first paint. */
    :root[data-rail="folded"] { --rail-col: var(--rail-folded-w); }
    :root[data-rail="folded"] .rail { padding-inline: var(--s-2); overflow: hidden; }
    :root[data-rail="folded"] .rail .brand { padding-inline: 0; white-space: nowrap; }
    :root[data-rail="folded"] .rail .brand,
    :root[data-rail="folded"] .rail-body {
      visibility: hidden; opacity: 0;
      transition: opacity var(--quick) ease, visibility 0s var(--settle);
    }
    /* The room the rail gave back goes to a wide page; a reading column keeps
       its measure. */
    :root[data-rail="folded"] .main.is-wide { max-width: calc(72rem + var(--rail-w) - var(--rail-folded-w)); }
    .rail nav ul { display: grid; gap: 2px; list-style: none; padding: 0; }
    .rail nav a, .rail nav button {
      display: flex; align-items: center; gap: var(--s-3); width: 100%;
      min-height: 2.75rem; padding: 0 var(--s-3); border: 0; border-radius: var(--radius-control);
      background: none; color: var(--ink); text-decoration: none; text-align: left;
      font-weight: var(--w-medium);
    }
    .rail nav a:hover { background: var(--sunk); }
    .rail nav a span { hyphens: auto; }
    .rail nav [aria-current="page"] { background: var(--tint); color: var(--accent); }
    .rail nav .count { margin-left: auto; }
    .rail-foot { margin-top: auto; display: grid; gap: var(--s-3); }
    .main { padding: var(--s-6) var(--gutter) var(--s-7); }
    .main.is-wide { max-width: 72rem; }
    body.is-bare .shell { display: block; }
  }
}

/* ----------------------------------------------------------- components */

@layer mica.components {

  /* icons: 24-unit strokes, inline so they work without a connection */
  .icon {
    width: 1.25rem; height: 1.25rem; flex: none;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
  }
  :root[data-theme="sun"] .icon { stroke-width: 2.3; }

  /* page head: the title is the largest thing on the screen */
  .page-head { display: grid; gap: var(--s-1); padding: var(--s-2) 0 var(--s-5); }
  .page-head h1 {
    font-size: var(--t-2xl);
    font-weight: var(--w-heavy); letter-spacing: -.025em; line-height: 1.02;
  }
  @media (min-width: 60rem) { .page-head h1 { font-size: var(--t-3xl); } }
  .page-head .lede { color: var(--muted); font-size: var(--t-md); }
  .page-head .lede strong { color: var(--ink); }
  .head-row { display: flex; align-items: center; justify-content: space-between; margin-right: calc(-1 * var(--s-2)); }
  .back {
    display: inline-flex; align-items: center; gap: var(--s-1);
    min-height: 2.25rem; margin-left: -.35rem; width: fit-content;
    color: var(--muted); text-decoration: none; font-weight: var(--w-medium);
  }
  .back:hover { color: var(--ink); }

  /* callsigns and clock times: the one place the mono face is used */
  .call, .clock {
    font-family: var(--font-mono); font-weight: var(--w-medium);
    letter-spacing: .01em; font-variant-numeric: tabular-nums slashed-zero;
  }
  h1 .call { letter-spacing: -.01em; }

  /* sections */
  .section { display: grid; gap: var(--s-3); margin-top: var(--s-6); }
  .section-head { display: flex; align-items: baseline; gap: var(--s-3); }
  .section-head > :last-child:not(:first-child) { margin-left: auto; font-size: var(--t-sm); }
  .prose { max-width: var(--measure); line-height: 1.6; }
  .prose > * + * { margin-top: var(--s-4); }
  /* long text: a guide or a course page, read at a table or in a queue */
  .prose h2 { font-size: var(--t-lg); margin-top: var(--s-6); }
  .prose h3 { font-size: var(--t-md); margin-top: var(--s-5); }
  .prose :is(h2, h3, h4) { scroll-margin-top: calc(var(--bar-h) + var(--s-4)); }
  .prose ul, .prose ol { padding-left: 1.3rem; display: grid; gap: var(--s-1); }
  .prose li::marker { color: var(--muted); }
  .prose blockquote {
    margin: 0; padding-left: var(--s-4); color: var(--muted);
    border-left: var(--rule-w) solid var(--rule-strong);
  }
  .prose code {
    font-family: var(--font-mono); font-size: .92em;
    background: var(--sunk); padding: .05rem .3rem; border-radius: var(--radius-tag);
  }
  .prose pre {
    overflow-x: auto; padding: var(--s-3) var(--s-4);
    background: var(--sunk); border-radius: var(--radius-control);
    font-size: var(--t-sm); line-height: 1.5;
  }
  .prose pre code { background: none; padding: 0; font-size: inherit; }
  .prose hr { border: 0; border-top: var(--rule-w) solid var(--rule); margin-block: var(--s-6); }
  /* A table in running text scrolls rather than widening the page: wrapped in
     .table-scroll it fills the column of text and scrolls in the wrapper;
     unwrapped it scrolls inside itself. */
  .prose .table-scroll { overflow-x: auto; max-width: 100%; }
  .prose .table-scroll > table { display: table; width: 100%; }
  .prose table {
    display: block; overflow-x: auto; max-width: 100%;
    border-collapse: collapse; font-size: var(--t-sm); font-variant-numeric: tabular-nums;
  }
  .prose th, .prose td { min-width: 4.5rem; text-align: left; padding: var(--s-1) var(--s-3) var(--s-1) 0; border-bottom: var(--rule-w) solid var(--rule); vertical-align: top; }
  .prose th { font-weight: var(--w-strong); border-bottom-color: var(--rule-strong); }
  /* A table somebody is still filling in is mostly blank cells, and lines
     between the rows alone leave a stack of rules on the screen. The line down
     the side of a column is what says there is one. */
  .prose :is(th, td) + :is(th, td) { padding-left: var(--s-3); border-left: var(--rule-w) solid var(--rule); }
  .prose td:empty::after { content: ""; display: inline-block; min-height: 1lh; }
  .prose figure { margin: var(--s-5) 0; }
  .prose img { max-width: 100%; height: auto; display: block; border-radius: var(--radius-control); }
  .prose figcaption { font-size: var(--t-sm); color: var(--muted); margin-top: var(--s-1); }
  .quiet { color: var(--muted); }
  .small { font-size: var(--t-sm); }

  /* buttons: action is filled, choice is outlined, toggle carries a box */
  .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
    min-height: var(--tap); padding: 0 var(--s-5);
    border: var(--field-w) solid var(--accent); border-radius: var(--radius-control);
    background: var(--accent); color: var(--on-accent);
    font-weight: var(--w-strong); font-size: var(--t-md); line-height: 1.15;
    text-decoration: none; text-align: center; text-wrap: balance; max-width: 100%;
    transition: transform var(--quick) ease-out, background-color var(--quick);
  }
  .btn:hover { text-decoration: none; filter: brightness(1.06); }
  .btn:active { transform: scale(.97); }
  .btn:focus-visible { outline-color: var(--ink); }
  .btn.is-quiet { background: var(--tint); border-color: var(--tint); color: var(--accent); }
  .btn.is-line { background: transparent; border-color: var(--rule-strong); color: var(--ink); }
  .btn.is-danger { background: var(--danger); border-color: var(--danger); color: var(--on-danger); }
  .btn.is-block { display: flex; width: 100%; }
  .btn.is-big { min-height: 3.5rem; font-size: var(--t-lg); }
  .btn[disabled], .btn[aria-disabled="true"] {
    background: var(--sunk); border-color: var(--sunk); color: var(--muted); cursor: not-allowed; filter: none;
  }
  .icon-btn {
    position: relative;
    display: inline-grid; place-items: center; flex: none;
    width: var(--tap); height: var(--tap); padding: 0;
    border: 0; border-radius: var(--radius-control); background: none; color: var(--ink);
  }
  .icon-btn:hover { background: var(--sunk); }
  .icon-btn[aria-pressed="true"] { background: var(--ink); color: var(--paper); }
  .icon-btn.is-framed { border: var(--field-w) solid var(--rule-strong); }
  /* one that does nothing here, such as the top row's "up", looks it */
  .icon-btn:disabled { color: var(--rule-strong); cursor: not-allowed; background: none; }
  .icon-btn.is-framed:disabled { border-color: var(--rule); }
  .icon-btn .count { position: absolute; top: -.3rem; right: -.3rem; }
  .linkish {
    padding: 0; border: 0; background: none; color: var(--accent);
    text-decoration: underline; text-underline-offset: .18em; font-weight: var(--w-medium);
  }
  .actions { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }

  /* a number waiting for somebody */
  .count {
    display: inline-grid; place-items: center;
    min-width: 1.25rem; height: 1.25rem; padding: 0 .3rem;
    border-radius: 1rem; background: var(--accent); color: var(--on-accent);
    font-size: .72rem; font-weight: var(--w-heavy); line-height: 1;
  }

  /* tags: a word about a thing (category, "free"), not a control */
  .tag {
    display: inline-flex; align-items: center; gap: .3em;
    padding: .1rem .45rem; border-radius: var(--radius-tag);
    background: var(--sunk); color: var(--muted);
    font-size: var(--t-xs); font-weight: var(--w-strong); line-height: 1.5;
  }
  .tag.is-accent { background: var(--tint); color: var(--accent); }
  .tag.is-late { background: color-mix(in srgb, var(--late) 14%, var(--paper)); color: var(--late); }
  .tag.is-done { background: color-mix(in srgb, var(--done) 14%, var(--paper)); color: var(--done); }

  /* a person: coloured dot, callsign, first name */
  .who { display: inline-flex; flex-wrap: wrap; align-items: baseline; column-gap: .35em; color: var(--ink); }
  .who .call { white-space: nowrap; }
  .who::before {
    content: ""; align-self: center; width: .6rem; height: .6rem; border-radius: 50%;
    background: var(--dot, var(--muted));
  }
  .who .call { font-size: .92em; }
  .who.is-nobody { color: var(--muted); font-style: italic; }
  .who.is-nobody::before { background: none; box-shadow: inset 0 0 0 1.5px var(--rule-strong); }
  .p1 { --dot: var(--person-1); } .p2 { --dot: var(--person-2); }
  .p3 { --dot: var(--person-3); } .p4 { --dot: var(--person-4); }
  .p5 { --dot: var(--person-5); } .p6 { --dot: var(--person-6); }
  .p7 { --dot: var(--person-7); } .p8 { --dot: var(--person-8); }

  /* tabs: a view of the same list. Underline, count, scrolls sideways */
  .tabs {
    display: flex; gap: var(--s-4); overflow-x: auto; scrollbar-width: none;
    margin: 0 calc(-1 * var(--gutter)); padding: 0 var(--gutter);
    border-bottom: var(--rule-w) solid var(--rule);
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs a {
    position: relative; flex: none;
    display: inline-flex; align-items: center; gap: .4em;
    min-height: var(--tap); color: var(--muted); text-decoration: none;
    font-weight: var(--w-strong);
  }
  .tabs a .n { font-size: var(--t-xs); font-weight: var(--w-strong); color: var(--muted); }
  .tabs a[aria-current] { color: var(--ink); }
  .tabs a[aria-current]::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: calc(-1 * var(--rule-w));
    height: 3px; border-radius: 3px 3px 0 0; background: var(--accent);
  }

  /* find: search the page, filters behind one button beside it */
  .find { display: flex; gap: var(--s-2); margin-top: var(--s-4); }
  .find-field { position: relative; flex: 1; }
  .find-field .icon { position: absolute; left: .85rem; top: 50%; translate: 0 -50%; color: var(--muted); pointer-events: none; }
  .find-field input { padding-left: 2.6rem; }
  .find-empty { padding: var(--s-5) 0; color: var(--muted); }

  /* fields */
  .field { display: grid; gap: var(--s-1); }
  .field + .field, .field + fieldset, fieldset + .field { margin-top: var(--s-5); }
  .label { font-weight: var(--w-strong); font-size: var(--t-sm); }
  .label-row { display: flex; align-items: center; gap: var(--s-1); }
  .label .opt { color: var(--muted); font-weight: var(--w-text); }
  .help { color: var(--muted); font-size: var(--t-sm); }
  .error { color: var(--danger); font-size: var(--t-sm); font-weight: var(--w-strong); display: flex; gap: .35em; align-items: center; }
  .input, .field input:not([type="checkbox"], [type="radio"], [type="range"]), .field select, .field textarea {
    width: 100%; min-width: 0; min-height: 3.1rem; padding: .7rem .9rem;
    border: var(--field-w) solid var(--rule-strong); border-radius: var(--radius-control);
    background: var(--surface); color: var(--ink);
    font-size: max(16px, 1rem); line-height: 1.35;
  }
  .field textarea { min-height: 7rem; resize: vertical; }
  .field select {
    appearance: none; padding-right: 2.6rem;
    background-image: none;
  }
  .select { position: relative; }
  .select .icon { position: absolute; right: .85rem; top: 50%; translate: 0 -50%; pointer-events: none; color: var(--muted); }
  .field input:focus, .field select:focus, .field textarea:focus, .input:focus {
    outline: 3px solid color-mix(in srgb, var(--accent) 35%, transparent); outline-offset: 0; border-color: var(--accent);
  }
  .field.has-error input, .field.has-error select, .field.has-error textarea { border-color: var(--danger); }
  ::placeholder { color: var(--muted); opacity: .8; }
  .pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 9.5rem), 1fr)); gap: var(--s-5) var(--s-3); align-items: start; }
  .pair > .field { min-width: 0; }
  .form { display: grid; gap: var(--s-5); }
  .form .field + .field, .pair > .field + .field { margin-top: 0; }
  .form > .form-foot { margin-top: var(--s-3); }
  .form-foot { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-6); }
  .form-errors {
    display: flex; gap: var(--s-2); align-items: flex-start; color: var(--danger);
    padding: var(--s-3) var(--s-4); border-radius: var(--radius-control);
    background: color-mix(in srgb, var(--danger) 9%, var(--paper));
    font-weight: var(--w-strong); margin-bottom: var(--s-5);
  }
  fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
  legend { padding: 0; margin-bottom: var(--s-2); }

  /* check: one checkbox or radio before its words; the whole row is the target */
  .check {
    display: flex; align-items: flex-start; gap: var(--s-3);
    min-height: var(--tap); padding: .7rem 0; cursor: pointer; font-weight: var(--w-medium);
  }
  .check input {
    appearance: none; flex: none; display: grid; place-items: center;
    width: 1.5rem; height: 1.5rem; margin: 0; background: var(--surface);
    border: var(--field-w) solid var(--rule-strong); border-radius: 6px;
  }
  .check input[type="radio"] { border-radius: 50%; }
  .check input::after { content: ""; scale: 0; }
  .check input[type="checkbox"]::after {
    width: .75rem; height: .4rem; margin-top: -.18rem;
    border: 0 solid var(--on-accent); border-width: 0 0 2.5px 2.5px; rotate: -45deg;
  }
  .check input[type="radio"]::after { width: .7rem; height: .7rem; border-radius: 50%; background: var(--accent); }
  .check input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
  .check input[type="radio"]:checked { border-color: var(--accent); }
  .check input:checked::after { scale: 1; }
  .check input:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
  .check input:disabled { background: var(--sunk); border-style: dashed; }
  .check > span { padding-top: .1rem; }
  .options { display: grid; }
  .options > .check + .check { border-top: var(--rule-w) solid var(--rule); }

  /* two or three controls that make one value: date and time, day month year */
  .multi { display: flex; flex-wrap: wrap; gap: var(--s-2); }
  .multi > * { flex: 1 1 8rem; min-width: 0; }
  .field .multi select { appearance: auto; }

  /* file: the current one, a way to remove it, and the chooser */
  .file { display: grid; gap: var(--s-2); }
  .file-current { display: flex; align-items: center; gap: var(--s-2); overflow-wrap: anywhere; }
  .file-current .icon { color: var(--muted); }
  .field input[type="file"] { padding: .45rem; }
  input[type="file"]::file-selector-button {
    min-height: 2.4rem; margin-right: var(--s-3); padding: 0 var(--s-4);
    border: var(--field-w) solid var(--rule-strong); border-radius: calc(var(--radius-control) - 3px);
    background: var(--surface); color: var(--ink); font: inherit; font-weight: var(--w-strong); cursor: pointer;
  }

  /* odd sizes among the text-like fields */
  .field input[type="color"] { width: 5rem; padding: .3rem; cursor: pointer; }
  .field select[multiple] { min-height: 8.5rem; padding: .35rem; }
  .field select[multiple] option { padding: .4rem .5rem; border-radius: 6px; }

  /* locked: dimmed and dashed, so it does not rest on colour alone */
  .field :is(input, select, textarea):disabled {
    background: var(--sunk); color: var(--muted); border-style: dashed; cursor: not-allowed; opacity: 1;
  }
  .form-errors > div { display: grid; gap: var(--s-1); }

  /* choice: one of a few, drawn as a segmented control */
  .choice {
    /* Equal segments while they fit; a segment never gets narrower than its
       longest word, and the control takes a second row before it would. */
    display: flex; flex-wrap: wrap;
    padding: 3px; gap: 3px; border-radius: calc(var(--radius-control) + 3px);
    background: var(--sunk);
  }
  .choice label, .choice button {
    flex: 1 1 0; min-width: min-content; overflow-wrap: normal;
    position: relative;
    display: flex; align-items: center; justify-content: center; gap: .4em;
    min-height: calc(var(--tap) - 6px); padding: 0 var(--s-2);
    border: 0; border-radius: var(--radius-control); background: none;
    color: var(--muted); font-weight: var(--w-strong); text-align: center; line-height: 1.15;
  }
  .choice input { position: absolute; opacity: 0; pointer-events: none; }
  .choice label:has(input:checked), .choice button[aria-pressed="true"] {
    background: var(--surface); color: var(--ink);
    box-shadow: 0 0 0 var(--field-w) var(--rule-strong);
  }
  .choice label:has(input:focus-visible) { outline: 3px solid var(--accent); outline-offset: 1px; }
  .choice .icon { width: 1.1rem; height: 1.1rem; }
  /* A switch whose own words change under it — the theme, which changes the
     weight the page is set in, and the language, which changes every word —
     stands in a grid. Wrapped, it re-wraps when the words change width, and
     the button just pressed moves out from under the thumb. */
  .choice.is-grid { display: grid; grid-template-columns: 1fr 1fr; }

  /* toggles: several of many, each with a box that fills. The same drawn two
     ways: a label round a checkbox in a form, or a button with aria-pressed
     and a .box when each press is sent at once, one small form per button */
  .toggles { display: flex; flex-wrap: wrap; gap: var(--s-2); }
  .toggles > form { display: contents; }
  /* Many of many — a roster to pick from — stands in columns rather than
     wrapping as chips, so the eye runs down it the way it runs down a list and
     every box is where the one above it was. Wrapped, one longer name moves
     every box after it, and the row re-wraps whenever the words change: the
     same trap as the theme switch in docs/decisions.md M-001.
     14rem, not less: a column has to hold the longest label at the largest
     text size without splitting the word, and the track is in rem so it grows
     when the reader turns the text up. On a phone that comes to one column at
     every text size, which is the right answer there; on a desk, three or
     four, each holding more than one name.
     `min(…, 100%)` is what keeps that honest: a bare 14rem is a floor the
     track cannot go under, so at 320px and the largest text the grid grew
     wider than the phone and took the whole page sideways with it. Capped at
     the container, the column narrows instead. */
  .toggles[data-many] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  }
  .toggles label, .toggles button {
    display: inline-flex; align-items: center; gap: .5em;
    min-height: var(--tap); padding: 0 var(--s-4) 0 var(--s-3);
    border: var(--field-w) solid var(--rule-strong); border-radius: var(--radius-control);
    background: none; color: var(--ink); font: inherit;
    font-weight: var(--w-medium); cursor: pointer;
  }
  .toggles input, .toggles .box {
    appearance: none; margin: 0; flex: none; width: 1.3rem; height: 1.3rem;
    border: var(--field-w) solid var(--rule-strong); border-radius: 5px; background: var(--surface);
    display: grid; place-items: center;
  }
  .toggles input::after, .toggles .box::after {
    content: ""; width: .7rem; height: .38rem; margin-top: -.15rem;
    border: 0 solid var(--on-accent); border-width: 0 0 2.5px 2.5px; rotate: -45deg; scale: 0;
  }
  .toggles input:checked, .toggles [aria-pressed="true"] .box { background: var(--accent); border-color: var(--accent); }
  .toggles input:checked::after, .toggles [aria-pressed="true"] .box::after { scale: 1; }
  .toggles label:has(input:checked), .toggles button[aria-pressed="true"] { border-color: var(--accent); background: var(--tint); }
  .toggles button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
  .toggles input:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

  /* settings: a row that is its own label, the control at the thumb's end */
  .settings > * + * { border-top: var(--rule-w) solid var(--rule); }
  .settings .field + .field { margin-top: 0; }
  .setting {
    display: flex; align-items: center; gap: var(--s-4);
    min-height: 3.5rem; padding: var(--s-3) 0; cursor: pointer;
  }
  .setting-text { display: grid; gap: 2px; flex: 1; min-width: 0; }
  .setting-label { font-weight: var(--w-medium); }
  .setting .value { color: var(--muted); }
  .setting > .icon:last-child { color: var(--muted); }
  a.setting { color: var(--ink); text-decoration: none; }

  /* switch: the thumb moves, the track fills, and a mark inside says which.
     A switch in a list posts the moment it is pressed, so there it is a
     button and says so with aria-checked; drawn the same either way, because
     a grey track under a setting that is on reads as one that did not take. */
  .switch {
    appearance: none; position: relative; flex: none; margin: 0; padding: 0; border: 0;
    cursor: pointer;
    width: 3.25rem; height: 2rem; border-radius: 1rem;
    background: var(--rule-strong); transition: background-color var(--quick);
  }
  .switch::before {
    content: ""; position: absolute; top: 3px; left: 3px; z-index: 1;
    width: calc(2rem - 6px); height: calc(2rem - 6px); border-radius: 50%;
    background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / .3);
    transition: translate var(--settle) cubic-bezier(.3, .7, .2, 1);
  }
  .switch::after { /* off: a small ring on the right */
    content: ""; position: absolute; top: 50%; right: .55rem; translate: 0 -50%;
    width: .5rem; height: .5rem; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--surface);
  }
  .switch:checked, .switch[aria-checked="true"] { background: var(--accent); }
  .switch:checked::before, .switch[aria-checked="true"]::before { translate: 1.25rem 0; }
  .switch:checked::after, .switch[aria-checked="true"]::after { /* on: a short bar on the left */
    right: auto; left: .8rem; width: 2px; height: .7rem; border-radius: 1px;
    box-shadow: none; background: var(--on-accent);
  }
  .switch:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
  .switch:disabled { opacity: .45; cursor: not-allowed; }

  /* stepper: a small whole number, minus and plus either side */
  .stepper {
    display: inline-flex; align-items: center; gap: 2px; width: fit-content;
    padding: 2px; border: var(--field-w) solid var(--rule-strong); border-radius: var(--radius-control);
    background: var(--surface);
  }
  .field .stepper input {
    width: 3.25rem; min-height: 2.75rem; padding: 0; border: 0; background: none;
    text-align: center; font-weight: var(--w-strong); font-size: var(--t-lg);
  }
  .field .stepper input:focus { outline-offset: 2px; }
  .js .stepper input { -moz-appearance: textfield; appearance: textfield; }
  .js .stepper input::-webkit-inner-spin-button, .js .stepper input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
  .stepper .icon-btn { width: 2.75rem; height: 2.75rem; color: var(--accent); }
  .stepper .icon-btn:disabled { color: var(--rule-strong); cursor: not-allowed; background: none; }

  /* range: a big thumb for a gloved hand, the value in words beside it */
  .range { display: flex; align-items: center; gap: var(--s-4); }
  .range output { min-width: 3.5rem; text-align: right; font-weight: var(--w-strong); }
  .range input {
    appearance: none; flex: 1; min-width: 0; height: var(--tap); margin: 0; background: none; cursor: pointer;
  }
  .range input::-webkit-slider-runnable-track { height: 6px; border-radius: 3px; background: var(--rule-strong); }
  .range input::-moz-range-track { height: 6px; border-radius: 3px; background: var(--rule-strong); }
  .range input::-moz-range-progress { height: 6px; border-radius: 3px; background: var(--accent); }
  .range input::-webkit-slider-thumb {
    appearance: none; width: 1.75rem; height: 1.75rem; margin-top: calc(3px - .875rem);
    border-radius: 50%; background: var(--accent); border: 3px solid var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / .3);
  }
  .range input::-moz-range-thumb {
    width: 1.75rem; height: 1.75rem; border-radius: 50%; background: var(--accent);
    border: 3px solid var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / .3);
  }

  /* groups and rows: the list is the unit, not the card */
  .groups { margin-top: var(--s-5); }
  .group + .group { margin-top: 0; }
  .group .rows { padding-bottom: var(--s-2); }
  .group-head {
    position: sticky; top: var(--bar-h); z-index: 5;
    display: flex; align-items: baseline; gap: var(--s-3);
    padding: var(--s-2) 0;
    background: var(--sunk); border-block: var(--rule-w) solid var(--rule);
  }
  .group-head h2, .group-head h3 { font-size: var(--t-md); font-weight: var(--w-heavy); letter-spacing: 0; }
  .group-head .today { color: var(--accent); }
  .group-head .quiet { margin-left: auto; font-size: var(--t-sm); }
  @media (min-width: 60rem) { .group-head { top: 0; } }

  .rows { margin: 0; }
  .row {
    position: relative;
    display: grid; grid-template-columns: 4.1rem var(--tap) minmax(0, 1fr); align-items: start;
    column-gap: var(--s-1);
    padding: var(--s-2) 0;
    transition: opacity var(--settle), translate var(--settle);
  }
  .row + .row::before {
    content: ""; position: absolute; top: 0; right: 0;
    left: calc(4.1rem + var(--tap) + var(--s-1));
    border-top: var(--rule-w) solid var(--rule);
  }
  .row:hover { background: color-mix(in srgb, var(--sunk) 60%, transparent); }
  .row-time {
    display: grid; justify-items: end; padding-top: .7rem; padding-right: var(--s-1);
    line-height: 1.1;
  }
  .row-time .clock { font-size: var(--t-md); }
  .row-time .kind { font-size: .7rem; color: var(--muted); margin-top: 2px; }
  .row-body { display: grid; gap: .15rem; padding: .55rem 0 .35rem; min-width: 0; }
  .row-title {
    color: var(--ink); text-decoration: none; font-weight: var(--w-medium);
    font-size: var(--t-md); line-height: 1.3;
  }
  .row-title::after { content: ""; position: absolute; inset: 0; } /* whole row opens the task */
  .row-meta {
    display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--s-3); row-gap: 0;
    color: var(--muted); font-size: var(--t-sm);
  }
  .row .mark, .row form { position: relative; z-index: 1; }

  /* a list of things rather than events: no time, no tick, the title at the
     gutter. The mark button was what gave a row its tap height, so a row
     without one has to reach it itself — the whole row is still the link. */
  .rows.is-untimed > .row {
    grid-template-columns: minmax(0, 1fr);
    align-items: center; min-height: var(--tap);
  }
  .rows.is-untimed > .row:has(> .row-aside) { grid-template-columns: minmax(0, 1fr) auto; }
  .rows.is-untimed > .row + .row::before { left: 0; }
  .rows.is-untimed > .row > .row-body { padding: var(--s-1) 0; }
  .row-aside { justify-self: end; align-self: center; color: var(--muted); font-size: var(--t-sm); }
  /* the title's overlay is the whole row, so anything else to press in one
     has to be lifted above it or the tap never reaches it */
  .rows.is-untimed :is(a, button):not(.row-title) { position: relative; z-index: 1; }

  /* the late time says it once, in rust; no chip on every row */
  .row.is-late .row-time .clock, .row.is-late .row-time .kind { color: var(--late); }
  .late-by { color: var(--late); font-weight: var(--w-strong); }

  /* now: the one loud thing, a marker stroke over the time */
  .row.is-now .row-time .clock {
    background: var(--marker); color: var(--on-marker);
    padding: .05rem .3rem; margin-right: -.3rem; border-radius: 3px;
    box-decoration-break: clone;
  }
  .row-meta .now-label { background: none; padding: 0; color: var(--ink); }
  .now-label { background: var(--marker); color: var(--on-marker); padding: 0 .3rem; border-radius: 3px; font-weight: var(--w-strong); }

  /* done: out of the way */
  .row[data-state="done"] .row-title { color: var(--muted); text-decoration: line-through; text-decoration-thickness: 1.5px; }
  .row[data-state="done"] .row-time { opacity: .55; }
  .row[data-state="done"].is-late .row-time .clock { color: var(--muted); }
  .row.is-leaving { opacity: 0; translate: 1.25rem 0; }

  /* state mark: empty ring, half, full with a tick. Shape says it; colour agrees */
  .mark {
    display: grid; place-items: center; width: var(--tap); height: var(--tap);
    margin-top: .1rem; padding: 0; border: 0; border-radius: 50%; background: none;
    color: var(--rule-strong);
  }
  .mark .icon { width: 1.6rem; height: 1.6rem; stroke-width: 2; }
  .mark:hover { background: var(--sunk); }
  [data-state="progress"] .mark, .mark.is-progress { color: var(--progress); }
  [data-state="done"] .mark, .mark.is-done { color: var(--done); }
  .mark .m-todo, .mark .m-progress, .mark .m-done { display: none; }
  [data-state="todo"] .mark .m-todo,
  [data-state="progress"] .mark .m-progress,
  [data-state="done"] .mark .m-done { display: block; }
  .mark:active { transform: scale(.9); }
  /* a mark nobody here may tick: printed, not pressable, the ring dashed as a
     disabled checkbox is */
  .mark.is-static { cursor: default; }
  .mark.is-static:hover { background: none; }
  .mark.is-static:active { transform: none; }
  .mark.is-static .m-todo { stroke-dasharray: 3.5 3; }
  .state-word { font-weight: var(--w-strong); }
  [data-state="progress"] .state-word { color: var(--progress); }
  .state-word { display: inline-flex; align-items: center; gap: .3em; }
  .state-word .icon { width: 1.05rem; height: 1.05rem; }
  .state-word.is-done { color: var(--done); }
  .state-word.is-missing { color: var(--late); }
  [data-state="done"] .state-word { color: var(--done); }

  /* facts: term beside value on a phone, hairline between */
  .facts { display: grid; }
  .facts > div {
    display: grid; grid-template-columns: minmax(0, 7rem) minmax(0, 1fr); gap: var(--s-3);
    padding: var(--s-3) 0; border-top: var(--rule-w) solid var(--rule);
  }
  .facts > div:last-child { border-bottom: var(--rule-w) solid var(--rule); }
  .facts dt { color: var(--muted); font-size: var(--t-sm); padding-top: .12rem; }
  .facts dd { min-width: 0; }

  /* status block: a state and the one thing to do about it */
  .status {
    display: grid; gap: var(--s-3);
    padding: var(--s-4); border-radius: var(--radius-control);
    background: var(--sunk);
  }
  .status-line { display: flex; align-items: center; gap: var(--s-3); }
  .status-line .big { font-size: var(--t-lg); font-weight: var(--w-heavy); line-height: 1.2; }
  .status.is-on { background: color-mix(in srgb, var(--done) 12%, var(--paper)); }
  .status.is-on .status-line .icon { color: var(--done); }
  /* Away: lent out, taken, not where it belongs. As loud as is-on, because
     "somebody has this" is the thing worth noticing on the page. */
  .status.is-away { background: color-mix(in srgb, var(--late) 12%, var(--paper)); }
  .status.is-away .status-line .icon { color: var(--late); }

  /* thread: comments, oldest first */
  .thread { display: grid; gap: var(--s-4); }
  .thread li { display: grid; gap: var(--s-1); }
  .thread-head { display: flex; align-items: baseline; gap: var(--s-2); }
  .thread .when { color: var(--muted); font-size: var(--t-sm); }
  .thread p { max-width: 65ch; }

  /* empty: a list with nothing in it, and what to do */
  .empty {
    display: grid; gap: var(--s-3); justify-items: start;
    padding: var(--s-5) var(--s-4); border: var(--field-w) dashed var(--rule-strong);
    border-radius: var(--radius-control); color: var(--muted);
  }
  .empty strong { color: var(--ink); }

  /* progress: a native bar with the numbers written beside it */
  .progress { display: grid; gap: var(--s-1); }
  .progress-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-3); }
  .progress-text { color: var(--muted); font-size: var(--t-sm); }
  .progress progress {
    appearance: none; width: 100%; height: .5rem; border: 0; border-radius: .25rem;
    background: var(--sunk); overflow: hidden; color: var(--done);
  }
  .progress progress::-webkit-progress-bar { background: var(--sunk); border-radius: .25rem; }
  .progress progress::-webkit-progress-value { background: var(--done); border-radius: .25rem; }
  .progress progress::-moz-progress-bar { background: var(--done); border-radius: .25rem; }
  :root[data-theme="sun"] .progress progress { box-shadow: inset 0 0 0 1px var(--rule-strong); }

  /* note: something worth knowing on this page, said with an icon and words */
  .note {
    display: flex; gap: var(--s-3); align-items: flex-start;
    padding: var(--s-3) var(--s-4); border-radius: var(--radius-control);
    background: var(--sunk); color: var(--ink);
  }
  .note > .icon { margin-top: .15rem; color: var(--muted); }
  .note.is-info { background: color-mix(in srgb, var(--progress) 11%, var(--paper)); }
  .note.is-info > .icon { color: var(--progress); }
  .note.is-warn { background: color-mix(in srgb, var(--late) 11%, var(--paper)); }
  .note.is-warn > .icon { color: var(--late); }
  .note.is-error { background: color-mix(in srgb, var(--danger) 11%, var(--paper)); }
  .note.is-error > .icon { color: var(--danger); }
  .note.is-ok { background: color-mix(in srgb, var(--done) 11%, var(--paper)); }
  .note.is-ok > .icon { color: var(--done); }
  .note p { max-width: 60ch; }

  /* hint: a question mark that opens a short explanation, no script needed */
  .hint-btn {
    display: inline-grid; place-items: center; width: 2.75rem; height: 2.75rem; margin: -.6rem -.4rem -.6rem 0;
    padding: 0; border: 0; border-radius: 50%; background: none; color: var(--muted); vertical-align: middle;
  }
  .hint-btn:hover { color: var(--accent); background: var(--sunk); }
  .hint {
    inset: auto var(--gutter) calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--s-3)) var(--gutter);
    max-width: 30rem; margin: 0 auto; padding: var(--s-3) var(--s-4);
    border: 0; border-radius: var(--radius-control);
    background: var(--ink); color: var(--paper); box-shadow: var(--lift);
    font-weight: var(--w-medium);
  }
  @media (min-width: 60rem) { .hint { inset: auto var(--s-6) var(--s-6) auto; } }

  /* disclosure: more that most people do not need, one tap away */
  .disclosure { border-block: var(--rule-w) solid var(--rule); }
  .disclosure + .disclosure { border-top: 0; }
  .disclosure > summary {
    display: flex; align-items: center; gap: var(--s-3);
    min-height: var(--tap); list-style: none; cursor: pointer; font-weight: var(--w-strong);
  }
  /* The name takes the room, so what a row says about itself ends where the
     row above it ended rather than stepping in and out with the name's
     length. In the summary's weight it would be a second heading on the
     line; it is the note beside the name. */
  .disclosure > summary > :first-child { margin-right: auto; }
  .disclosure > summary .quiet { font-weight: var(--w-text); text-align: right; }
  .disclosure > summary::-webkit-details-marker { display: none; }
  .disclosure > summary .icon { transition: rotate var(--quick); color: var(--muted); }
  .disclosure[open] > summary .icon { rotate: 180deg; }
  .disclosure > :not(summary) { padding-bottom: var(--s-4); }

  /* action sheet: the rarer things to do, destructive apart, cancel last */
  .action-list { display: grid; }
  .action-list > * + * { border-top: var(--rule-w) solid var(--rule); }
  .action-list button, .action-list a {
    display: flex; align-items: center; gap: var(--s-3); width: 100%; min-height: 3.25rem;
    padding: 0; border: 0; background: none; color: var(--ink); text-decoration: none; text-align: left;
    font-weight: var(--w-medium);
  }
  .action-list .icon { color: var(--muted); }
  .action-list .is-danger, .action-list .is-danger .icon { color: var(--danger); }
  .action-list + .action-list { margin-top: var(--s-4); border-top: var(--rule-w) solid var(--rule); }
  .sheet .btn.is-block { margin-top: var(--s-4); }

  /* busy: a pressed button while the page goes to the server */
  .btn[aria-busy="true"] { opacity: .7; cursor: progress; }

  /* figures: a few numbers worth knowing, side by side, no boxes */
  .figures {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr));
    gap: var(--s-4) var(--s-5); margin: 0 0 var(--s-5);
  }
  .figures > div { display: grid; gap: 2px; padding-top: var(--s-2); border-top: var(--field-w) solid var(--ink); }
  .figures dt { color: var(--muted); font-size: var(--t-sm); }
  .figures dd { font-size: var(--t-xl); font-weight: var(--w-heavy); line-height: 1.1; }
  .figures dd small { font-size: var(--t-sm); color: var(--muted); font-weight: var(--w-medium); }

  /* table: for comparing; on a phone .is-stacked turns each row into a block */
  /* display: table, so a table that also carries a spacing class such as
     .section stays a table. As a grid its header row comes off its columns. */
  .table { display: table; width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
  .table th, .table td { padding: var(--s-3) var(--s-3); text-align: left; vertical-align: top; }
  .table thead th {
    color: var(--muted); font-size: var(--t-sm); font-weight: var(--w-strong); white-space: nowrap;
    border-bottom: var(--field-w) solid var(--rule-strong); vertical-align: bottom;
  }
  .table tbody td { border-bottom: var(--rule-w) solid var(--rule); }
  .table tbody tr:hover { background: color-mix(in srgb, var(--sunk) 70%, transparent); }
  .table tbody tr:has(.is-check input:checked) { background: var(--tint); }
  .table tfoot :is(td, th) { border-top: var(--field-w) solid var(--rule-strong); font-weight: var(--w-strong); }
  .table .num { text-align: right; }
  .table .is-check { width: 3rem; padding-block: 0; }
  .table .is-check .check { min-height: var(--tap); padding: .6rem 0; }
  .table .sort { display: inline-flex; align-items: center; gap: .25rem; color: inherit; text-decoration: none; }
  .table .sort:hover { color: var(--ink); }
  .table .sort .icon { width: 1rem; height: 1rem; }
  .table .sort .is-up { rotate: 180deg; }
  .table [aria-sort] { color: var(--ink); }
  /* position, so what is hidden inside it is hidden inside it: a caption
     or a column heading only a screen reader reads is placed absolutely,
     and without a containing block here it is laid out against the page
     instead — out past the right edge of a table that scrolls, widening
     the page it was supposed to be kept out of. */
  .table-wrap { position: relative; overflow-x: auto; }

  @media (max-width: 44rem) {
    .table.is-stacked thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
    .table.is-stacked, .table.is-stacked tbody, .table.is-stacked tfoot { display: block; }
    .table.is-stacked tr {
      display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: var(--s-3);
      padding: var(--s-3) 0; border-bottom: var(--rule-w) solid var(--rule);
    }
    .table.is-stacked td, .table.is-stacked tfoot th { display: block; padding: 0; border: 0; text-align: left; }
    .table.is-stacked .is-check { grid-row: span 9; width: auto; }
    /* with no tick box in front every cell is a whole line; with one, the
       cells line up in the column beside it */
    .table.is-stacked tr:not(:has(> .is-check)) { grid-template-columns: minmax(0, 1fr); }
    .table.is-stacked tbody tr:has(> .is-check) > :not(.is-check) { grid-column: 2; }
    .table.is-stacked .is-lead { font-weight: var(--w-strong); padding-bottom: var(--s-1); }
    .table.is-stacked td[data-label] { display: grid; grid-template-columns: minmax(0, min(7.5rem, 42%)) minmax(0, 1fr); gap: var(--s-3); overflow-wrap: anywhere; }
    .table.is-stacked td[data-label]::before { content: attr(data-label); color: var(--muted); font-size: var(--t-sm); }
    .table.is-stacked tfoot tr { border-top: var(--field-w) solid var(--rule-strong); border-bottom: 0; }
    .table.is-stacked tfoot td.is-check { display: none; }
    .table.is-stacked tfoot .is-lead { grid-column: 1 / -1; }
    .table.is-stacked tfoot td[data-label] { grid-column: 1 / -1; }
  }

  /* bulk: what to do with the ticked rows, under the thumb */
  .bulk-bar {
    position: sticky; bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); z-index: 10;
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
    margin: var(--s-4) calc(-1 * var(--gutter)) 0; padding: var(--s-3) var(--gutter);
    background: var(--paper); border-top: var(--rule-w) solid var(--rule);
  }
  .bulk-count { flex: 1 1 100%; color: var(--muted); font-size: var(--t-sm); }
  .bulk-bar .btn { flex: 1 1 0; min-width: 0; }
  @media (min-width: 60rem) {
    .bulk-bar { bottom: 0; }
    .bulk-count { flex: 1 1 auto; }
    .bulk-bar .btn { flex: none; }
  }

  /* camera: what the lens sees, with the part that counts marked out.
     Held up to a label in a storeroom, so the frame keeps its shape before
     the camera starts and the surround is dimmed rather than cropped —
     what is outside the mark still helps somebody aim. */
  .lens {
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: var(--radius-control);
    background: var(--sunk);
  }
  .lens video, .lens img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .lens-mark {
    position: absolute;
    inset: 18% 12%;
    border: 2px solid var(--accent);
    border-radius: var(--radius-control);
    box-shadow: 0 0 0 100vmax rgb(0 0 0 / 25%);
  }

  /* narrow: the box the script puts above a long set of choices */
  .input.is-narrow { margin-bottom: var(--s-2); }

  /* photos: what a thing looks like, when a list of filenames will not do.
     A grid because pictures are compared side by side, not read down; the
     tile is the link, and what it is drawn on shows through while it
     loads. The one that stands for the thing says so in a word — an
     outline alone is not a state. */
  .photos {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
    gap: var(--s-3);
    margin: var(--s-4) 0;
    padding: 0;
    list-style: none;
  }
  .photos > * { margin: 0; }
  .photos a {
    display: block;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-control);
    overflow: hidden;
    background: var(--sunk);
  }
  .photos img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .photos figcaption {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-1) var(--s-2);
    margin-top: var(--s-1);
    color: var(--muted);
    font-size: var(--t-sm);
  }
  /* A picture that is not a photograph — a PDF, a manual — keeps the same
     tile so the grid stays a grid, with the word doing the telling. */
  .photos .instead {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--muted);
    font-size: var(--t-sm);
  }

  /* pages: previous and next at the ends, where a thumb finds them */
  .pages { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-1) var(--s-3); margin-top: var(--s-5); }
  .pages > :last-child { margin-left: auto; }
  .page-step {
    display: inline-flex; align-items: center; gap: var(--s-1); min-height: var(--tap); padding: 0 var(--s-3);
    border-radius: var(--radius-control); color: var(--accent); font-weight: var(--w-strong); text-decoration: none;
  }
  a.page-step:hover { background: var(--sunk); }
  .page-step.is-off { color: var(--rule-strong); }
  .page-list { display: none; list-style: none; padding: 0; margin: 0; gap: 2px; }
  .page-list a {
    display: grid; place-items: center; min-width: var(--tap); min-height: var(--tap);
    border-radius: var(--radius-control); color: var(--ink); text-decoration: none; font-weight: var(--w-strong);
  }
  .page-list a:hover { background: var(--sunk); }
  .page-list a[aria-current] { background: var(--ink); color: var(--paper); }
  .page-gap { display: grid; place-items: center; min-width: 2rem; color: var(--muted); }
  .page-where { flex: 1 1 auto; text-align: center; color: var(--muted); font-size: var(--t-sm); }
  @media (min-width: 60rem) {
    .page-list { display: flex; }
    .page-where { display: none; }
  }

  /* action dock: the page's main action, above the tab bar, under the thumb */
  .dock {
    position: sticky; bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    z-index: 10; display: flex; gap: var(--s-2);
    margin: var(--s-6) calc(-1 * var(--gutter)) 0; padding: var(--s-3) var(--gutter);
    background: var(--paper); border-top: var(--rule-w) solid var(--rule);
  }
  .dock > form { display: flex; }
  .dock > .grow { flex: 1; }
  .dock .btn { flex: 1 1 0; min-width: 0; }
  @media (min-width: 60rem) {
    .dock { position: static; margin: var(--s-6) 0 0; padding: 0; border: 0; background: none; }
    .dock > .grow, .dock .btn { flex: none; }
  }

  /* sheet: a native dialog rising from the bottom on a phone */
  .sheet {
    width: 100%; max-width: 32rem; max-height: 88dvh;
    margin: auto auto 0; padding: var(--s-2) var(--gutter) calc(var(--s-5) + env(safe-area-inset-bottom));
    border: 0; border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    background: var(--surface); color: var(--ink); box-shadow: var(--lift);
  }
  /* rise in and fall out; browsers without @starting-style just appear */
  .sheet {
    transition: opacity var(--settle), translate var(--settle) cubic-bezier(.2, .8, .2, 1),
      overlay var(--settle) allow-discrete, display var(--settle) allow-discrete;
  }
  .sheet:not([open]) { opacity: 0; translate: 0 2rem; }
  .sheet::backdrop {
    background: transparent;
    transition: background-color var(--settle), overlay var(--settle) allow-discrete, display var(--settle) allow-discrete;
  }
  .sheet[open]::backdrop { background: var(--scrim); }
  @starting-style {
    .sheet[open] { opacity: 0; translate: 0 2rem; }
    .sheet[open]::backdrop { background: transparent; }
  }
  /* the grip: a thumb's worth of target around four visible pixels, and the
     sheet follows the thumb while it is held */
  .sheet-grip {
    position: relative; width: 2.5rem; height: 4px; margin: var(--s-1) auto var(--s-3);
    border-radius: 2px; background: var(--rule-strong); cursor: grab; touch-action: none;
  }
  .sheet-grip::before { content: ""; position: absolute; inset: -.75rem -2rem; }
  .sheet-grip:active { cursor: grabbing; }
  .sheet.is-pulled { transition: none; user-select: none; }
  .sheet.is-pulled .sheet-grip { cursor: grabbing; }
  .sheet-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s-2); touch-action: none; }
  .sheet-head h2 { font-size: var(--t-lg); }
  /* logging out sits apart from the places to go, up by the title */
  .sheet-head .logout { margin-left: auto; margin-right: var(--s-2); }
  .logout .linkish { display: inline-flex; align-items: center; gap: var(--s-2); min-height: var(--tap); }
  .logout .icon { width: 1.1rem; height: 1.1rem; }
  .sheet .list a, .sheet-page .list a {
    display: flex; align-items: center; gap: var(--s-3); min-height: 3.25rem;
    color: var(--ink); text-decoration: none; font-weight: var(--w-medium);
    border-bottom: var(--rule-w) solid var(--rule);
  }
  .sheet .list a .count, .sheet-page .list a .count { margin-left: auto; }
  .menu .list a > .icon:last-child { margin-left: auto; width: 1rem; height: 1rem; }
  .menu .list a .count + .icon:last-child { margin-left: var(--s-2); }
  .menu-group {
    margin: var(--s-5) 0 var(--s-1); color: var(--muted);
    font-size: var(--t-sm); font-weight: var(--w-strong); letter-spacing: 0;
  }
  .menu-section:first-child .menu-group { margin-top: var(--s-2); }
  .menu-find { margin-bottom: var(--s-2); }
  .rail .menu-group { margin: var(--s-4) var(--s-3) var(--s-1); }
  .sheet .list a .icon, .sheet-page .list a .icon { color: var(--muted); }
  @media (min-width: 60rem) {
    .sheet { margin: auto; border-radius: var(--radius-sheet); padding-bottom: var(--s-5); }
    .sheet-grip { display: none; }
  }

  /* toasts: what just happened, bottom of the screen, gone by itself */
  .toasts {
    position: fixed; z-index: 40; left: var(--gutter); right: var(--gutter);
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--s-3));
    display: grid; gap: var(--s-2); justify-items: center; pointer-events: none;
  }
  .toast {
    display: flex; align-items: center; gap: var(--s-3); max-width: 28rem; width: 100%;
    padding: var(--s-2) var(--s-2) var(--s-2) var(--s-4); border-radius: var(--radius-control);
    background: var(--ink); color: var(--paper); box-shadow: var(--lift);
    font-weight: var(--w-medium); pointer-events: auto;
    transition: opacity var(--settle), translate var(--settle);
  }
  .toast .icon-btn { color: inherit; margin-left: auto; width: 2.5rem; height: 2.5rem; }
  .toast .icon-btn:hover { background: color-mix(in srgb, var(--paper) 15%, transparent); }
  .toast > .icon { flex: none; }
  .toast.is-error { background: var(--danger); color: var(--on-danger); }
  .toast.is-warn { background: var(--late); color: var(--paper); }
  .toast-action {
    flex: none; min-height: 2.5rem; padding: 0 var(--s-3); margin-left: auto;
    border: 0; border-radius: var(--radius-control); background: none;
    color: var(--marker); font-weight: var(--w-heavy);
  }
  .toast-action:hover { background: color-mix(in srgb, var(--paper) 15%, transparent); }
  .toast-action + .icon-btn { margin-left: 0; }
  .toast.is-leaving { opacity: 0; translate: 0 .75rem; }
  @media (min-width: 60rem) { .toasts { left: auto; right: var(--s-6); bottom: var(--s-6); justify-items: end; } }

  /* offline: the page is a saved copy */
  .offline { padding: var(--s-2) var(--gutter); background: var(--marker); color: var(--on-marker); font-weight: var(--w-strong); font-size: var(--t-sm); }

  /* two columns on a desk for detail pages; one on a phone */
  @media (min-width: 60rem) {
    .split { display: grid; grid-template-columns: minmax(0, 1fr) 19rem; gap: var(--s-7); align-items: start; }
    .split > aside { position: sticky; top: var(--s-6); grid-column: 2; grid-row: 1; }
    .split > :not(aside) > .section:first-child { margin-top: 0; }
  }
}

/* ------------------------------------------------------------ utilities */

@layer mica.utilities {
  .visually-hidden {
    position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .js .js-hide { display: none; }
  html:not(.js) .js-only { display: none; }
  .stack > * + * { margin-top: var(--s-3); }
  /* Grid children may shrink below their content, so a wide thing inside
     wraps or scrolls on its own instead of widening the page. */
  .section > *, .form > *, .split > *, .field > *, .groups > *, .pair > *, .status > * { min-width: 0; }

  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation: none !important; transition: none !important; }
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
  }
}

@layer mica.components {
  /* lines: a plain list, text left and a value right, hairline between */
  .lines li {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3);
    min-height: var(--tap); padding: var(--s-3) 0;
    border-bottom: var(--rule-w) solid var(--rule);
  }
  .lines.is-quiet li > :last-child { color: var(--muted); }
  .plain { list-style: none; padding: 0; display: grid; gap: var(--s-1); }
  .active-filters { display: flex; gap: var(--s-2); margin-top: var(--s-3); }
  .active-filters .tag { font-size: var(--t-sm); padding: .25rem .5rem; text-decoration: none; }
  .active-filters .icon { width: 1rem; height: 1rem; }
  .grow { flex: 1; }
}
