/* SPDX-License-Identifier: GPL-3.0-or-later
 * Copyright (C) 2026 Óscar García Amor <ogarcia@connectical.com>
 *
 * No framework. A panel needs a layout, a table, a form, a button and a badge,
 * and that is what is here — rather than eighty kilobytes of somebody else's
 * widgets to update by hand without npm to warn us.
 *
 * Both schemes come from `color-scheme` and one set of variables, so the browser
 * picks and nothing has to be toggled.
 *
 * Every measure is in `rem` and every line is in pixels, and a test holds both to
 * it. A measure is anything that decides how much room something takes, and all
 * of it moves with the size the reader asked their browser for: somebody who set
 * that to twenty because they cannot read fifteen gets a whole panel a quarter
 * larger, sidebar and glyphs included, rather than large text crammed into
 * furniture that stayed put. A line is not a measure — a hairline is one physical
 * pixel and has to stay one, since in `rem` it would land on a fraction that
 * browsers round differently from one edge to the next, and this whole look is
 * that its lines are all the same. Corners and shadows go with the lines.
 *
 * Every grid that fits as many columns as it can is written
 * `repeat(auto-fit, minmax(min(Xrem, 100%), 1fr))` and never with a bare `Xrem`.
 * A bare one is a floor the column keeps however narrow the screen gets: at 28rem
 * the last remaining column stays 448 pixels wide on a phone that has 350, and
 * whatever lives at its right edge — a button, a figure, the end of a name — is
 * off the side of the screen. The `min` lets the floor give way exactly when it
 * was doing harm and changes nothing at any width where it was not. Four screens
 * were overflowing on a phone this way before it was written down here. */

:root {
  color-scheme: light dark;

  --ink: light-dark(#1c1c1e, #ededee);
  --quiet: light-dark(#77777f, #8f8f98);
  --paper: light-dark(#fcfcfb, #101012);
  --line: light-dark(#e6e5e2, #26262a);
  --alarm: light-dark(#b3261e, #ff8a80);

  /* Two shades per accent, because one will not do: what reads on white is too
   * dark on black. `light-dark()` picks, the same way every other colour here is
   * picked, so there is no second set of rules for the dark panel. */
  --accent-blue: light-dark(#1f5eaa, #7fb0ec);
  --accent-teal: light-dark(#00706b, #4dc4bc);
  --accent-green: light-dark(#2c6e56, #7fd0ae);
  --accent-amber: light-dark(#a15c00, #e8a33d);
  --accent-crimson: light-dark(#c2185b, #f07fa8);
  --accent-plum: light-dark(#6d28d9, #b79cf0);

  /* What the panel is without anybody having chosen. `accent::DEFAULT` in the
   * panel's own source names the same colour, and it has to: choosing that one
   * takes the attribute off the root and leaves this line to answer. The two
   * drifting apart is one colour that cannot be picked and another whose swatch
   * does nothing. */
  --accent: var(--accent-green);

  --side: 15rem;

  /* The scale, by what each size is for rather than by how big it is. Five,
   * because five is what the panel turned out to use for everything that is not
   * a heading or a figure — and a size used once is written where it is used,
   * since a name for it would only be a second place to look.
   *
   * In `rem`, like every measure in this sheet except the rules themselves. See
   * the note over `font-size` below for what that is answering. */
  --lettering: 0.75rem;   /* 12px — small capitals over a run of rows */
  --aside: 0.8125rem;     /* 13px — the quiet line under almost everything */
  --control: 0.875rem;    /* 14px — buttons, rows, menu items */
  --text: 0.9375rem;      /* 15px — the panel's own size */
  --name: 1rem;           /* 16px — what a row is called, and what is typed into */

  font-family: system-ui, sans-serif;
}

/* There is no `--raised`, and that is the whole of the look: nothing here is a
 * card. What used to be a panel on a lighter background is now the page itself,
 * divided by a 1px line where a division is meant and by whitespace where it is
 * not. Two weights of border exist — 1px for every rule, 2px for the two things
 * that measure rather than divide: the mark on the section you are in, and the
 * track under a figure.
 *
 * The scale is a typographic one — 12, 13, 14, 15, 16, 17, 20, 26, 30, 34 — and it
 * is written in `rem` against the browser's own sixteen, which is what keeps each
 * step a round fraction of it: 12 is 0.75rem, 20 is 1.25rem. The five sizes the
 * panel reaches for constantly are named above; the rest are written where they
 * are used. */

* { box-sizing: border-box; }

/* Nothing here leans on the margins a browser gives a paragraph or a heading.
 * Every gap is either a `gap` on the container or a margin this file asks for,
 * because a default margin on the last thing in a box is what leaves a dialogue
 * with more air under its buttons than over its title — and it does that in
 * every box, not just the one you happen to be looking at. */
p, h1, h2, dl, dd { margin: 0; }

/* The size the panel is set at, and deliberately not on `:root`.
 *
 * A `font-size` in pixels on the root is what makes every `rem` under it a pixel
 * measure wearing another unit: the root stops following the size the reader
 * asked their browser for, and somebody who set it to twenty because they cannot
 * read fifteen goes on being shown fifteen. Left alone, the root is whatever they
 * chose, `rem` scales with it, and this line only says that the panel sits one
 * notch under it — the same fifteen pixels as before for a browser at its
 * default, and eighteen and three quarters for one at twenty.
 *
 * Which is also why the root stays at the browser's sixteen rather than being set
 * to fifteen some other way: it keeps every measure in the sheet a round fraction
 * of it. Eight pixels is half a rem, twenty-four is one and a half, the sidebar's
 * two hundred and forty is fifteen. */

body {
  margin: 0;
  color: var(--ink);
  background: var(--paper);
  font-size: var(--text);
}

/* Which accent, on whatever carries the attribute. Not written as `:root[...]`,
 * so the same rules colour a swatch on the preferences screen: the button showing
 * plum redefines the variable for itself and is plum whatever the panel is set to.
 *
 * The default is the absence of the attribute rather than a value of its own,
 * which is what makes choosing it the same as never having chosen. Anything
 * unrecognised matches nothing here and inherits the default, so a colour written
 * by a newer panel is harmless rather than colourless. */
[data-accent="blue"] { --accent: var(--accent-blue); }
[data-accent="teal"] { --accent: var(--accent-teal); }
[data-accent="green"] { --accent: var(--accent-green); }
[data-accent="amber"] { --accent: var(--accent-amber); }
[data-accent="crimson"] { --accent: var(--accent-crimson); }
[data-accent="plum"] { --accent: var(--accent-plum); }

/* Nothing here is underlined. A link in this panel is a word in the accent, and
 * every place that wants one had been turning the underline off for itself — the
 * popover, the rail, the row back to a list — which is three rules saying what one
 * says. What underlines is a name in a list, on hover, where the underline is the
 * answer to "is this clickable". */
a {
  color: var(--accent);
  text-decoration: none;
}

/* --- the shared words ---------------------------------------------------- */

/* Some classes below are worn by several screens on purpose. Which ones is not a
 * matter of taste, and it is not left to be inferred: the list is `SHARED` in
 * `main.rs`, and a test holds the sheet to it.
 *
 *   A bare class rule — `.foo { … }` — is allowed only for a word on that list.
 *   Everything else is styled through whatever owns it: `.shelf .actions`.
 *
 * The list exists because two names collided in one afternoon and nothing could tell.
 * The Overview has said `class="reading"` for the processor figure since it was
 * written, styled through `.gauge .reading` because that is the only place the word
 * means a reading; a panel elsewhere took a bare `.reading` for its scrolling body,
 * and the Overview grew a horizontal scrollbar. `.whence` went the same way, and
 * `.acting` and `.named` had been leaking for far longer — the first of them putting
 * `margin-top` on a play glyph pinned with `inset: 0`, which is why that glyph sat
 * below the centre of its own box.
 *
 * Two tests were tried before this and both were wrong the same way. "A rule that lays
 * things out belongs to one screen" flagged twenty-four names, nearly all deliberate:
 * `.titled`, `.pill`, `.finding` and `.checkbox` are one shape wherever they appear.
 * "A scoped name does not get a bare rule later" flagged twenty more, because this
 * sheet is ordered by screen rather than base-then-variant, so `.brand .icon` sits
 * hundreds of lines above the bare `.icon` it refines. Nothing mechanical can tell
 * shared from collided while the sharing is unwritten. So it is written.
 *
 * What it asks of anybody adding a class is one decision, once: is this a word the
 * whole panel shares, or one screen's own? That decision was what nobody was making.
 *
 * Roughly, the words fall into three groups. How something reads wherever it is —
 * `quiet`, `lead`, `part`, `note`, `hint`, `lettering`, `path`, `failure`. Furniture
 * every screen has — `titled`, `nothing`, `finding`, `facts`, `figures`,
 * `counts`, `count`, `named-figure`, `bled`, `two`, `rail`, `settings`, `saving`,
 * `after`, `instead`, `option`, `options`. And things that are the same thing wherever
 * they appear — `pill`, `link`, `checkbox`, `icon`, `art`, `avatar`, `badge`, `menu`,
 * `menu-item`, `dropdown`, `veil`, `scrim`, and the `sheet` family. */

/* --- the page header ----------------------------------------------------- */

/* The title and what it is, with the one action that belongs to the screen as a
 * whole against the right. Aligned to the top rather than the middle: the action
 * belongs beside the title, not beside the two lines' average. */
.titled {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
  margin-bottom: 2.5rem;
}

/* The gap is the first thing to give on a narrow screen: what is on either side of
 * it is a title and an action, and neither has any width to spare. */
@media (max-width: 45rem) {
  .titled {
    gap: 0.75rem;
    margin-bottom: 1.75rem;
  }
}

h1 {
  margin: 0 0 0.375rem;
  font-size: 1.875rem;
  font-weight: 400;
  letter-spacing: -0.02em;
}

.titled .lead { margin: 0; }

/* A section with a place in the panel and nothing in it yet. The band is what keeps
 * it from reading as a screen that failed to load: an empty screen says nothing,
 * and an empty screen with a line above and below it says the emptiness is on
 * purpose. */
.nothing {
  padding: 2.5rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  color: var(--quiet);
  font-size: var(--control);
  text-align: center;
}

/* --- facts ---------------------------------------------------------------- */

/* A name against a value, with a hairline under each. The third place this appeared
 * it had drifted: twenty pixels of gap on one screen and sixteen on another, eleven
 * of padding here and nine there, and the same four declarations written out three
 * times. One shape, and what each place changes about it is beside it.
 *
 * `dt`/`dd` or a `li` with two children — the element says which it is, so a list of
 * pairs and a list of events need no class to tell them apart. */
.facts {
  margin: 0;
  padding: 0;
  list-style: none;
}

.facts > * {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--line);
}

.facts dt { color: var(--quiet); }

.facts dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* A value that opens something is still a value.
 *
 * `.link` is sized and shaped for where it usually appears — the quiet line under a
 * control — so it arrives here at 13px in a column of 15px, and its `font: inherit`
 * drops the tabular figures every other figure in this list is set in. Read down the
 * column that is one row in a different typeface, which is the one thing a row of
 * facts must not be. Both are given back; what it does to the colour is the part
 * that says it opens. */
.facts .link {
  font-size: inherit;
  font-variant-numeric: tabular-nums;
}

/* An event and when it happened, which is the other way round: what is being read is
 * the list of events, and the times are what each one turns out to have been. */
.facts li > :last-child {
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --- the icons ----------------------------------------------------------- */

/* Sized in ems so an icon grows with whatever text it sits beside, and squared
 * so a row of them lines up whatever each one draws. Nothing sets a colour:
 * every one strokes with currentColor and takes the colour of its label, which
 * is what makes a section that lights up light its icon too. */
.icon {
  flex: 0 0 auto;
  display: inline-flex;
  width: 1.15em;
  height: 1.15em;
}

.icon svg { width: 100%; height: 100%; }

/* --- the frame ----------------------------------------------------------- */

/* Two columns and nothing across the top. Everything that is permanently there —
 * the name, where you can go, a scan while it runs, who you are — lives in the
 * left column, and the right column is only ever the screen you asked for. The
 * top bar this replaces held three things that each belong somewhere in that
 * column, and holding them cost a strip of every screen. */
.shell {
  display: grid;
  /* minmax(0, 1fr) and not 1fr: a plain 1fr will not shrink below the width of
   * what is inside it, so anything wide — a table, a long path — stretches the
   * column and takes the whole page sideways with it. The overflow rule on the
   * box around the table can only work if the column is allowed to be narrower
   * than its contents. */
  grid-template-columns: var(--side) minmax(0, 1fr);
  height: 100vh;
}

/* One line divides the columns, and both sit on the same paper. `min-height: 0`
 * so the navigation in the middle is what scrolls when there is too much of it,
 * rather than the whole column growing and pushing the account block off the
 * bottom of the window. */
.side {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 1.625rem 0 0;
  border-right: 1px solid var(--line);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  padding: 0 1.625rem 1.625rem;
  font-size: var(--name);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: inherit;
  flex: none;
}

/* The one spot of colour in the column that is not a state. */
.brand .icon { width: 1.1875rem; height: 1.1875rem; color: var(--accent); }

.side nav {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

/* A heading over a run of entries, at the width of the column so it lines up with
 * them. The first one needs no room above it: the wordmark already left some. */
.nav-title {
  margin: 1.375rem 0 0.375rem;
  padding: 0 1.625rem;
  font-size: var(--lettering);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--quiet);
}

.nav-title:first-child { margin-top: 0; }

/* The whole width of the column, marked by a rule down its left edge rather than
 * by a shape inside it. Quiet until it is where you are, which is the only state
 * an entry has.
 *
 * The border is declared in full here and only its colour is changed below. The
 * other way round — shorthand in one rule, `border-left-color` in another —
 * leaves whichever rule loses the cascade painting a grey bar down entries you
 * have merely visited. */
.side nav a {
  display: block;
  padding: 0.4375rem 1.625rem;
  border-left: 2px solid transparent;
  color: var(--quiet);
  font-size: var(--control);
}

.side nav a:hover { color: var(--ink); }

/* `aria-current` and not a class: that is what the router sets, and the class
 * this waited for never existed, so nothing was ever marked. Which is also why
 * the attribute is the better hook — it cannot fall out of step with the router
 * without the page being wrong for a screen reader too. */
.side nav a[aria-current="page"] {
  border-left-color: var(--accent);
  color: var(--ink);
}

/* Everything permanent that is not navigation, stacked against the bottom of the
 * column and divided from what is above it. Each block draws its own top line, so
 * the stack reads the same however many of them are there — and a scan strip that
 * comes and goes does not take a border with it that belonged to its neighbour. */
.side > .foot {
  margin-top: auto;
  flex: none;
}

.foot > * { border-top: 1px solid var(--line); }

/* Turns while the scan turns. Anybody who asked not to be moved gets a still
 * icon and the same information. */
.working .icon { animation: turn 1.4s linear infinite; }

@keyframes turn {
  to { transform: rotate(1turn); }
}

@media (prefers-reduced-motion: reduce) {
  .working .icon { animation: none; }
}

.body {
  min-width: 0;
  overflow-y: auto;
  padding: 2.75rem 3.5rem 3.75rem;
}

/* Nothing is capped. A window is as wide as somebody made it, and a screen of
 * figures that stops at a fixed measure leaves the rest of a wide one empty while
 * the same tables scroll sideways on a narrow one. What holds a line of prose to a
 * readable length is the column it is in, not a ceiling over the whole screen. */

/* --- the folding menu ---------------------------------------------------- */

/* None of these exists on a wide screen: the column is simply there, and there is
 * nothing across the top to hold a way of opening it. */
.bar, .menu-shade { display: none; }

@media (max-width: 45rem) {
  .shell {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 3.5rem minmax(0, 1fr);
  }

  /* The way to the sections and the name of the thing. Nothing else: what used to
   * sit on the right of a header is at the bottom of the column this opens, which
   * is one place for it rather than two. */
  .bar {
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--line);
  }

  .bar .brand { padding: 0 1rem 0 0.25rem; }

  .side {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--side);
    z-index: 2;
    background: var(--paper);
    transform: translateX(-100%);
    transition: transform 0.15s ease-out;
  }

  .side.out { transform: none; }

  .menu-shade {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 1;
    background: rgb(0 0 0 / 35%);
  }

  /* 44px square, because a finger is not a pointer. */
  .menu-button {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
    color: inherit;
  }

  /* Who you are is up in the bar at this width, and the bar is where the way out
   * is with it. Left in the column it would be the same block twice on one screen
   * — once behind a button, once in the corner — and the one behind the button is
   * the one that costs a tap to reach. */
  .side .dropdown { display: none; }

  .body { padding: 1.125rem; }

  h1 { font-size: 1.625rem; }
}

/* --- who you are, at the foot of the column ------------------------------ */

/* The whole block opens the menu, so the target is the row rather than the small
 * round thing at its left end. */
.whoami {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  margin: 0;
  padding: 1rem 1.625rem;
  border: none;
  border-top: 1px solid var(--line);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.whoami .avatar {
  width: 1.75rem;
  height: 1.75rem;
}

/* Two lines that must not push what follows off the end, whatever the name is: the
 * chevron in the sidebar, the badge and the columns in a row of the accounts. */
.whoami .who, .roster .who, .mine-who .who {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
}

.whoami .who > *, .roster .who > *, .mine-who .who > * {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.whoami .role, .mine-who .role { font-size: var(--lettering); color: var(--quiet); }

/* Pointing along the row rather than down: what it opens comes up from here. */
.whoami .chevron {
  margin-left: auto;
  color: var(--quiet);
  transform: rotate(-90deg);
}

/* Upward, because there is no room below: this is the last thing in the column.
 * Inset from both edges rather than hung off one, so it reads as belonging to the
 * block underneath it rather than to the window's edge — and no minimum width, since
 * what it has to fit inside is a 240px column. */
.mine {
  position: absolute;
  left: 0.875rem;
  right: 0.875rem;
  bottom: calc(100% + 0.5rem);
  z-index: 3;
}

/* The same block in the corner of the bar, which is the other place it is: the disc
 * on its own, in a square a finger can hit, and the name and the role left out
 * because there is a wordmark beside them and no room for both.
 *
 * The bar is always there and the column is not, so what was a tap into the sections
 * and a tap back out of them is now a corner nothing else was using — and the auto
 * margin is what claims it, the bar being a row with the way to the sections and the
 * wordmark at the other end of it. The margin on the other side is what keeps the
 * disc off the edge of the screen: the square around it is bigger than it is, so
 * without it the two look the same distance from the edge and the disc is not. */
.cornered { margin: 0 0.5rem 0 auto; }

.cornered .whoami {
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border-top: none;
}

.cornered .whoami .who, .cornered .whoami .chevron { display: none; }

/* Filled while it is open, which is the whole of what the corner has to say: there
 * is no chevron up here to turn, and the menu it opens is over the screen rather
 * than beside the disc. */
.cornered .whoami[aria-expanded="true"] .avatar {
  background: var(--accent);
  color: light-dark(#fff, #0c1a14);
}

/* What the corner opens is a sheet up from the bottom edge of the screen rather than
 * a box hanging off the disc that opened it.
 *
 * The disc is in the top right corner, which on a phone is the far corner: under the
 * hand holding it and above every finger that could reach it. So what it opens comes
 * up from the near edge instead, where the panel already puts a short list on a
 * phone — the dialogues arrive from this edge at this width, with these corners and
 * this shadow, and this is the same thing asked of the same screen. */
.cornered .mine {
  position: fixed;
  inset: auto 0 0;
  z-index: 9;
  padding: 0;
  border: none;
  border-top: 1px solid var(--line);
  border-radius: 14px 14px 0 0;
  box-shadow: 0 -14px 40px light-dark(rgb(0 0 0 / 14%), rgb(0 0 0 / 45%));
}

/* Whose account it is, before what can be done to it. The disc is larger here than
 * the one that opened the sheet, because up in the bar it is a landmark and here it
 * is the subject, and the two lines beside it are the pair the column carries. */
.mine-who {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0;
  padding: 1.125rem 1.25rem 1rem;
  border-bottom: 1px solid var(--line);
}

.mine-who .avatar {
  width: 2.5rem;
  height: 2.5rem;
  font-size: var(--control);
}

/* Rows a thumb can hit, divided rather than spaced. Three links in a box at the foot
 * of a column are a menu and the rounded highlight on each of them is what says
 * which one the pointer is on; across the whole width of a phone the same three are
 * a list, and a list is a thing with lines in it. */
.cornered .mine a, .cornered .mine .menu-item {
  min-height: 3.25rem;
  padding: 0.875rem 1.25rem;
  border-radius: 0;
  border-bottom: 1px solid var(--line);
}

/* The way out is the last row and keeps no line under it: the sheet's own edge is
 * there, and under that the strip a phone keeps for itself. The space is what sets
 * leaving apart from going somewhere, which in the column is a rule across the box. */
.cornered .mine .leave {
  padding-bottom: 1.75rem;
  border-bottom: none;
}

/* The one entry that is not a place to go. Its colour has to survive the rule
 * above, which sets `inherit` for everything else in here. */
.mine .leave { color: var(--alarm); }

/* Between what you can go and see and what acts: the one thing that ends the
 * session in the sidebar, and cutting somebody off in a row of the accounts. */
.mine hr, .menu hr {
  height: 0;
  margin: 0.375rem 0.25rem;
  border: none;
  border-top: 1px solid var(--line);
}

/* --- what belongs to you ------------------------------------------------- */

.dropdown { position: relative; }

/* The one action that belongs to a whole screen, beside its title. Outlined and
 * transparent rather than filled: it is the way to start the thing this screen is
 * about, not a decision to be talked into.
 *
 * Rounded all the way, which is the one place a pill shape appears outside a badge.
 * It says "an action" where a rectangle here would say "a field". */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
  margin: 0;
  padding: 0.5rem 0.9375rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font-size: var(--control);
  /* One line, whatever the window. A pill is a shape with a word in it, and a
   * shape with a word broken across two lines is not that shape any more: it grows
   * downwards, its round ends stop matching its height, and it stops looking like
   * something to press. What gives instead is the title beside it, which is prose
   * and wraps for a living. */
  white-space: nowrap;
}

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

/* The same button while the scan it started is running: the glyph turns and the pill
 * takes the accent, which is what everything live in this panel is coloured. It is
 * not a state of the pill so much as the pill being the scan for a while. */
.pill.scanning {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  color: var(--accent);
}

.pill.scanning .icon { animation: turn 1.4s linear infinite; }

@media (prefers-reduced-motion: reduce) {
  .pill.scanning .icon { animation: none; }
}

/* Both labels in one cell, so the pill is as wide as the longer of them and holds
 * still while somebody aims at it. Hidden rather than removed, for the same reason:
 * a label taken out of the flow would take its width with it — and `visibility`
 * hides it from whatever reads the page aloud as well, so only one is announced. */
.pill.scanning .swap { display: grid; }

.pill.scanning .swap > * { grid-area: 1 / 1; }

.pill.scanning .undo { visibility: hidden; }

/* Under the pointer, or under focus: a keyboard cannot hover, and a button whose
 * meaning is only revealed by hovering has no meaning for one. What it turns is the
 * alarm colour, because what it does now is stop something. */
.pill.scanning:hover, .pill.scanning:focus-visible {
  border-color: color-mix(in srgb, var(--alarm) 45%, transparent);
  color: var(--alarm);
}

.pill.scanning:hover .doing, .pill.scanning:focus-visible .doing { visibility: hidden; }

.pill.scanning:hover .undo, .pill.scanning:focus-visible .undo { visibility: visible; }

/* The same shape filled, for the one action a screen exists to offer: adding what
 * the screen is a list of. Outlined says "you may"; filled says "this is the one".
 *
 * The ink on it flips with the scheme, because the accent does: the dark palette's
 * accents are pale on purpose, and white on a pale green is white on nothing. */
.pill.solid {
  border-color: transparent;
  background: var(--accent);
  color: light-dark(#fff, #0c1a14);
  font-weight: 500;
}

.pill.solid:hover { color: light-dark(#fff, #0c1a14); }

.pill.solid:disabled { opacity: 0.45; cursor: default; }

/* The one that cannot be undone. Red is spent on this and on nothing else in the
 * same box. */
.pill.undoing {
  border-color: transparent;
  background: var(--alarm);
  color: #fff;
}

.pill.undoing:hover { color: #fff; }

/* Outlined in the alarm colour rather than filled with it: the way into something
 * that cannot be undone, which is not the same as doing it. The same treatment the
 * scan strip gives its own way out. */
.pill.risky {
  border-color: color-mix(in srgb, var(--alarm) 45%, transparent);
  color: var(--alarm);
}

.pill.risky:hover { border-color: var(--alarm); color: var(--alarm); }

/* An action written as a word. Not a link — it goes nowhere, so it is a button
 * wearing the clothes of one — and not a bordered button either, which is three
 * boxes in a row where the words fit in the space the boxes would have taken.
 *
 * Quiet until it is pointed at, so a row of them does not compete with the name of
 * the thing they act on. */
.link {
  margin: 0;
  padding: 0;
  border: none;
  color: var(--quiet);
  font: inherit;
  font-size: var(--aside);
  cursor: pointer;
}

.link:hover { color: var(--ink); }

/* The one that cannot be undone reads like its neighbours and turns its own colour
 * rather than growing a line under itself. Quiet at rest for the same reason they
 * are: three words in a row, one of them permanently red, is a row with something
 * shouting in it. What it does is said by the word, and asked again by the dialogue
 * before anything happens.
 *
 * Not `.link.alarm`. There is an `.alarm` of its own further down this file, for
 * text that is a warning; it is one class, like `.link` is, and it comes later — so
 * it won the resting colour, the word sat permanently red, and the hover had nothing
 * left to change. */
.link.risky:hover { color: var(--alarm); }

.link:disabled { opacity: 0.5; }

.pill .icon { width: 1rem; height: 1rem; }

/* Along the row, and down once the menu under it is open: the direction the thing
 * it opens comes from. */
.pill .chevron {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--quiet);
  transition: transform 0.15s ease-out;
}

.pill[aria-expanded="true"] .chevron { transform: rotate(90deg); }

@media (prefers-reduced-motion: reduce) {
  .pill .chevron { transition: none; }
}

/* An account as a circle with its initial in it.
 *
 * Tinted with the accent for somebody who administers the server and with plain ink
 * for somebody who does not. It is how the panel says which you are without
 * spelling it out — in the sidebar, and down the accounts — and it says it in the
 * same colour as everything else that is a state. */
.avatar {
  flex: none;
  width: 1.875rem;
  height: 1.875rem;
  padding: 0;
  margin: 0;
  border-radius: 50%;
  border: none;
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: var(--accent);
  font-size: var(--lettering);
  font-weight: 600;
  line-height: 1;
  display: grid;
  place-items: center;
}

.avatar.plain {
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  color: var(--quiet);
}

/* Invisible, and only there while the menu is. It sits under the menu and over
 * the page, so it catches every click except the ones meant for the menu itself. */
.veil {
  position: fixed;
  inset: 0;
  z-index: 2;
}

/* Except under the sheet the bar opens, where it dims what it covers — the same
 * tint, and for the same reason, as the one behind a drawer: what is over the screen
 * there is the width of the screen, and a sheet that dims nothing looks like part of
 * the page it is sitting on. */
.cornered .veil {
  z-index: 8;
  background: light-dark(rgb(20 20 20 / 22%), rgb(0 0 0 / 45%));
}

/* The box a menu comes in, wherever it opens from: the dots at the end of a row, the
 * scan button, the block with your name on it. One box, because they
 * are one thing — a raised surface with a short list on it — and the popover in the
 * column was the only one that had been brought to the drawing. */
.menu, .mine {
  display: flex;
  flex-direction: column;
  padding: 0.375rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  box-shadow: 0 10px 30px light-dark(rgb(0 0 0 / 12%), rgb(0 0 0 / 35%));
}

/* Hangs off the button rather than sitting in the flow, so opening it moves
 * nothing else on the page. Wide enough for the longest entry any of them has, and
 * whatever still does not fit scrolls inside the menu rather than off the window. */
.menu {
  position: absolute;
  top: calc(100% + 0.375rem);
  right: 0;
  z-index: 3;
  min-width: 14.375rem;
  overflow-y: auto;
}

/* One rule for an entry in any of them. The popover on the block with your name on
 * it had a rule of its own with the padding and the size in it and not the colour,
 * so its three links came out accented in the middle of a menu. */
.menu a, .menu-item, .mine a {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.625rem;
  padding: 0.5rem 0.625rem;
  border-radius: 5px;
  color: inherit;
  text-align: left;
  /* In this order: `font` is shorthand and would take the size back out. */
  font: inherit;
  font-size: var(--control);
  background: transparent;
  border: none;
  margin: 0;
  cursor: pointer;
}

.menu a:hover, .menu-item:hover, .mine a:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* The second line of an entry that needs explaining. Inside the button, so the
 * whole thing highlights together and every part of it is clickable. */
.explained {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.125rem;
}

.menu-note {
  font-size: var(--lettering);
  color: var(--quiet);
}

/* Follows the entry when the entry lights up, rather than staying grey against a
 * tinted background. */
.explained:hover .menu-note { color: inherit; }

.menu-item.chosen {
  color: var(--accent);
  font-weight: 600;
}

/* Whose row this menu belongs to, over the things it can do to them. A menu that
 * opens next to a pointer says nothing about which of eight rows it came from, and
 * two of the items here cut somebody off. */
.menu-who {
  margin: 0;
  padding: 0.5rem 0.625rem 0.375rem;
  font-size: var(--lettering);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--quiet);
}

/* How many it would take away, against the right of the item that would. */
.menu-item .figure {
  margin-left: auto;
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* The one that cannot be undone, in the colour everything destructive wears. It
 * still opens a dialogue: the colour is a warning and not the guard. */
.menu-item.risky { color: var(--alarm); }

.badge {
  /* Inline flex and middle aligned: with padding and no baseline of its own, a
   * badge sits low against the text beside it. */
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: 0.5rem;
  padding: 0.125rem 0.4375rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent);
  font-size: var(--lettering);
}

/* Just the dots, and no wider than they are: it lives at the end of a row and
 * anything more would push the columns around. */
.dots {
  margin: 0;
  padding: 0.25rem;
  border: none;
  color: var(--quiet);
  line-height: 1;
}

.dots:hover { color: var(--ink); }
.dots:disabled { opacity: 0.4; }

/* --- the dialogue -------------------------------------------------------- */

/* The only raised surface in the panel, and the only border and shadow in it. It is
 * allowed them precisely because nothing else has them: a box that floats over the
 * screen has to say it is floating, and there is nothing else it could be confused
 * with.
 *
 * A real `dialog` opened with `showModal`. Escape closes it, the focus stays inside,
 * the page behind goes inert and the backdrop is the browser's own — none of which
 * is written here. */
.sheet {
  width: 27.5rem;
  max-width: 100%;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 24px 60px light-dark(rgb(0 0 0 / 18%), rgb(0 0 0 / 55%));
}

/* A sentence and two answers. Anything with a field in it wants the full width. */
.sheet.narrow { width: 23.75rem; }

/* `color-scheme` on the backdrop as well as on the page: a backdrop is not a child
 * of anything, so without it `light-dark()` here has no scheme to read. */
.sheet::backdrop {
  color-scheme: light dark;
  background: light-dark(rgb(28 28 30 / 34%), rgb(0 0 0 / 62%));
}

/* --- the picture, looked at rather than glanced at ------------------------ */

/* A sleeve at 56 pixels in the corner of a panel is a label, not a picture. This is
 * the picture.
 *
 * A `dialog` like the one above, and for the same reasons — Escape, the trapped
 * focus, the inert page behind — but with nothing of the box about it: no border,
 * no background, no padding. What is on screen is the image and the darkened page.
 *
 * As big as it goes, and never bigger than itself. `vmin` bounds it by whichever
 * edge of the screen is shorter, so it is a square that fits however the window is
 * shaped; `width: auto` is what stops a sleeve scanned at 300 pixels being blown up
 * into a blurred square on a large display. Between those two, whichever is smaller
 * wins, which is the whole rule. */
.enlarged {
  max-width: none;
  max-height: none;
  padding: 0;
  border: none;
  background: none;
  overflow: visible;
}

.enlarged img {
  display: block;
  width: auto;
  height: auto;
  max-width: 88vmin;
  max-height: 88vmin;
  cursor: zoom-out;
  /* Its own edge, because a white sleeve on a dark backdrop otherwise ends
   * nowhere in particular. */
  box-shadow: 0 24px 60px rgb(0 0 0 / 45%);
}

.enlarged::backdrop {
  color-scheme: light dark;
  background: light-dark(rgb(28 28 30 / 62%), rgb(0 0 0 / 78%));
}

/* Says the picture in a drawer's heading can be pressed. Only the frame that holds
 * one: the glyph that stands in for a missing picture leads nowhere. */
.emblem.pressable {
  cursor: zoom-in;
}

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

/* What is being asked, and what it is about. No ✕ in the corner: Escape, the
 * backdrop and the cancel action are three ways out already, and a fourth drawn in
 * the corner is the only thing in the box that is about the box. */
.sheet-body { padding: 1.625rem 1.875rem 1.375rem; }

.sheet h2 {
  margin: 0 0 0.375rem;
  font-size: 1.25rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-transform: none;
  color: inherit;
}

/* `pretty` so a two line lead does not leave one word on the second line. */
.sheet-lead {
  margin: 0;
  font-size: var(--control);
  color: var(--quiet);
  text-wrap: pretty;
}

/* What is being filled in or chosen: a column, its rows a fixed distance apart, and
 * air over the first of them so it stands off the line that says what this is for. */
.sheet-content {
  display: flex;
  flex-direction: column;
  gap: 1.375rem;
  margin-top: 1.5rem;
}

/* The form, which in every sheet that has one is wrapped around the whole dialogue —
 * body, foot and the failure under it — so that Enter does what the button does. The
 * same column with the same distance between those blocks, and no margin: it shared
 * one with the stack of fields above, and a margin on a child of the dialogue itself
 * has nowhere to go. The border around a `dialog` stops it collapsing out, so it
 * became a strip of nothing over the heading of every sheet with a field in it — seen
 * only once a sheet turned up without a form and sat a whole 1.5rem higher. */
.sheet form {
  display: flex;
  flex-direction: column;
  gap: 1.375rem;
}

/* Stacked one under another and separated by nothing but air. A field is a label, a
 * line and what the line is for.
 *
 * The entry screen is a sheet with no dialogue around it and its fields are the
 * same fields, so it is named here rather than given a second set of rules that
 * would drift from these ones. */
.sheet label, .entry label {
  display: flex;
  flex-direction: column;
  gap: 0.3125rem;
  font-size: inherit;
  color: inherit;
}

.sheet label > span:first-child, .entry label > span {
  font-size: var(--lettering);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--quiet);
}

/* A line under the value rather than a box around it. The box was drawn to say
 * "this is where you type", which is what the label above it says. */
.sheet input, .sheet select, .entry input {
  padding: 0.375rem 0;
  border: none;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  font-size: inherit;
}

.sheet input:focus-visible, .sheet select:focus-visible, .entry input:focus-visible {
  outline: none;
  border-bottom-color: var(--accent);
}

/* The label goes with it. The bottom border alone is a thin thing to notice, and
 * two things changing colour together is what makes it obvious which field has the
 * cursor. */
.sheet label:focus-within > span:first-child { color: var(--accent); }

/* A checkbox is not a value with a line under it: it is a box and the words beside
 * it. Both of the rules above have to be undone for it — the column and the line —
 * because a checkbox is a `label` with an `input` in it like every other field here
 * and nothing else tells them apart. */
.sheet label.checkbox, .entry label.checkbox {
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
}

.sheet label.checkbox input {
  padding: 0;
  border: none;
}

/* A path, a key, a name to be typed back: text where every character counts and
 * none of them should be guessed at. */
.sheet .exact {
  font-family: ui-monospace, monospace;
  font-size: var(--control);
}

.sheet .hint {
  margin: 0;
  font-size: var(--aside);
  color: var(--quiet);
}

/* Facts about the thing, or what is about to be lost: a line each, name against
 * value, hairline between. The same shape as the lists on the screen you land on. */
.sheet .facts {
  margin-top: 1.5rem;
  border-top: 1px solid var(--line);
}

/* A sentence standing where that list would have been, and set off from the lead by
 * as much as the list would have been: without it the two paragraphs read as one,
 * and the second answers a question the first did not ask. */
.instead { margin-top: 1.5rem; }

/* Tighter than elsewhere: a dialogue is 440 pixels wide and what it says about what
 * it is handing over is a footnote to the thing itself. */
.sheet .facts > * {
  padding: 0.5625rem 0;
  font-size: var(--control);
}

/* Something handed over once: a secret on a band tinted with the accent, at the
 * only moment it will ever be readable. `user-select: all` so one click takes all
 * of it and no character is left behind. */
.handover {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-top: 1.375rem;
  padding: 0.875rem 1rem;
  border-radius: 6px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.handover code {
  flex: 1;
  min-width: 0;
  font-family: ui-monospace, monospace;
  font-size: var(--aside);
  line-height: 1.5;
  overflow-wrap: anywhere;
  user-select: all;
}

.handover button {
  flex: none;
  margin: 0;
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  color: inherit;
  font-size: var(--aside);
}

/* One line, the answers against the right. The one that goes ahead is a pill and
 * the one that does not is plain text: two solid buttons side by side are two
 * things weighed equally, and they are not. */
.sheet-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1.25rem;
  padding: 1rem 1.875rem;
  border-top: 1px solid var(--line);
}

.sheet-foot .away {
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--control);
  white-space: nowrap;
  cursor: pointer;
}

.sheet-foot .away:hover { color: var(--ink); }

.sheet-foot .pill:disabled { opacity: 0.45; cursor: default; }

.sheet .failure { padding: 0 1.875rem 1.125rem; }

/* A sheet up from the bottom edge rather than a box in the middle. A dialogue on a
 * phone that floats in the centre is a box with the keyboard over half of it; from
 * the bottom, what is being filled in stays above it. */
@media (max-width: 40rem) {
  .sheet, .sheet.narrow {
    width: 100%;
    max-width: none;
    margin: auto 0 0;
    border: none;
    border-top: 1px solid var(--line);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -14px 40px light-dark(rgb(0 0 0 / 14%), rgb(0 0 0 / 45%));
  }

  /* What says the sheet came up from the edge and can go back down it. */
  .sheet::before {
    content: "";
    display: block;
    width: 2.25rem;
    height: 0.25rem;
    margin: 0.625rem auto 0.125rem;
    border-radius: 999px;
    background: var(--line);
  }

  .sheet-body { padding: 0.875rem 1.125rem 1.25rem; }
  .sheet h2 { font-size: 1.1875rem; }
  .sheet input, .sheet select { padding: 0.5625rem 0; }

  /* Both answers a thumb's width tall, and apart from each other rather than
   * together in the corner. */
  .sheet-foot {
    justify-content: space-between;
    gap: 1rem;
    padding: 0.875rem 1.125rem 1.375rem;
  }

  .sheet-foot .away, .sheet-foot .pill {
    min-height: 2.75rem;
    font-size: var(--text);
  }

  .sheet-foot .pill { padding: 0 1.25rem; }
}

/* --- a scan, while it lasts ---------------------------------------------- */

/* In the flow of the column rather than floating over anything: a scan is
 * something the server is doing, not something being said to you. */
.strip {
  display: flex;
  flex-direction: column;
  padding: 0.6875rem 1.625rem 0.75rem;
}

/* The only thing that toggles, and it is the row and not a chevron on the end of
 * it. Being clickable is enough of an affordance for a line that is already the
 * colour of a link. */
.strip-head {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  width: 100%;
  margin: 0 0 0.4375rem;
  padding: 0;
  border: none;
  color: var(--accent);
  font: inherit;
  font-size: var(--aside);
  text-align: left;
  cursor: pointer;
}

.strip-head .icon { width: 0.875rem; height: 0.875rem; }

.strip-head .counted {
  margin-left: auto;
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* Opens by growing its own row from nothing, which animates where a height cannot:
 * there is no number to animate to, because it depends on what is inside.
 *
 * No padding in here. Padding would survive the collapse and leave an eight pixel
 * gap where the closed state should be nothing at all; everything inside is spaced
 * by `gap` and by its own margins, which do collapse with it. */
.strip-stats {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  transition: grid-template-rows 240ms ease-out;
}

.strip.open .strip-stats { grid-template-rows: 1fr; }

.strip-stats > div {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5625rem;
}

@media (prefers-reduced-motion: reduce) {
  .strip-stats { transition: none; }
}

/* Label against value, the way every other pair of figures in the panel is set. */
.strip-stats .pair {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  font-size: var(--aside);
}

.strip-stats .pair > :last-child { font-variant-numeric: tabular-nums; }

/* Outlined in the alarm colour rather than filled with it: it is the way out of
 * something already running, not the point of the block. */
.strip .stop {
  margin: 0.1875rem 0 0.5rem;
  padding: 0.4375rem 0.625rem;
  border: 1px solid color-mix(in srgb, var(--alarm) 45%, transparent);
  border-radius: 6px;
  color: var(--alarm);
}

/* What is being read right now, which is the one line here that says something is
 * happening rather than how much has happened. Monospaced because it is a path,
 * and clipped to one line because a path is as long as it likes. */
.strip .where {
  overflow: hidden;
  font-family: ui-monospace, monospace;
  font-size: var(--lettering);
  color: var(--quiet);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- the login ----------------------------------------------------------- */

/* The moment between asking who is there and being told. Not the login screen and
 * not the panel, so it wears neither. */
.awaiting {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1rem;
}

/* The one screen with nothing around it — no column, no header, nowhere else to
 * go.
 *
 * Two bands over 47.5rem — the name and then the form, in that order both across
 * and down — and one column under it, at the foot of this section.
 *
 * ⚠️ Not `flex-wrap`, which is what folded this screen before. Two bands of
 * `23.75rem` do fold under 47.5rem on their own, and that is the trouble: the
 * width where the screen changes shape was then said twice, once as a basis here
 * and once as a query there, and the two disagree the moment a scrollbar takes its
 * pixels out of the content box. The bands fold while the query still says there
 * is room for both, and the fold arrives without any of the rules that make it a
 * screen. One authority, and it is the query. */
.entry {
  position: fixed;
  inset: 0;
  z-index: 20;
  overflow-y: hidden;
  display: flex;
  background: var(--paper);
}

/* Each band takes its own scrolling, and the screen takes none. A phone held
 * sideways is where that matters: the name and its line fit such a screen with
 * room to spare and the form does not, and while the screen scrolled as a whole
 * the only way to reach the button was to push the name off the top — moving the
 * half that fitted in order to see the half that did not. Now the band that ran
 * out of room is the band that moves. */
.entry > * {
  flex: 1 1 23.75rem;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  padding: clamp(1.75rem, 5vw, 3.5rem);
}

/* Wide enough for the longest label either language has, and no wider: a login
 * form as wide as a monitor is a login form that looks mislaid. Centred in its
 * own band, which is the one thing on this screen that is.
 *
 * ⚠️ By its own auto margins and not by `justify-content: center`, which centres
 * past the top of a band that has run out of room: the space is taken from both
 * ends, and the end a scroll starts at is the end it can never get back to — the
 * heading and the first field were above the form's own scroll origin, where
 * nothing reaches them. An auto margin gives up instead, resolving to 0 when there
 * is nothing left to divide, so a form taller than its band starts at the top of
 * it and all of it can be scrolled to. */
.entry form {
  width: 100%;
  max-width: 25rem;
  margin: auto;
}

/* Spaced by margins rather than by one gap on the form, because the rows are not
 * all the same distance apart: the lead belongs to the heading above it and
 * stands well clear of the first field, and the tick belongs to the button. */
.entry h1 { font-size: 1.625rem; }
.entry .lead { margin: 0 0 1.875rem; }
.entry label { margin-bottom: 1.375rem; }
.entry .checkbox { margin-bottom: 1.75rem; font-size: var(--control); }

/* The band that is not the form: the name at the top and, centred in what is left
 * under it, the one line that says what this is. Nothing else — what a stranger
 * can read here is the whole of what this page tells anybody who has not got in.
 */
.banner {
  gap: clamp(1.75rem, 5vh, 2.5rem);
  border-right: 1px solid var(--line);
}

/* The same name and glyph the sidebar carries, since this is where somebody sees
 * it first and the two screens have to be the same program. */
.banner .mark {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin: 0;
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.banner .mark .icon {
  width: 1.3125rem;
  height: 1.3125rem;
  color: var(--accent);
}

/* The only large type in the panel. It sets its own measure in characters rather
 * than in pixels, so it breaks after the same words at every size, and the auto
 * margins centre it in what is left under the name rather than dropping it to the
 * foot. */
.claim {
  max-width: 15ch;
  margin: auto 0;
  font-size: clamp(2rem, 4.4vw, 3.5rem);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.03em;
  text-wrap: pretty;
}

/* A label with something on its right: what the field is, and the one thing that
 * can be done to it. */
/* A field with a word above it that acts on it. Two rows: the label and that word
 * on the first, the field across the second.
 *
 * A grid rather than the column the other fields are, because the order in the
 * markup and the order on the screen have to differ here. Tab follows the markup,
 * so the button has to come after the field it reveals — otherwise leaving the
 * username lands on "Show" instead of on the password, and somebody typing their
 * way into the panel is stopped by a control they did not ask for. Drawn, the
 * button belongs up on the label's line, and this is what puts it back there.
 *
 * So the focus order is username, password, Show: the button is still reachable
 * from the keyboard, which `tabindex="-1"` would have cost, and it is reached
 * after the thing it is about rather than before it. */
/* A password field with the word that reveals it up on the label's line.
 *
 * Not bound to the entry screen, because it is the same thing wherever a password
 * is typed once and never repeated: the way in, and an administrator making
 * somebody else's account. Two rows and two columns — the label and the word share
 * the first, the field takes the whole of the second — which is what puts the
 * button back where it is drawn after the markup put it after the field, where tab
 * needs it. */
/* Named the way the rule it overrides is named — `.sheet label, .entry label` —
 * because that one is a class and an element, and a bare `.secret` loses to it on
 * specificity: the field went back to a column and the word dropped below the box
 * in both places at once. */
.sheet label.secret, .entry label.secret {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
}

.secret > span { grid-area: 1 / 1; }

.secret .reveal { grid-area: 1 / 2; }

.secret input { grid-area: 2 / 1 / 3 / 3; }

/* The label and the word beside it sit on one baseline. Only those two: the field
 * is alone on its own row, where there is nothing to line it up with. */
.secret > span, .secret .reveal { align-self: baseline; }

/* Said as a word, underlined, because it is a choice between two words and a
 * glyph on the line where the text goes is a target nobody can name. */
.reveal {
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--aside);
  text-decoration: underline;
  text-underline-offset: 0.1875rem;
  cursor: pointer;
}

.reveal:hover { color: var(--ink); }

/* What went wrong, over the fields rather than on them. A rule down the left in
 * the alarm colour and the faintest wash behind it: neither field is wrong on its
 * own, and marking both would say which one to look at when the server
 * deliberately did not. */
.trouble {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  margin: 0 0 1.625rem;
  padding: 0.75rem 0.875rem;
  border-left: 2px solid var(--alarm);
  background: color-mix(in srgb, var(--alarm) 7%, transparent);
  font-size: var(--control);
}

.trouble .icon {
  flex: none;
  width: 1.0625rem;
  height: 1.0625rem;
  margin-top: 0.0625rem;
  color: var(--alarm);
}

/* The one button on the screen, at the width of the form and tall enough to be a
 * target on a phone. */
.pill.wide {
  width: 100%;
  min-height: 2.75rem;
}

/* How somebody gets back in when they cannot. Below the button and behind a
 * hairline, because it is not part of logging in — it is what to do instead. */
.recovery {
  margin: 1.625rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
  font-size: var(--aside);
  color: var(--quiet);
}

/* One column: a phone, and anything else too narrow to stand two bands. The
 * threshold is two bands of `23.75rem`, which is where they stopped fitting when
 * this screen folded itself.
 *
 * Five things in a single stack — the name, the heading, the fields, the button,
 * and what to do when the password is the thing that is lost. The band beside the
 * form has nothing to be beside any more, so it is reduced to the one thing in it
 * that says whose screen this is.
 *
 * Nothing here stretches the form to the height of what is left, which was tried:
 * the note at its foot then went to the foot of the screen and took the whole gap
 * under the button with it, which reads as something missing between the two. The
 * note keeps the distance it has under the button on every other screen, and the
 * form keeps the top of the column: what was tried after that was letting the band
 * centre it, and a form centred under a name it belongs to reads as a form that
 * came loose from it. */
@media (max-width: 47.5rem) {
  /* One column, and one thing that scrolls again: the screen. Up there the name
   * stays while the form moves beside it, because they are two bands; down here it
   * is what the form is under, part of the same column, and it goes up with it. */
  .entry {
    flex-direction: column;
    overflow-y: auto;
  }

  .entry > * { overflow-y: visible; }

  /* The name at the top and no taller than the name: `flex: none` against the
   * `23.75rem` every band has, which down here is a basis in the other axis and
   * would otherwise hold a third of a phone screen open under one line of text. */
  .banner {
    flex: none;
    padding-bottom: 0;
    border-right: none;
  }

  /* The line of large type. It was written to fill the height of a band beside the
   * form, and above the form it is the largest thing on a screen whose heading is
   * the second largest. */
  .claim { display: none; }

  /* The heading stands further from the name than the fields stand from each
   * other, and no further: enough to be a second block, not enough to look like
   * it belongs to something else. */
  .entering { padding-top: 2.375rem; }

  /* And the form at the top of what is left rather than in the middle of it: the
   * auto margin that centres it in a band of its own has nothing to centre it
   * against down here — a form centred under a name it belongs to reads as a form
   * that came loose from it. */
  .entry form { margin-top: 0; }
}

/* --- where you land ------------------------------------------------------ */

.lead { margin: 0 0 1.75rem; }

/* Four figures between two rules, divided by rules of their own. What was here
 * before was four cards, each with the number in one weight and a coloured disc
 * beside it; the discs were four colours competing where four numbers were meant
 * to be counted, and the cards were four boxes drawn to hold what a line already
 * separates.
 *
 * Counted rather than fitted: `auto-fit` would happily leave three across and one
 * alone underneath, which reads as a mistake. */
.counts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.count {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 1.375rem 1.25rem;
  border-left: 1px solid var(--line);
}

/* The two on the ends are against the margins, so neither takes a line or padding
 * on the side where there is nothing to be divided from. The row then measures the
 * full width of the screen, which is what makes the two rules across it read as the
 * edges of one band rather than as a box drawn around four cells. */
.count:first-child {
  padding-left: 0;
  border-left: none;
}

.count:last-child { padding-right: 0; }

/* The glyph on the label line, at the size of the label and in its colour, so it
 * is part of the word rather than a mark beside the number. */
.named-figure {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}

.named-figure .icon { width: 0.8125rem; height: 0.8125rem; }

/* Large and light. A figure this size does not need weight as well, and at 400 it
 * reads as a quantity rather than as a headline. */
.count .figure {
  font-size: 2.125rem;
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.count .quiet { font-size: var(--aside); }

/* Two across, then two by two, then one — with the lines following, which is why
 * they are on the cells rather than on the container. */
@media (max-width: 46rem) {
  .counts { grid-template-columns: 1fr 1fr; }

  .count { padding: 1.125rem 1rem; }

  .count:nth-child(odd) {
    padding-left: 0;
    border-left: none;
  }

  .count:nth-child(-n + 2) { border-bottom: 1px solid var(--line); }

  .count .figure { font-size: 1.75rem; }
}

/* Two lists side by side, and one under the other when there is no room. The gap
 * is horizontal only: each list draws its own lines and two of them stacked have
 * to meet, or the seam between the columns becomes a gap in the ruling.
 *
 * Both columns are meant to hold the same number of rows. An asymmetry here is
 * what the eye lands on first, and it is the whole reason this screen was redrawn. */
.panes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 3.5rem;
  margin-top: 2.5rem;
}

@media (max-width: 46rem) {
  .panes { grid-template-columns: 1fr; }
}

/* A heading over a run of rows, in the same small uppercase as the ones in the
 * navigation: they do the same job, so they are the same thing. */
.pane h2, .part {
  margin: 0 0 0.25rem;
  font-size: var(--lettering);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--quiet);
}

.pane h3 {
  margin: 1.375rem 0 0.375rem;
  font-size: var(--lettering);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--quiet);
}

.pane h3:first-of-type { margin-top: 0; }

/* Here the lines only divide, so there is none over the first row and none under the
 * last: what is above the list is the heading, and what is below it is air. In the
 * rail and in a dialogue the same list is a block of its own and is closed at both
 * ends. */
.pane .facts > *:last-child { border-bottom: none; }

/* --- the process --------------------------------------------------------- */

/* Two rows, one per thing measured: what it is and what it is a share of on the
 * left, the bar across the middle, the figure against the right. The middle column
 * is what makes them rows rather than a list — the same 2px track that measures
 * elsewhere in the panel, at whatever width the window leaves it. */
.gauge {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr) 5.75rem;
  align-items: center;
  gap: 1.25rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--line);
}

.gauges > .gauge:first-child { border-top: 1px solid var(--line); }

/* The name over what it is a share of, because "61.4 %" and "of 8 cores" are one
 * statement and reading them apart is reading half of it. */
.gauge-head {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.gauge .note {
  font-size: var(--lettering);
  color: var(--quiet);
}

/* Tabular, because these are the only digits in the panel that change while
 * somebody is looking at them, and proportional ones make the number jiggle as
 * it goes from a 1 to a 4. */
.gauge .reading {
  text-align: right;
  font-size: 1.25rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* The figures under the two rows: whatever else is worth saying about the process,
 * in the same even pairs as the lists above, so nothing here is lopsided either. */
.gauges + .panes { margin-top: 0; }

@media (max-width: 46rem) {
  .gauge {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.375rem 1.25rem;
  }

  /* Under both, across the full width: at this size the track is the only thing
   * with room to spare. */
  .gauge .track {
    grid-column: 1 / -1;
    grid-row: 2;
  }
}

/* Two pixels of line, filled from the left. The track is `--line` and the fill is
 * the accent, so the one moving thing on the screen is the colour the account
 * chose.
 *
 * A track and a fill and nothing else: no radius at this height, where a rounded
 * end on a two pixel bar is a smudge, and no gradient. The same shape measures the
 * progress of a scan and, later, of a song.
 *
 * Called a track and not a bar because `bar` is the strip across the top of a
 * narrow screen. Both were `.bar` for a while: the later rule won, the top bar
 * became two pixels of grey, and being two pixels of grey it still took a cell of
 * the frame's grid — which pushed the screen into the column under the sections. */
.track {
  display: block;
  width: 100%;
  height: 0.125rem;
  background: var(--line);
}

.track > span {
  display: block;
  height: 100%;
  background: var(--accent);
}

/* --- the last scan ------------------------------------------------------- */

/* Always here, rather than behind the disclosure this replaces. A scan that
 * finished is the normal state of a server, and what it did is four figures: they
 * fit on one line, and a line of figures does not need to be opened.
 *
 * The `details` that used to hold them said "2 hours ago" and hid the rest. It
 * hid four numbers behind a click to save a line that was there anyway. */
.last-scan { margin-top: 2.5rem; }

.figures {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
  padding-top: 0.875rem;
  border-top: 1px solid var(--line);
}

.figures > div {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.figures .figure {
  font-size: 1.25rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.figures .quiet { font-size: var(--aside); }

/* The two figures that mean something is wrong open the screen that names what is
 * wrong, and only while they are above zero — at zero they are tiles like the other
 * two, because there is nothing to go and see.
 *
 * A tile that opens and not a tile that shouts. It reads exactly as its two
 * neighbours do, colours included: a row of four figures where one is red and one is
 * blue stops being a row of four figures. The whole of what says it opens is the
 * arrow after its name, which is small, quiet and the only thing here that its
 * neighbours do not have. */
.figures .amiss {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  color: inherit;
  text-decoration: none;
}

/* The name and the arrow on one line, the arrow set at the size of the word it
 * follows and half a space off it. */
.figures .opens {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  font-size: var(--aside);
  color: var(--quiet);
}

.figures .opens .icon {
  width: 0.8125rem;
  height: 0.8125rem;
}

@media (max-width: 46rem) {
  .figures { grid-template-columns: 1fr 1fr; }
}

.working {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--accent);
}

/* A path is long, arbitrary and not worth breaking the layout over. */
.path {
  font-size: var(--aside);
  overflow-wrap: anywhere;
}

.quiet { color: var(--quiet); }

/* Its own space, because with the browser's margins gone it would otherwise sit
 * against whatever it is complaining about. */
.failure {
  margin-top: 0.75rem;
  color: var(--alarm);
}

/* Inside a form the gap already spaces it, and a margin on top of that would
 * make the message drift away from the field it belongs to. */
form .failure, .sheet .failure { margin-top: 0.25rem; }

/* Why a listing has nothing in it. Where the first row would have been, on the hairline
 * every run of rows in this panel starts with — never a card floating mid-page, and no
 * illustration. Only a genuine failure gets the alarm colour, and none of these is one:
 * an empty collection is a state, not a fault.
 *
 * Not `.empty`, which a date field with nothing in it already wears, and not `.nothing`,
 * which is the one-line version of this and still what a queue and a roster use. This
 * one says *why*, which is the whole difference.
 *
 * The same width a run of settings is capped at, rather than a measure of its own. It
 * was `46ch` — about half of this — on the reasoning that these are paragraphs and
 * paragraphs want a short line. But nothing else in the panel is set to a reading
 * measure, so the one block that was came out looking like a column that had failed to
 * fill, and a block sitting where a list should be reads as part of that list. Matching
 * the widest thing the panel caps is what makes it look deliberate. */
.diagnosis {
  max-width: 47.5rem;
  padding: 1.875rem 0 2rem;
  border-top: 1px solid var(--line);
}

.diagnosis h2 {
  margin-bottom: 0.375rem;
  font-size: var(--name);
  font-weight: 400;
}

.diagnosis p {
  font-size: var(--control);
  line-height: 1.55;
  text-wrap: pretty;
}

/* The figures that are the proof of the diagnosis: how much was read against how much
 * of it said anything. */
.diagnosis .facts { margin-top: 1.25rem; }

/* What can be done about it, where there is anything. */
.diagnosis .remedy {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-top: 1.25rem;
}

/* A text button that reads as a link and is one in every way but the element. Used for
 * the one action an empty state has beside a pill, and for carrying on down a list that
 * stopped. */
.plainly {
  padding: 0;
  border: none;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: var(--control);
  cursor: pointer;
}

.plainly:hover { text-decoration: underline; }

.diagnosis > .plainly { margin-top: 1.25rem; }

/* In the foot of a list that stopped partway, beside the line saying how far it got. */
.brink .plainly { font-size: var(--aside); }

/* --- the collection ------------------------------------------------------- */

/* One row per track, and the heading row over them, sharing their columns the way
 * the roster's do: written twice in one rule, because the rows are a list and a
 * list cannot be a child of the grid its heading is in.
 *
 * The first column takes what is going and the rest are floored, so a title as
 * long as a sentence pushes nothing out of line — every row's length is under the
 * word Length however long the titles above it are.
 *
 * The floors add up to 764 pixels with the gaps, and 788 with the bleed, which is
 * what the scroll box below is set to. */
.listing-head, .listing > li {
  display: grid;
  grid-template-columns:
    1.75rem minmax(12.5rem, 3fr) minmax(9.375rem, 2fr) minmax(9.375rem, 2fr) minmax(6.875rem, 1fr) 4.625rem;
  gap: 1.25rem;
  /* The twelve pixels the scroll box outside is pulled out by, so a row's tint and
   * its hairline run past the text on both sides while the text stays in line with
   * the rest of the screen. */
  padding: 0 0.75rem;
  min-width: 52.25rem;
}

/* The strip of air left over the heading once it has come to rest, which the rows
 * would otherwise go on scrolling through.
 *
 * A sticky heading stops below its scroll container's `padding-top` rather than at
 * the edge of it, and `.body` keeps 2.75rem up there: far more air than a heading
 * at rest wants over it, and every pixel of it showing the list slide past. The
 * negative `top` below brings the resting place up to 1rem short of the edge, and
 * this covers that 1rem in the colour of the page.
 *
 * The two are one number said twice: whatever `top` leaves over the heading is what
 * this has to be as tall as. At rest the band falls inside the page header's own
 * 2.5rem of margin, so it never reaches text. */
.listing-head::before {
  content: "";
  position: absolute;
  inset: auto 0 100%;
  height: 1rem;
  background: var(--paper);
}

/* Stays where it is while the rows go past it. Which is what a list with no
 * pages needs and a paged one does not: after two hundred rows there is otherwise
 * nothing on screen saying which column is the album and which the genre.
 *
 * It needs the paper under it — the rows would otherwise show through — and it has
 * to sit above them, since a row scrolled under it paints in the same place. */
.listing-head {
  position: sticky;
  /* The page's own 2.75rem of air, less the 1rem the heading keeps over itself. */
  top: -1.75rem;
  z-index: 1;
  padding-bottom: 0.625rem;
  border-bottom: 1px solid var(--line);
  background: var(--paper);
  font-size: var(--lettering);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--quiet);
}

.listing {
  margin: 0;
  /* A list is indented forty pixels unless it is told not to, which here would
   * push the first column out of line with the heading over it. */
  padding: 0;
  list-style: none;
}

/* Pressing a row opens what is known about the track, so it says it is pressable.
 * The first column is a button of its own and keeps its own press: playing a song
 * must never also open a panel about it. */
.listing > li {
  align-items: center;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}

.listing > li:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }

/* Every cell is one line and gives way with an ellipsis. A title that wrapped
 * would make its row taller than the rest, and a list whose rows are different
 * heights cannot be read down. */
.listing > li > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* What it is called is the thing being read; everything else on the row is what it
 * turns out to be. */
.listing .by, .listing .from, .listing .kind { color: var(--quiet); }

/* A track whose file is not where it was. Dimmed rather than struck through or
 * coloured: it is still in the collection and still yours, and a scan finding the
 * file again puts it back exactly as it was. The clause after the artist is what
 * says why it is dim. */
.listing > li.gone { opacity: 0.55; }

/* The edge of the list: that more is on its way, how much of it there is, and the
 * thing whose coming into view asks for it. Centred under the rows rather than
 * against one end, because it is not a row and reads as the list carrying on.
 *
 * The padding is what gives it height when it is empty, which it is whenever
 * nothing is on its way — and something with no height is never reached, so the
 * list would stop growing. */
.brink {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.625rem;
  padding: 1.625rem 0 0.25rem;
  font-size: var(--aside);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* Turns a little faster than the scan's does: this is one thing arriving, not a
 * pass over the whole collection. Anybody who asked not to be moved gets it still,
 * and the line beside it says the same thing in words. */
.brink .icon {
  width: 1.125rem;
  height: 1.125rem;
  animation: turn 1.1s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .brink .icon { animation: none; }
}

/* One column, once five of them no longer fit.
 *
 * What decides the width is not the window: the five floors and their gaps come to
 * 764 pixels, the column of sections takes 240 and the page's own padding another
 * 112. Above that the row is a row of a table; below it the table would have to be
 * dragged sideways to be read, and a list you have to drag is not a list — the
 * scroll box is there so that nothing breaks, not so that it gets used. */
@media (max-width: 72rem) {
  /* Headings over columns that are no longer there. */
  .listing-head { display: none; }

  .listing-head, .listing > li { min-width: 0; }

  .listing > li {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.25rem 1rem;
    padding: 0.875rem 0.75rem;
  }

  /* The title across the first line, then the four other things in pairs: who made
   * it against what kind of thing it is, and what record it is from against how
   * long it lasts. Nothing is dropped — a row this narrow still says all five —
   * and each line reads as a pair rather than as a column of loose facts, with
   * what is being named on the left and what it turns out to be on the right.
   *
   * Set out by area rather than by order, so the pairs can be built out of cells
   * the markup lists one after another. */
  /* The number and its button stay on the title's line, which is the line that was
   * left holding one thing across its whole width for exactly this. Everything below
   * lines up with the title rather than with the edge of the row, so the number
   * stands at the head of the three lines it belongs to. */
  .listing > li { grid-template-columns: 1.75rem minmax(0, 1fr) auto; }

  .listing .starter { grid-area: 1 / 1; }

  .listing .what { grid-area: 1 / 2 / 1 / 4; }

  .listing .by { grid-area: 2 / 2; }

  .listing .kind {
    grid-area: 2 / 3;
    text-align: right;
  }

  .listing .from { grid-area: 3 / 2; }

  .listing .figure { grid-area: 3 / 3; }

  /* All four at the size every second line in this panel is set at, so each pair
   * reads as one line rather than as a value with a caption beside it. */
  .listing .by, .listing .kind, .listing .from, .listing .figure {
    font-size: var(--aside);
  }
}

/* --- starting something --------------------------------------------------- */

/* The first column of a track's row: its number at rest, the play triangle when the
 * pointer is over the row, and a pause on the row that is sounding.
 *
 * The two are stacked rather than swapped, so the column never changes width as the
 * pointer crosses it — and the one on top carries the paper, or the number would
 * show through the triangle. Pulled left by six pixels so the glyph sits over the
 * row's own left edge rather than indented from it. */
/* Square, and the width of its own column, so the number and the glyph that
 * replaces it both sit in the middle of it. The frame pulls this six pixels left of
 * its cell; copied over it only read as a column that had slipped, because the rest
 * of this table's columns start where the page's text does. */
.starter {
  position: relative;
  display: grid;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--aside);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

/* Both layers fill the button and centre their own contents, and that is the whole
 * point of them being written together.
 *
 * `place-content` as well as `place-items`, and the gap put back to nought: a `Show`
 * leaves a marker beside what it draws, so each of these holds two things rather than
 * one — and the button inherits `gap: 0.375rem` from every button in this panel. Two
 * items and a gap is a row six pixels wider than its glyph, centred as a row, which
 * put the glyph three pixels off. `place-items` centres each item in its own cell and
 * could not see it.
 *
 * They were a flow item with an absolute one over it, which is two different ways of
 * finding the middle of the same box — and they disagreed by half the column: the
 * number came out hard against one edge while the glyph sat in the centre, leaning
 * right on a wide screen and left on a narrow one, because the two views lay the row
 * out differently. Whatever the flow item was doing, it does not matter now: neither
 * of these is in flow, so there is only one way left to be centred. */
.starter .resting, .starter .acting {
  position: absolute;
  inset: 0;
}

.starter .acting {
  background: var(--paper);
  color: var(--accent);
  opacity: 0;
}

/* On hover of the whole row, not of the button: the target is small and the row is
 * what the pointer is over. */
.listing > li:hover .starter .acting { opacity: 1; }

.starter .icon {
  width: 0.9375rem;
  height: 0.9375rem;
}

/* The row that is sounding says so without being hovered, and says it in the accent
 * across the whole row. */
.listing > li.sounding { color: var(--accent); }

.listing > li.sounding .starter .acting { opacity: 1; }

.listing > li.sounding .by,
.listing > li.sounding .from,
.listing > li.sounding .kind,
.listing > li.sounding .figure { color: inherit; }

/* --- the records ---------------------------------------------------------- */

/* As many columns as fit rather than a number of them, so the same markup is two
 * covers wide on a phone and eight on a monitor and there is no width written down
 * anywhere to be wrong. `auto-fill` and not `auto-fit`: with three records in the
 * collection, `auto-fit` collapses the empty tracks and stretches those three to a
 * third of the window each, which is a shelf of three enormous covers. */
.shelves {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(12.5rem, 100%), 1fr));
  gap: 1.625rem 1.375rem;
}

.sleeve {
  display: flex;
  flex-direction: column;
  gap: 0.5625rem;
  min-width: 0;
}

/* The square, and the button over it. Nothing but a frame for the two of them to be
 * positioned against. */
.cover {
  position: relative;
  display: block;
}

/* A circle in the middle of the cover, there only while the pointer is on the
 * record. Deliberately not the whole square: the rest of the cover is what will open
 * the record itself, and a target that filled it would leave nowhere to press for
 * that.
 *
 * In the corner rather than the middle, filled in the accent, and lifted off the
 * sleeve by a shadow. Which is what lets the cover stay exactly as it is while the
 * pointer is over it: a circle of paper in the centre needed a wash behind it to keep
 * its glyph legible over a pale sleeve, and that wash dimmed the one thing on this
 * screen worth looking at. Filled in the accent it carries its own contrast, and the
 * shadow is what holds it off a sleeve of any colour. */
.cover .over {
  position: absolute;
  right: 0.625rem;
  bottom: 0.625rem;
  width: 2.5rem;
  height: 2.5rem;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 4px 12px light-dark(rgb(0 0 0 / 28%), rgb(0 0 0 / 50%));
  opacity: 0;
  cursor: pointer;
}

/* The same pairing as any filled pill in this panel: whatever reads on the accent. */
.cover .over .icon {
  width: 1.125rem;
  height: 1.125rem;
  color: light-dark(#fff, #0c1a14);
}

/* Under the pointer, on focus, and for as long as this is the record sounding —
 * which is the one case where it is not a way to start something but the way to stop
 * it, and a control that only exists while pointed at would be a shelf you have to
 * hunt across to find what is playing. */
.sleeve:hover .cover .over,
.cover .over:focus-visible,
.cover .over.sounding { opacity: 1; }

/* Gone entirely where there is no pointer to hover with, which is every phone and
 * tablet. The alternative was leaving it on every cover for good, and a shelf of
 * records with a button stamped on each of them is a shelf of buttons — the covers are
 * the one thing on this screen worth looking at.
 *
 * Nothing is lost by taking it away there. Playing a record is what its own panel is
 * for, which a tap already opens; stopping one is what the bar at the bottom of the
 * screen is for, which is always in reach; and which record is playing is said in words
 * on the third line of every sleeve, and in the accent on its name. The button was the
 * shortcut, not the way. */
@media (hover: none) {
  .cover .over { display: none; }
}

/* And the name in the accent, the way the sounding row of a track listing is. */
.sleeve .what.sounding { color: var(--accent); }

/* Square whether or not there is anything in it, so the names below sit on one
 * line across the shelf rather than stepping up and down with the art.
 *
 * One class for the picture and for the frame standing in for it: they are the same
 * square, and the difference is that one has a glyph centred in it. */
.art {
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--line);
  object-fit: cover;
}

.art .icon {
  width: 2.125rem;
  height: 2.125rem;
}

/* The name, then who made it, then what it is. Three lines that belong together, so
 * the last two are inside the first rather than beside it. */
.sleeve .what {
  min-width: 0;
  font-size: var(--control);
  line-height: 1.3;
}

.sleeve .what span {
  display: block;
  color: var(--quiet);
  font-size: var(--aside);
}

/* Who made it reads as a name rather than as a fact about the record, so it keeps
 * the size of the line above it and gives the smaller size to the figures. The
 * figures are the only line here that is numbers, and they line up down the shelf. */
.sleeve .by { font-size: var(--control); }

.sleeve .what > :last-child { font-variant-numeric: tabular-nums; }

/* --- everybody credited --------------------------------------------------- */

/* Two columns of rows on a wide window, one on a narrow one. The only screen in the
 * collection that fills the room by running two columns rather than by making
 * anything larger, because there is nothing here to make larger: a name and two
 * figures is a row, and a row stretched to fourteen hundred pixels is a name at one
 * end and a number at the other.
 *
 * They fill across and not down, which is what the grid does on its own — and is the
 * only way round that survives a list growing as it is scrolled. Columns filled
 * downwards would be rebalanced end to end every time another fifty names arrived,
 * moving the one somebody was reading.
 *
 * The rule across the top belongs to the whole listing, so it runs through the gap
 * as well; every row closes itself. */
/* 28rem and not the 20 a row needs to hold its three parts, because a row that
 * merely fits is a row whose name is cut: the two figures take about 135px of it
 * whatever the name is, so a 350px column leaves 160px for a name — and "The Velvet
 * Underground" is 165. At 28rem a 1920 window runs three columns of 485px each,
 * which gives a name nearly 300, and a wider one goes to four before it goes to
 * five. */
.crowd {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(28rem, 100%), 1fr));
  gap: 0 3.5rem;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

/* The bleed is per row here rather than on the block: with two columns, a block
 * pulled out at its edges would do nothing for the row in the second column. There
 * is room for it either side — the gap between the columns is 3.5rem and the page's
 * own padding is the same. */
.crowd > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: 1rem;
  margin: 0 -0.75rem;
  padding: 0.8125rem 0.75rem;
  border-bottom: 1px solid var(--line);
}

.crowd > li:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }

.crowd .what {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.crowd .figure { color: var(--quiet); }

/* The genres, which are the artists' rows with two things taken away.
 *
 * Narrower columns, because a genre is a word and not a name: nothing here needs
 * room for "The Incredible String Band", and five columns of short words read better
 * than three with a hand of air on the right of each.
 *
 * And no tint under the pointer and no bleed, unlike every other row in the
 * collection. A genre has no page behind it — it is the one thing here that is not
 * an object, with no identifier beyond its own name — and a row that lights up as
 * you reach it has promised something there is nothing to give. */
.kinds {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(16.25rem, 100%), 1fr));
  gap: 0 3.5rem;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

/* Pressable, since a genre opens its own panel now. The twelve pixels either side
 * are what lets the tint run past the text while the text stays lined up with the
 * heading above it — the same trick every other listing plays, done here with a
 * margin because these rows sit in the cells of a grid. */
.kinds > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 1rem;
  margin: 0 -0.75rem;
  padding: 0.8125rem 0.75rem;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}

.kinds > li:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }

.kinds .what {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kinds .figure { color: var(--quiet); }

/* --- what is yours -------------------------------------------------------- */

/* The three kinds of thing you can have marked, over the listing of whichever is
 * showing. Not the drawer's tab strip: that divides one thing into parts and sits
 * inside a panel with its own padding, and these are three listings that happen to be
 * about the same account.
 *
 * No rule across the bottom either, because the listing under it brings its own — a
 * hairline here would sit a hand's width above another one. */
.picking {
  display: flex;
  flex-wrap: wrap;
  gap: 1.375rem;
  margin-bottom: 1.5rem;
}

/* Text and not pills, like every other choice in this panel. The line under the
 * chosen one is there at rest in transparent, so choosing another moves nothing. */
.picking button {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.25rem 0;
  border: none;
  border-bottom: 1px solid transparent;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--control);
  cursor: pointer;
}

.picking button:hover { color: var(--ink); }

.picking .chosen {
  border-bottom-color: var(--accent);
  color: var(--accent);
}

/* Lifted into the accent rather than ringed, like every other borderless control
 * here: a box appearing round a bare word reads as something that was not there a
 * moment ago. */
.picking button:focus-visible {
  outline: none;
  color: var(--accent);
}

/* How many of that kind there are, which stays quiet on the chosen one as well: it is
 * a figure beside a word, not part of the word. */
.picking .figure {
  color: var(--quiet);
  font-size: var(--aside);
}

/* Every one of these is a 44px target on a phone. */
@media (max-width: 45rem) {
  .picking button { padding: 0.75rem 0; }
}

/* --- libraries ----------------------------------------------------------- */

/* One row per library on three columns: what it is, how much of it there is, and
 * what can be done to it. These were cards on a raised surface, which is what a
 * card is for — grouping things that do not line up — and the trouble was that
 * these do line up. A column of figures and a column of actions read as columns.
 *
 * The first column takes whatever width is left and the other two are fixed, so
 * every row's figures and every row's actions start at the same place however long
 * a path is. `minmax(0, 1fr)` and not `1fr`, or a path with no spaces in it would
 * refuse to shrink and push the other two off the side.
 *
 * `baseline` and not `center`: the three columns have two lines each and it is the
 * first line of each that belongs together. */
.shelf {
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

/* The two fixed columns are wider than the design's 150 and 120, and the reason is
 * the reason everything here is wider than a drawing: the drawing was in English.
 * "3 missing · 2 h ago" fits in 150 pixels and "12 ausentes · hace un momento" does
 * not, and the third column has to hold three actions at once — Editar, Activar,
 * Eliminar — because that is what a switched off library offers.
 *
 * Sized for the worst case rather than for the common one, so that a row does not
 * change height when it changes state. A row that grows a line while somebody is
 * aiming at it moves every row below it. */
.shelf > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 10.625rem 10.9375rem;
  align-items: baseline;
  gap: 1.5rem;
  padding: 1.25rem 0;
  border-bottom: 1px solid var(--line);
}

.shelf .what { min-width: 0; }

.shelf h2 {
  margin: 0 0 0.25rem;
  font-size: var(--name);
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  color: inherit;
}

/* A switched off library goes quiet and keeps everything else. It is set aside,
 * not broken, and the row still says what is in it and still lets it be edited. */
.shelf > li.off h2 { color: var(--quiet); }

/* Wraps mid-path when it has to, which for a path with no spaces in it is the only
 * place it can wrap. */
.shelf .path {
  margin: 0;
  font-family: ui-monospace, monospace;
  font-size: var(--aside);
  overflow-wrap: anywhere;
}

.shelf .much {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* One line, and never two. `nowrap` is not there to look tidy: it is what makes the
 * worst case an overflow to the left instead of a second line, and an overflow
 * moves nothing. Every fixed width here is a guess at how long a language is, and
 * this is what a wrong guess costs. */
.shelf .much .quiet {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--aside);
  white-space: nowrap;
}

/* The state over the things that change it, both against the right so the column
 * has one edge. */
/* Two lines, the same way the other two columns have two lines: a first line and a
 * block under it with four pixels over it. This was a flex column with a `gap`,
 * which is a different mechanism for the same job — and a flex container's height is
 * its tallest item rather than a line box, so the four pixels came out measured from
 * somewhere else and the column read as more loosely set than its neighbours. */
.shelf .doing { text-align: right; }

.shelf .state {
  display: block;
  font-size: var(--aside);
  color: var(--quiet);
}

.shelf .state.on { color: var(--accent); }

/* Never wraps, for the same reason as the line above: switching a library off adds
 * a third action, and a third action that dropped onto a second line would push
 * every row under it down as it appeared.
 *
 * Kept to the shelf. It was bare, and the track listing has a `.acting` of its own —
 * the play glyph that comes up over a row's number — so this was landing on that too.
 * `display` and `gap` were overridden there, and `margin-top: 0.25rem` was not: on a
 * span with `inset: 0` that pushes the top edge down and leaves the bottom, which put
 * the glyph two pixels below the centre of its own box. The last of the off-centre
 * play button, found by asking which names two screens share rather than by looking
 * at it again. */
.shelf .actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 0.25rem;
  gap: 0.75rem;
  font-size: var(--aside);
  white-space: nowrap;
}

/* Stacked once there is no room for three columns, with the actions against the
 * left like everything else rather than hanging off an edge that is no longer
 * there.
 *
 * Its own breakpoint, and a wider one than the rest of the panel uses. What decides
 * it is not the window: it is that two fixed columns take 345 pixels of it and the
 * path gets what is left, so this screen runs out of room a long way before a screen
 * of figures does. By the time the columns would have collapsed at the panel's usual
 * 736 the paths had been wrapping mid-directory for a while. */
@media (max-width: 53.125rem) {
  .shelf > li {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.625rem;
  }

  .shelf .much, .shelf .doing { text-align: left; }
  .shelf .actions { justify-content: flex-start; }
}

/* --- who there is -------------------------------------------------------- */

/* The search and the one action that belongs to the whole screen, at the other end
 * of the title's line. It takes what width it can and gives it back first: what is
 * on the left is a title, and a title that has to wrap so a search box can keep its
 * three hundred pixels has been outvoted by a search box. */
.finding {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1.25rem;
  flex: 1 1 20rem;
  flex-wrap: wrap;
}

/* A field that is a line under a word, not a box. Same idiom as the fields in a
 * dialogue, and for the same reason: the border around an input is a rectangle
 * drawn to say "type here" where a rule and a glyph already say it. */
.search {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  flex: 1 1 13.75rem;
  max-width: 20rem;
  padding: 0 0 0.4375rem;
  border-bottom: 1px solid var(--line);
  color: var(--quiet);
  font-size: inherit;
}

.search .icon {
  width: 1rem;
  height: 1rem;
}

.search input {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: var(--control);
}

/* The cross that empties the field, at the end of the field. No colour of its own:
 * it takes the box's, so it is grey at rest and in the accent while something is
 * being typed into it, which is where it is any use.
 *
 * `stretch` and no height, because a square tall enough to press would be taller
 * than a line of text — and the cross comes and goes with the first letter typed, so
 * anything that made the box taller would move the rule under it as you type. This
 * way it is as tall as the field already is, and all of that is pressable. */
.search .wipe {
  display: grid;
  place-items: center;
  flex: none;
  align-self: stretch;
  width: 1.5rem;
  padding: 0;
  border: none;
  cursor: pointer;
}

/* The browser's own cross on a search field, which is a different grey in every
 * browser and is now a second one beside ours. */
.search input::-webkit-search-cancel-button { display: none; }

.search:focus-within { color: var(--accent); }

.search:focus-within, .search input:focus-visible {
  border-bottom-color: var(--accent);
  outline: none;
}

/* One row per account, and the heading row over them. The two share their columns
 * by saying them twice in one rule rather than by `subgrid`, which would need the
 * rows to be children of the same grid — and they are not: the rows are a list, and
 * a list is a `ul` with `li` in it.
 *
 * The first column takes what is left and the others are floored, so every row's
 * figures start where the heading says they will however long a name is. */
.roster-head, .roster > li {
  display: grid;
  grid-template-columns:
    minmax(12.5rem, 3fr) minmax(6.5625rem, 1fr) minmax(8.125rem, 1fr) minmax(6.5625rem, 1fr) 1.5rem;
  gap: 1.5rem;
  /* The twelve pixels the block outside is pulled out by, so a row's tint and its
   * hairline run past the text on both sides while the text stays lined up with
   * everything else on the screen. */
  padding: 0 0.75rem;
  min-width: 42.75rem;
}

.roster-head {
  padding-bottom: 0.625rem;
  border-bottom: 1px solid var(--line);
  font-size: var(--lettering);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--quiet);
}

.roster {
  margin: 0;
  /* A list starts forty pixels in unless it is told not to. Which is not an
   * indent here: it is the first column of a grid, pushed out of line with the
   * heading over it. */
  padding: 0;
  list-style: none;
}

.roster > li {
  align-items: center;
  padding-top: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
}

.roster > li:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }

/* Three cells of the grid, wrapped only so that they can become one line together
 * when the columns go. On a wide screen the wrapper is not there as far as layout is
 * concerned. */
.roster .about { display: contents; }

/* Numbers under a heading that is also a number's, both against the right and both
 * in tabular figures, so "1 · 1" and "12 · 3" line up on the dot. */
.roster-head .figure, .roster .figure, .listing-head .figure, .listing .figure,
.crowd .figure, .kinds .figure {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The bleed itself: the twelve pixels a row's tint and hairline run past the text
 * on either side, so the text stays in line with the rest of the screen while the
 * row reads as a band rather than as a label with a tint stopping at the last
 * letter.
 *
 * Where the rows are in a box that scrolls sideways this goes on the box and never
 * on anything inside it: inside, the left twelve can never be reached — a scroll
 * offset is never negative — and the right twelve widen the content and ask for a
 * scrollbar of their own. Where there is no such box, as in a list whose heading
 * has to stay put, it goes on the block holding the rows. */
.bled { margin: 0 -0.75rem; }

/* The name and what is said about it, on one line. The name is what gives way when
 * the column runs out, so the badge beside it never goes off the end. */
.roster .whose {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
}

.roster .whose .named {
  overflow: hidden;
  text-overflow: ellipsis;
}

.roster .account {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
}

/* Which they are, as a word. The colour is the same one the avatar beside it is
 * tinted with, so the row says it twice in one glance rather than twice in two. */
.roster .rank { color: var(--quiet); }

.roster .rank.admin { color: var(--accent); }

/* One column, once four of them no longer fit.
 *
 * What decides the width is not the window: the four floors and their gaps come to
 * 684 pixels, the column of sections takes 240 of whatever there is and the page's
 * own padding another 112. Above that the row is a row of a table; below it the table
 * would have to be dragged sideways to be read, and a list you have to drag is not a
 * list — the scroll box is there so nothing breaks, not so that it gets used. Under
 * 45rem the column of sections becomes a bar across the top, which gives the page
 * back 200 pixels and still not enough for four columns, so one breakpoint covers
 * both. */
@media (max-width: 65rem) {
  /* Headings over columns that are no longer there. */
  .roster-head { display: none; }

  .roster-head, .roster > li { min-width: 0; }

  .roster > li {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.375rem 0.875rem;
    padding: 0.875rem 0.75rem;
  }

  .roster .account { grid-area: 1 / 1; }

  /* Still at the end of the line the name is on, which is the line it belongs to. */
  .roster .dots { grid-area: 1 / 2; }

  /* What were three columns become one line of prose under the name, set at the size
   * every second line in this panel is set at. */
  .roster .about {
    grid-area: 2 / 1 / 3 / 3;
    /* In line with the name and the address, not with the edge of the row: the three
     * of them are one block of text about one account, and this line started under
     * the initial in its circle. Thirty pixels of circle and the twelve of its gap,
     * which is what `.roster .account` sets. */
    padding-left: 2.625rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.125rem 0.75rem;
    font-size: var(--aside);
  }

  .roster .about .figure { text-align: left; }

  /* Two numbers with nothing written over them are two numbers. On a wide screen the
   * pair sits under a heading that says which is which; stacked there is nowhere to
   * put one, and both figures are on the account's own screen, one tap away. */
  .roster .using { display: none; }
}

/* What the list adds up to, and how to open one. Under the rows, where somebody
 * ends up. */
.tally {
  margin: 1.125rem 0 0;
  font-size: var(--aside);
  color: var(--quiet);
  text-wrap: pretty;
}

/* --- accounts ------------------------------------------------------------ */

/* The link that names a row. Ink rather than the accent, because a row is not a
 * sentence with a link in it: what is coloured in this list is what says something —
 * who administers, whose row is yours — and every name would be coloured.
 *
 * Kept to the roster. It was bare, and the opened player has a `.named` of its own —
 * the block holding a song's title and who made it — so a title was being set at
 * weight 500 and underlining itself when the pointer crossed it. */
.roster .named {
  color: var(--ink);
  font-weight: 500;
}

.roster .named:hover { text-decoration: underline; }

/* Fixed to the viewport, because a box that scrolls sideways clips whatever
 * overflows it — which is what left this menu drawn inside the list and cut off.
 * Fixed is outside all of that; where to put it is worked out from the button's own
 * rectangle. */
.menu.afloat {
  position: fixed;
  /* Whichever edge it hangs from is set inline, once the room either side of the
   * button is known. `auto` here is what lets the upward case leave `top` alone:
   * with both edges set, a fixed box is stretched between them instead. */
  top: auto;
  text-align: left;
}

/* Wide content sideways inside its own box, never the whole page. */
.scrolls { overflow-x: auto; }

/* Back to the list, above the heading, small enough not to compete with it. */
.back { margin-bottom: 0.5rem; font-size: var(--control); }

/* The box before its words, both clickable, and the cursor saying so. */
.checkbox {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  color: var(--ink);
  font-size: var(--name);
}

.checkbox input {
  width: 1rem;
  height: 1rem;
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
}

/* Said after something worked. Not an error and not silence. */
.note {
  margin-top: 0.75rem;
  color: var(--accent);
}

/* --- what an account may reach ------------------------------------------- */

/* A box per library, in as many columns as fit. Never stretched across a wide
 * screen: a row whose tick is at the left margin and whose count is nine hundred
 * pixels away is a row nobody can read across. */
.ticks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(17.5rem, 100%), 1fr));
  gap: 0 3.5rem;
  margin-top: 0.875rem;
  border-top: 1px solid var(--line);
}

/* The tick, what it is, and how much of it there is. Three columns, so the counts
 * line up down the grid however long a name is. */
.tick {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.875rem 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}

.tick input {
  width: 1rem;
  height: 1rem;
  margin: 0;
  accent-color: var(--accent);
}

/* The name at the size of ordinary text and in ordinary ink. Inside a `label`
 * neither is the default: a label is small and quiet, which is right for the word
 * beside a checkbox and wrong for the name of the thing being ticked. */
.tick .what {
  min-width: 0;
  font-size: var(--text);
  color: var(--ink);
}

/* One line and cut off at the end, which is the opposite of what a path does
 * everywhere else here: in a grid this narrow a wrapped path would make one row
 * taller than the three beside it. */
.tick .path {
  display: block;
  font-size: var(--lettering);
  /* Said here because the name above it is in ink and a path inherits from it: what
   * a path is, everywhere in the panel, is the quiet line under the name. */
  color: var(--quiet);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tick .figure {
  font-size: var(--aside);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* A library switched off is out of reach for everybody, so the row says so and goes
 * quiet — and stays tickable, because the restriction outlives it being switched
 * back on. */
.tick.off { opacity: 0.55; }

/* The rail's own actions, one under the other and each as wide as the rail: there
 * are two of them and neither is the answer to the other. */
.rail .doings {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.875rem;
}

.rail .doings .pill {
  width: 100%;
  justify-content: center;
}

.rail .hint { margin-top: 0.625rem; }

/* --- the ways into an account -------------------------------------------- */

/* Two lists that belong together: side by side while there is room for both, one
 * under the other when there is not, and no media query deciding which. The keys
 * take the wider share — a key has a name somebody chose and a menu at the end of
 * its row, a session has neither. */
.lists {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 2.75rem 3.5rem;
  margin-top: 2.75rem;
}

.lists > :first-child {
  flex: 3 1 30rem;
  min-width: 0;
}

.lists > :last-child {
  flex: 2 1 21.25rem;
  min-width: 0;
}

/* A heading with the one action that adds to what is under it, on the same line:
 * the two are words, not blocks, which is why they line up on their baselines. */
.parted {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  /* The four pixels a heading has under it on its own, since the heading in here
   * gives them up to sit on a line with the action. */
  margin-bottom: 0.25rem;
}

.parted h2 { margin: 0; }

/* A second heading in the same column stands well clear of the list above it,
 * which is what says that list ended. */
.pane h2 ~ h2 { margin-top: 2.5rem; }

/* The action that adds to a list, in the accent and written as a word. A pill here
 * would be the loudest thing in a column of hairlines, and what it opens is a
 * dialogue that asks two questions. */
.offer {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: var(--control);
  cursor: pointer;
}

.offer .icon {
  width: 0.9375rem;
  height: 0.9375rem;
}

/* One way into the account: a key, or a browser. Two lines rather than a row of
 * columns, because these dates are prose — "used 5 h ago · never expires · made
 * 14 May 2026" is read across, not compared down a column — and prose is what
 * fits when two lists have half a screen each.
 *
 * The action is a cell of its own spanning both lines, so it stays against the
 * right and level with the name whatever the sentence under it says. */
/* Air above the rule, because the line that introduces the list is not part of it.
 * Without it the first rule sat against the descender of the sentence explaining
 * what the list is, and the two read as one block of small grey text. */
.ways {
  margin: 1rem 0 0;
  padding: 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

.ways > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.25rem 0.875rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--line);
}

/* The keys wear a glyph, in a column narrow enough to read as an indent rather
 * than as a column. It is what tells the two lists apart at a glance once they are
 * beside each other. */
.ways.keyed > li { grid-template-columns: 1.25rem minmax(0, 1fr) auto; }

.ways .mark {
  grid-row: span 2;
  align-self: center;
  color: var(--quiet);
}

.ways .mark .icon {
  width: 1.0625rem;
  height: 1.0625rem;
}

.ways .what {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  min-width: 0;
  font-size: var(--name);
}

.ways .doing {
  grid-row: span 2;
  align-self: center;
  justify-self: end;
  white-space: nowrap;
}

.ways .said {
  font-size: var(--aside);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

.ways.keyed .said { grid-column: 2; }

/* A list whose rows carry a figure: what it is called, how it is going, and the
 * action. Every cell is placed by hand, which is not fussiness — leave the figure's
 * column to `auto` and it lands in column one and pushes the name onto a row of its
 * own. */
.ways.tallied > li { grid-template-columns: minmax(0, 1fr) auto auto; }

.ways.tallied .what { grid-area: 1 / 1; }

.ways.tallied .standing {
  grid-area: 1 / 2;
  justify-self: end;
  white-space: nowrap;
  font-size: var(--aside);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* The figure in the alarm colour when it is a figure that means something is wrong,
 * which the row decides: a queue with nobody trying to send it is not stuck. */
.ways.tallied .standing.alarm { color: var(--alarm); }

.ways.tallied .doing { grid-area: 1 / 3; }
.ways.tallied .said { grid-area: 2 / 1 / 2 / 3; }

/* What the far end said, on a line of its own under everything. In the alarm colour,
 * which it was not: it wore the class and the stylesheet had no rule for it, so the
 * one line saying a machine refused something read exactly like the address above
 * it. */
.ways.tallied .said.wrong {
  grid-area: 3 / 1 / 3 / 3;
  color: var(--alarm);
}

/* A key that no longer opens anything. Quiet, and neither struck through nor gone:
 * it is the row that says what was withdrawn, which is the whole reason revoking
 * is not deleting. */
.ways > li.off .what { color: var(--quiet); }

.ways > li.off .mark { opacity: 0.5; }

/* What has become of the thing named beside it, in one word. */
.tag {
  font-size: var(--lettering);
  color: var(--alarm);
}

/* And the same word when it is somebody's decision rather than something wrong: a
 * destination paused on purpose says so, and says it quietly. A key that expired or
 * was revoked keeps the alarm, which is the difference. */
.tag.deliberate { color: var(--quiet); }

/* The question at the foot of a list, with its answer next to it. At the foot and
 * never at the top: what makes somebody reach for this is having read the list. */
.after {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.625rem;
  margin: 0.875rem 0 0;
  font-size: var(--aside);
  color: var(--quiet);
}

/* One line between two rules: something this screen has to say and another screen
 * changes. The same field on two screens is two places to get it wrong. */
.tail {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 0;
  padding: 0.875rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  font-size: var(--control);
}

.tail a { white-space: nowrap; }

/* Beside a form field or an action, saying what it wants or why it is not
 * offered. */
.hint { font-size: var(--aside); }

/* One line under the control that just did something, saying it did — or saying it
 * did not. Under the control, because a pane whose settings save themselves has no
 * button to put it beside, and the note this one used to share with the form above it
 * appeared at the foot of the whole screen, a column away from the switch somebody
 * had just touched. */
.settled {
  margin: 0.5rem 0 0;
  font-size: var(--aside);
  color: var(--quiet);
}

.settled.alarm { color: var(--alarm); }

/* Why what follows has gone quiet. The shape of `.trouble` without any of its alarm:
 * nothing is wrong, something is switched off.
 *
 * Air on both sides, and the same on both: what it explains has air above it when
 * this box is not there, and a box wedged against the sentence above it is a box
 * that reads as part of that sentence. Below, its margin and the list's collapse
 * into one, so the gap is the same whether this is here or not. */
.because {
  margin: 1rem 0;
  padding: 0.6875rem 0.875rem;
  border-left: 2px solid var(--quiet);
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  font-size: var(--aside);
  color: var(--quiet);
  text-wrap: pretty;
}

/* Still there, and doing nothing. Faded and not disabled: every action on it still
 * works, because switching a destination off, or adding one, is exactly what somebody
 * does while the whole thing is off. */
.stilled { opacity: 0.55; }

/* A hint that is a complaint. Said in the alarm colour rather than shown as a
 * banner, because it belongs to the field above it and goes away as soon as that
 * field is right. */
.alarm { color: var(--alarm); }

/* A field whose value is wrong as it stands. The border says so where the eye
 * already is, without moving anything. */
input.wrong, input.wrong:focus-visible { border-color: var(--alarm); }

/* A note under a heading, with room before whatever it introduces. */
.pane > .hint { margin-bottom: 1rem; }

/* --- the colours you pick, and every round button ------------------------ */

/* Two things, and the second is here because it is nobody's: the swatches on the
 * preferences screen, and the one rule that centres a glyph inside a round control
 * wherever the panel has one. The section was called "the parts", which said nothing
 * and let anything in. */


label { font-size: var(--aside); color: var(--quiet); }

.swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* A colour and nothing else — no outline of its own, which at this size read as a
 * grey ring around every colour. It carries its own `data-accent`, so the fill is
 * the same variable the panel paints with rather than a second copy of the
 * palette. */
.swatch {
  width: 1.375rem;
  height: 1.375rem;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
}

/* A hairline ring outside the circle rather than a tick inside it: at this size a
 * tick is a smudge, and the ring leaves the colour whole. One pixel, like every
 * other line in the panel, and set off far enough to read as a ring rather than as
 * a border. */
.swatch.chosen {
  outline: 1px solid var(--accent);
  outline-offset: 3px;
}

/* A date field with nothing in it still shows dd/mm/yyyy, in the same ink as a
 * value that had been typed. Dimmed, it reads as the hint it is — the same as a
 * placeholder anywhere else. */
input[type="date"].empty { color: var(--quiet); }

input, select, button {
  font: inherit;
  font-size: var(--control);
  color: inherit;
  padding: 0.4375rem 0.625rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
}

input:focus-visible, select:focus-visible, button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Inline flex so an icon and its label sit on one baseline. Buttons were being
 * laid out as text, which left every icon riding a little high.
 *
 * Centred, which only shows once a button is wider than its label — a whole
 * column of them at the width of the longest, for instance. */
/* What every button shares and nothing more. It used to be filled with the accent
 * here, as the default look of a button — which meant every button that was not that
 * had to undo a background, a weight and a margin before saying what it was. Nothing
 * is filled by default now: `.pill.solid` is, and it says so. */
button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  cursor: pointer;
}

button:disabled { opacity: 0.6; cursor: default; }

/* A control that is one glyph and nothing else, of which the player is made.
 *
 * The gap above is for a button that is a glyph beside a word, and every button here
 * inherits it — which is invisible until something puts two things inside one of
 * these. A `Show` does exactly that: it leaves a marker beside what it draws, so the
 * grid holds two items, the gap pushes them six pixels apart, and the pair is centred
 * as a pair. The glyph then sits three pixels left of the middle of its own button:
 * the play triangle in the row of a track, the round one in the sidebar, the one on
 * the bar of a phone.
 *
 * `place-content` as well as `place-items` for the same reason — `place-items` centres
 * each item inside its own cell and cannot see that the cells together are off. With
 * the gap at nought both say the same thing, and saying both is what keeps this from
 * depending on which. */
.starter, .starter .resting, .starter .acting,
.dock .step, .dock .toggle, .afoot .tap, .cover .over,
.sheet-player .tap, .sheet-player .step, .sheet-player .toggle {
  display: grid;
  gap: 0;
  place-content: center;
  place-items: center;
}

/* --- settings ------------------------------------------------------------- */

/* What a thing is on the left, how it is set on the right, one per row and a line
 * between them. Every screen that is a list of choices is built out of these — the
 * preferences, your own profile, the server's own settings — so the shape is
 * defined once here and none of those screens invents its own.
 *
 * Capped, and this is the one place in the panel that is: a field that grows to
 * nine hundred pixels promises nine hundred pixels of text. What is uncapped is a
 * screen of figures, where the width is there to be used. */
.settings { max-width: 47.5rem; }

/* Fluid by construction, with no width in a media query anywhere. The label asks for
 * 200 pixels and the control for 300 of the remaining two thirds; when the row
 * cannot give them both, `wrap` puts the control under its label by itself. A
 * breakpoint would be a guess at when that happens, and the guess would be wrong for
 * the language that writes "Confirma los cambios con tu contraseña actual".
 *
 * `baseline` and not `center`: a row whose control is two lines tall would otherwise
 * pull its label into the middle of them, away from the first line it names. */
.settings > .setting {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.375rem 2rem;
  padding: 1.375rem 0;
  border-top: 1px solid var(--line);
}

.settings > .setting > :first-child {
  flex: 1 1 12.5rem;
  min-width: 0;
}

.settings > .setting > :last-child {
  flex: 2 1 18.75rem;
  min-width: 0;
  max-width: 26.25rem;
}

/* The last one closes the run, so the set reads as a block rather than as rows
 * hanging off the bottom of the screen. */
.settings > .setting:last-child { border-bottom: 1px solid var(--line); }

/* What the row is for, under what it is called. */
.setting .why {
  display: block;
  font-size: var(--aside);
  color: var(--quiet);
}

/* A value the account cannot set for itself: a role is given by somebody else, so it
 * is text where the others have a field. */
.setting .flat { color: var(--quiet); }

/* Fields inside a run of settings look the way they look inside a dialogue: a line
 * under the value rather than a box around it. The checkbox is excluded by what it
 * is rather than by a class, since a box around a box is what the rule would draw.
 */
.settings input:not([type="checkbox"]), .settings select {
  width: 100%;
  padding: 0.375rem 0;
  border: none;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  font-size: inherit;
}

.settings input:not([type="checkbox"]):focus-visible, .settings select:focus-visible {
  outline: none;
  border-bottom-color: var(--accent);
}

/* The row that answers for all the others. Its label and its line are the accent,
 * which is the panel saying "this one is the condition" without a word of warning
 * anywhere. */
.setting.asked > :first-child > :first-child { color: var(--accent); }
.setting.asked input { border-bottom-color: var(--accent); }

/* What saves the block, against the right, outside the ruled area — it acts on the
 * rows above rather than being one of them. */
.saving {
  display: flex;
  justify-content: flex-end;
  padding-top: 1.125rem;
}

/* A second block on the same screen gets air over its heading. It always follows the
 * thing that saved the block before it, which is what this leans on rather than a
 * class saying "the later one". */
.saving + .part { margin-top: 2.75rem; }

/* Where the listens go, under the switch that governs them. The same air as between
 * any two blocks of a screen, and a word of its own rather than `form + .parted`,
 * which would reach every form on the panel that happens to be followed by a heading
 * with an action on its line. */
.whither { margin-top: 2.75rem; }

/* What is under that heading: the destinations, or the band saying there are none.
 * The air belongs to this box and not to the band, because `.nothing` is worn on
 * seven screens and the gap this needs is the one the list of destinations already
 * takes over its own first rule — the block begins in the same place whether it has
 * destinations or not. Where the sentence explaining the block sits above it, that
 * sentence's descenders were against the band's top rule.
 *
 * The list keeps its own margin and the two collapse into one, so nothing moved for
 * the case that was already right. */
.sending { margin-top: 1rem; }

/* And the line under the band, which is about the band and not part of it. Said as
 * `.nothing + .hint` rather than on the hint alone: the same words follow the list of
 * destinations nowhere, and a margin on every hint in here would push the ones that
 * belong to a field. */
.sending > .nothing + .hint { margin-top: 1rem; }

/* The form and the rail beside it, and neither of them a column of a grid: the two
 * ask for widths and the row gives what it can. Below about nine hundred pixels of
 * content the rail cannot have its 260 and wraps under the form on its own, which is
 * one line of CSS where a breakpoint would have been a number guessed at.
 *
 * `flex-start` so the rail sits at the top of the form rather than being stretched
 * down beside it. */
.two {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 2.5rem 3.5rem;
}

.two > .forms { flex: 3 1 26.25rem; min-width: 0; }

/* Under a band of figures it stands as far off as the two lists on the access screen
 * do: the figures are about the account and the form changes it, and read together
 * they came out as one block with a rule through it. Only under the figures — where a
 * screen starts with its title, the title's own margin has already said it. */
.counts + .two { margin-top: 2.75rem; }

/* Narrower than the form and capped, because everything in it is a short label and a
 * shorter value. */
.rail {
  flex: 1 1 16.25rem;
  min-width: 0;
  max-width: 20rem;
}

/* And uncapped once it is no longer beside the form.
 *
 * The cap is about sharing a line. Wrapped underneath, it turned into a column of
 * short labels down the left of a phone with a band of nothing to its right, which
 * is the cap doing the opposite of what it was for.
 *
 * The width is worked out rather than picked. The pair wraps when the column holding
 * it drops below 26.25 + 3.5 + 16.25 = 46rem, which with the sidebar's 15rem and the
 * page's own padding is a screen of about 68rem. And the cap only ever bites above
 * about 83rem, where the rail's quarter of the free space first exceeds 20rem — so
 * anywhere between those two says the same thing, and 72rem is the round number in
 * the middle. */
@media (max-width: 72rem) {
  .rail { max-width: none; }
}

/* Inside the pair the form's own columns tighten: it has less room than it had with
 * the screen to itself, and a label column still asking for 200 would push every
 * field into wrapping under it. */
.two .settings {
  max-width: none;
}

.two .settings > .setting {
  padding: 1.125rem 0;
}

.two .settings > .setting > :first-child { flex: 1 1 10.625rem; }
.two .settings > .setting > :last-child { flex: 2 1 16.25rem; }

/* A list of facts is a list of facts wherever it is: see `.facts`. What is left here
 * is that the rail's lists are blocks of their own, closed at the top, and set a
 * size down because a rail is narrower than a screen. */
.rail .facts {
  border-top: 1px solid var(--line);
  font-size: var(--control);
}

.rail .part { margin-top: 2rem; }
.rail .part:first-child { margin-top: 0; }

/* The way to the screen this one only summarises. */
.rail .onward {
  margin: 0.75rem 0 0;
  font-size: var(--control);
}

/* No underline, pointed at or not. A link here is said with the accent, and the
 * underline is what the browser draws when nobody has decided anything — the same
 * reason the actions in a library's row lost theirs. */

/* On a phone. Sixteen pixels is not a taste: anything smaller and iOS zooms the page
 * when the field takes focus, and a page that zooms on focus is a page that has to be
 * pinched back afterwards. The rest is thumbs — every target at least 44 pixels — and
 * the label always above its field, since there is no width for it to sit beside.
 */
@media (max-width: 40rem) {
  .settings input:not([type="checkbox"]), .settings select {
    padding: 0.5625rem 0;
    font-size: var(--name);
  }

  .settings > .setting { padding: 1rem 0; }

  .setting .checkbox, .saving .pill, .rail .onward a {
    min-height: 2.75rem;
  }

  .setting .checkbox { align-items: center; }
  .saving .pill { padding: 0 1.25rem; }

  /* The rail follows the form in the order it is written, so there is nothing to
   * reorder: what it needs is the air that the two columns gave it. */
  .rail { margin-top: 0.5rem; }
}

.setting label, .setting > span:first-child { color: inherit; font-size: inherit; }

/* A row of words where exactly one is in force. It was a row of buttons: three
 * boxes to say what three words say, and the boxes were the loudest thing on a
 * screen whose whole job is three quiet choices. */
.options {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  font-size: var(--control);
}

.option {
  margin: 0;
  padding: 0 0 0.125rem;
  border: none;
  color: var(--quiet);
  font: inherit;
  font-size: var(--control);
  cursor: pointer;
}

.option:hover { color: var(--ink); }

/* Underlined rather than filled, and in the accent — which on this screen is also
 * a preview of the colour underneath it. */
.option.chosen {
  border-bottom: 1px solid var(--accent);
  color: var(--accent);
}

/* Wide enough for the longest language and no wider. A control that stretches to
 * the width of the screen promises a value as long as the screen. */
select.narrow { width: 12.5rem; }

.setting .hint { margin: 0.5rem 0 0; }

/* The blocks of the settings screen — the one screen made of several runs of rows
 * that have nothing to do with one another, and so the one that would otherwise
 * leave a wide empty band beside a single narrow column. Tracks that ask for 420
 * pixels each: two columns where there is room for two, one where there is not,
 * and no width in a media query. */
.knobs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(26.25rem, 100%), 1fr));
  gap: 2.75rem 3.5rem;
}

/* A run inside a column is as wide as its column. The cap a run carries is for a
 * form standing on its own, and here it would leave the two columns different
 * widths on a wide screen. */
.knobs .settings { max-width: none; }

/* Blocks that share a column because neither fills one: two headings over one row
 * apiece, standing off each other by what the grid puts between its own rows.
 * `start` so they sit at the top of the column rather than being spread down the
 * height of the tallest one beside them. */
.stack {
  display: grid;
  align-content: start;
  gap: 2.75rem;
}

/* A tick and the value it turns on, on one line: neither says anything without the
 * other. A schedule with no hour is not a schedule, and an hour with the tick off
 * is a note about what would happen. */
.beside {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
}

/* An hour is four characters and a figure, so it is as wide as it is and set in
 * the face figures line up in. After the run's own field rule rather than before
 * it: the two selectors weigh the same and the later one is the one that wins. */
.settings input[type="time"] {
  width: auto;
  font-family: ui-monospace, monospace;
  font-size: var(--control);
}

/* Switched off it goes quiet rather than away: what it holds is what would happen
 * if the tick came back. */
.settings input[type="time"]:disabled { opacity: 0.5; }

/* The phrase the middle answer needs, on the line under the three answers, with
 * the field set into it rather than standing in front of a unit. Baselines, so the
 * box sits on the same line the words do. */
.hint.after {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.375rem;
}

/* Room for three digits and no more. Centred because it is read as part of a
 * sentence, and in figures that line up because the number under it changes as it
 * is typed. */
.settings input.count {
  width: 3em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* --- the jobs on the maintenance screen ---------------------------------- */

/* Two groups of jobs, in tracks of their own: a wide screen sets them side by
 * side instead of stretching one column across fourteen hundred pixels, and a
 * narrow one puts the second group under the first with its rows still together.
 *
 * The groups are what the grid lays out rather than the rows themselves. With
 * four rows in one grid the columns would be filled across — clear, compact,
 * covers, check — and the pairs that belong together would end up in different
 * columns. */
/* Two groups where there is room for two and one where there is not. This is the
 * grid whose bare floor put every run button off the side of a phone, which is what
 * the note at the head of this sheet was written from. */
.chores {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(27.5rem, 100%), 1fr));
  gap: 0 3.5rem;
}

/* The rule across the top belongs to the group; every row closes itself. */
.chores > .group { border-top: 1px solid var(--line); }

.chore {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
  padding: 1.25rem 0;
  border-bottom: 1px solid var(--line);
}

.chore > div { min-width: 0; }

.chore .what { display: block; }

/* What the job will do, with the number it will do it to already in the
 * sentence. `pretty` so a three line paragraph does not end on one word. */
.chore .why {
  display: block;
  margin-top: 0.125rem;
  font-size: var(--aside);
  color: var(--quiet);
  text-wrap: pretty;
}

/* When it last ran and what it found then. In tabular figures, since these are
 * read down the column as much as across the row. */
.chore .ran {
  display: block;
  margin-top: 0.375rem;
  font-size: var(--aside);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* What a check found, or what stopped a job: prose rather than a figure, and as
 * many lines as it has. */
.chore .wrong {
  display: block;
  margin-top: 0.375rem;
  font-size: var(--aside);
  color: var(--alarm);
  white-space: pre-line;
}

/* The same word on every row, so it never wraps and never changes width enough
 * to make the column jump. */
.chore .pill { white-space: nowrap; }

/* Every block after the jobs stands off them the way any second block stands off
 * the first.
 *
 * Said as the headings that follow the set, like the one that follows a Save, and
 * for the same reason beyond the meaning: `.part` sets `margin` as a shorthand
 * further down this sheet, so a rule of one class would be overwritten by it
 * whatever this one asked for. Two classes outweigh it and the order stops
 * mattering.
 *
 * A general sibling and not the next one. It was `+` while the history was the only
 * thing under the jobs, and the day a second block landed between them the history
 * lost its air without anything saying so — the rule still matched something, just
 * not the thing it was written for. */
.chores ~ .part { margin-top: 2.75rem; }

/* --- the files a scan could not account for -------------------------------- */

/* Both lists sit under one heading and each has its own label over it, so the
 * labels need to stand off what came before them without standing off as far as a
 * new section would. */
.lettering + .quiet { margin-bottom: 0.75rem; }

/* The second label, which is the only one that needs saying. The first sits under
 * the section's lead and takes that paragraph's own space; this one sits against
 * the bottom rule of the list above it, which is a line and gives nothing. What is
 * above it is the tail of the first block either way — the list itself, or the line
 * counting what did not fit. */
.shut + .lettering,
.elsewhere + .lettering { margin-top: 2rem; }

/* The unreadable: a path, what it weighs, and why it will not open.
 *
 * Two lines and not three columns. The path is the long one and the only one worth
 * reading first, so it takes the line; the size rides at its end, where it is a
 * footnote to it rather than a column of its own; and the reason goes underneath,
 * because it is a sentence and sentences do not fit in cells. */
.shut {
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

.shut > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0 1rem;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--line);
}

/* Monospace, because this is a path somebody is about to compare against one in a
 * terminal, and a proportional font makes two similar paths look alike. */
.shut .path {
  font-family: var(--mono);
  overflow-wrap: anywhere;
}

.shut .weighs {
  color: var(--quiet);
  font-size: var(--aside);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.shut .wrong {
  grid-area: 2 / 1 / 2 / 3;
  margin-top: 0.25rem;
  font-size: var(--aside);
  color: var(--alarm);
}

/* The missing: what it is, when it went, and what it holds. Three columns, and the
 * first one takes whatever is left because it is three lines of text and the other
 * two are a phrase each. */
.astray {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The width the three columns need goes on the rows and never on the list, which
 * is the same rule the roster follows and for a reason worth writing down: a list
 * that is itself too wide makes the box around it too wide, and a box that cannot
 * shrink stops being a scroll box and starts being a page that scrolls. Everything
 * to the right of it on the screen goes with it, which is how the run buttons four
 * rows up ended up past the edge. */
.astray > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 9rem minmax(11rem, auto);
  gap: 1rem;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--control);
  min-width: 41rem;
}

/* The heading row is the same grid so the columns line up, and quiet and small so
 * it reads as a label rather than as the first row. */
.astray .astray-head {
  padding: 0.5rem 0;
  border-top: 1px solid var(--line);
  color: var(--quiet);
  font-size: var(--aside);
}

.astray .what { display: grid; }

.astray .calling { font-weight: 500; }

.astray .by,
.astray .path {
  font-size: var(--aside);
  overflow-wrap: anywhere;
}

.astray .when {
  display: grid;
  color: var(--quiet);
  white-space: nowrap;
}

/* A row the server is about to clear out by itself. The reader can act before it
 * does, which is the only reason to say it at all. */
.astray .soon {
  font-size: var(--aside);
  color: var(--alarm);
}

.astray .holds { font-size: var(--aside); }

/* How many did not fit, and the line about a collection that was moved. Both are
 * afterwords to the list above them rather than parts of it. */
.elsewhere,
.moved {
  margin-top: 0.75rem;
  font-size: var(--aside);
}

/* Narrow enough that three columns cannot be read side by side, which is the same
 * width at which the roster gives its own up and for the same reason: the scroll
 * box is there so nothing breaks, not so that it gets dragged.
 *
 * What is left is the track, whole, and one line under it holding the two facts the
 * columns held. Nothing is dropped — when it went and what it holds are the two
 * things somebody is here to weigh, and a narrow screen is not a reason to make
 * them tap through to either. */
@media (max-width: 65rem) {
  /* Headings over columns that are no longer there. */
  .astray .astray-head { display: none; }

  .astray > li {
    min-width: 0;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.375rem 0.875rem;
  }

  .astray .what { grid-area: 1 / 1 / 1 / 3; }

  /* Side by side on the second line rather than stacked, so the row stays a row.
   * When it went comes first because it is the shorter of the two and the one that
   * is the same shape on every line. */
  .astray .when { grid-area: 2 / 1; }

  .astray .holds { grid-area: 2 / 2; }
}

/* What has been run lately: what happened on the left, when on the right. */
.lately {
  margin: 0.875rem 0 0;
  padding: 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

.lately > li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--control);
}

.lately .when {
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The running order of a record. Its own thing rather than the track listing's rows:
 * that one is a table of five columns being read down one of them, and this is a
 * numbered list being read in order. Bled by the same twelve pixels every other run of
 * rows in these panels is. */
.running { margin: 0 -0.75rem; }

.running > div {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr) auto;
  gap: 0.875rem;
  align-items: baseline;
  padding: 0.625rem 0.75rem;
  border-top: 1px solid var(--line);
  cursor: pointer;
}

.running > div:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }

.running > div:last-child { border-bottom: 1px solid var(--line); }

.running .what {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.running .figure {
  font-size: var(--aside);
  font-variant-numeric: tabular-nums;
  color: var(--quiet);
}

/* The one that is sounding says so across the whole row, like a track listing's does. */
.running .sounding, .wearing .sounding { color: var(--accent); }

.running .sounding .figure, .wearing .sounding .figure { color: inherit; }

/* A file that has gone stays in the order — this is where somebody comes to find out
 * what is gone — and says so by going quiet rather than by leaving a gap. */
.running .gone, .wearing .gone { opacity: 0.55; }

/* Which disc the rows under it are from, on a record that came on more than one. Not a
 * `lettering` heading: those name a section of the panel, and this names a run of rows
 * inside one. */
.disc {
  margin: 1.125rem 0 0.25rem;
  font-size: var(--aside);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

/* Everybody credited on a record's tracks. Pills because it is a set with no order
 * worth reading and no figures against it — a list of names down the panel would look
 * like rows that had lost their second column. */
.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

/* Each of them opens that artist, so each is a button rather than a word: a guest on
 * a record is the name somebody is most likely to want to follow. */
.pills > button {
  padding: 0.25rem 0.6875rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--aside);
  cursor: pointer;
}

/* Its edge, the way every other pill in the panel answers being pointed at — and no
 * underline, which is what the shared class does in a line of running text. A shape
 * with an edge has somewhere better to put the answer. */
.pills > button:hover {
  border-color: var(--accent);
  color: var(--accent);
  text-decoration: none;
}

/* An artist's most played songs, and their records. Two runs of rows in one panel that
 * both read as "a thing, and a figure or two about it", so they share their shape and
 * differ only in what the first column is: nothing at all, or a cover. */
.charted > div, .discography > div, .wearing > div {
  display: grid;
  gap: 0.875rem;
  align-items: center;
  padding: 0.6875rem 0.75rem;
  border-top: 1px solid var(--line);
  cursor: pointer;
}

.charted, .discography, .wearing { margin: 0 -0.75rem; }

.charted > div { grid-template-columns: minmax(0, 1fr) auto auto; }

.discography > div { grid-template-columns: 2.75rem minmax(0, 1fr) auto; }

/* What it is and how long it runs, and nothing between them: these songs share only
 * the word, so there is no third column of anything they have in common. */
.wearing > div { grid-template-columns: minmax(0, 1fr) auto; }

.charted > div:hover, .discography > div:hover, .wearing > div:hover {
  background: color-mix(in srgb, var(--ink) 3%, transparent);
}

.charted > div:last-child, .discography > div:last-child, .wearing > div:last-child {
  border-bottom: 1px solid var(--line);
}

/* What it is, over what it is off. Two lines in one cell rather than two cells, because
 * they are one label for one thing. */
.charted .what, .discography .what, .wearing .what {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.charted .by, .discography .by, .wearing .by {
  display: block;
  overflow: hidden;
  font-size: var(--aside);
  color: var(--quiet);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.charted .figure, .discography .figure, .wearing .figure {
  font-size: var(--aside);
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* The cover at the size a row has room for, and the empty frame where there is none. */
.discography .art {
  width: 2.75rem;
  height: 2.75rem;
}

.discography .art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.discography .art .icon {
  width: 1.25rem;
  height: 1.25rem;
}

/* --- what is sounding ----------------------------------------------------- */

/* At the foot of the column rather than across the bottom of the window, on the
 * principle the whole column runs on: everything permanently live is in the left
 * column and the screen keeps its full height. Three rows — what it is, where it has
 * got to, and the controls. */
.dock {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1.125rem 1.625rem;
}

/* The cover, and the two lines beside it. `min-width: 0` on both, or a long title
 * pushes the art off the column instead of ending in an ellipsis. */
.dock .sounding {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  min-width: 0;
}

.dock .art {
  flex: none;
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 3px;
}

/* The cover leads to the cover on its own, and the cursor is the whole of what says
 * so — no line lighting up under the pointer, here or on the two lines beside it.
 * The player is three things in a corner of the column and nothing in it should
 * change colour for being passed over.
 *
 * A button around the picture rather than a pressable span, because there is nothing
 * wrapping it here to conflict with: what a button gives for nothing is the keyboard,
 * which the span in a drawer's heading had to be handed a key listener to have. */
.dock .see-art {
  display: grid;
  flex: none;
  padding: 0;
  border: none;
  cursor: zoom-in;
}

.dock .art .icon {
  width: 1.1875rem;
  height: 1.1875rem;
}

.dock .what {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.3;
  font-size: var(--control);
}

/* Both lines give way rather than wrap: the column is fifteen rem wide and a title
 * that wrapped onto three lines would push the controls about. */
.dock .what > * {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Both lines are buttons now, and a button's label is centred by the browser. This
 * column is read down its left edge, title against credit, so they are aligned and
 * not balanced. */
.dock .what > button { text-align: left; }

/* The words that were already there, made pressable and left looking exactly as they
 * did: the title and the credit, in the column and on the phone alike.
 *
 * Deliberately not `.toward`, which is what a name in a credit wears in a drawer and
 * which underlines itself under the pointer. There it earns the mark — a line reading
 * "Above & Beyond feat. Zoë Johnston" has to say which stretch of the sentence leads
 * anywhere, and each stretch leads somewhere different. Here the line is pressed
 * whole, so there is nothing to pick out of it, and a title has never been underlined
 * anywhere in this panel. The cursor says it can be pressed and nothing else needs to.
 *
 * No `font` here, so a button keeps arriving at the size of a control: that is what
 * the title in the column should be, and it leaves the credit under it the smaller
 * size and the quieter ink that `.dock .by` gives it. The phone sets both lines
 * itself, which is where the font is inherited instead. */
.dock .what > .bare, .sheet-player .naming .bare {
  display: inline;
  padding: 0;
  border: none;
  background: transparent;
}

.dock .by {
  color: var(--quiet);
  font-size: var(--lettering);
}

/* Elapsed, the line, and the whole. The same 2px idiom as the scan and the process
 * bars, so nothing new enters the design. */
.dock .along {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--quiet);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
}

/* Where in the track we are, and the way to be somewhere else. The same control in
 * all three views, differing only in how thick its line is drawn.
 *
 * The line is on the wrapper and the control is a transparent `range` input over it:
 * `::before` does nothing on an input, which is a replaced element. That split is
 * also what makes it comfortable to hit — the line is two pixels and the thing you
 * press is sixteen.
 *
 * Two pixels, and in `rem` unlike every other line in this sheet: a hairline divides
 * and has to stay one physical pixel, but this measures, and what measures should
 * grow with the text it measures for. */
.along-track {
  position: relative;
  display: block;
  flex: 1;
  height: 1rem;
  cursor: pointer;
}

.along-track::before {
  content: "";
  position: absolute;
  top: 50%;
  right: 0;
  left: 0;
  height: 0.125rem;
  transform: translateY(-50%);
  /* Filled to `--at`, which whichever view drew this sets on the wrapper. One
   * gradient rather than a second element, so there is nothing between the line and
   * the input that could swallow a press. */
  background: linear-gradient(to right, var(--accent) var(--at), var(--line) var(--at));
}

.along-track input {
  appearance: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
}

/* No handle at rest: the line says where you are and a permanent dot on it is
 * furniture. It appears under the pointer and on focus, which is where somebody
 * needs to know the thing can be moved — and it is what an arrow key moves. */
.along-track input::-webkit-slider-thumb {
  appearance: none;
  width: 0.625rem;
  height: 0.625rem;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
}

.along-track input::-moz-range-thumb {
  width: 0.625rem;
  height: 0.625rem;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
}

.along-track:hover input::-webkit-slider-thumb,
.along-track input:focus-visible::-webkit-slider-thumb { opacity: 1; }

.along-track:hover input::-moz-range-thumb,
.along-track input:focus-visible::-moz-range-thumb { opacity: 1; }

.along-track input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.dock .transport {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Back and forward, quiet until they are wanted. */
.dock .step {
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  cursor: pointer;
}

.dock .step:hover { color: var(--ink); }

.dock .step .icon {
  width: 1.0625rem;
  height: 1.0625rem;
}

/* The one control that is a shape rather than a glyph on its own: it is the thing
 * somebody reaches for without looking, so it has an outline to aim at. */
.dock .toggle {
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}

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

.dock .toggle .icon {
  width: 0.9375rem;
  height: 0.9375rem;
}

/* How much is waiting behind this one. Not a button: there is nowhere yet to look at
 * a queue, and the count is worth saying on its own. */
/* No border, which needs saying: every `input`, `select` and `button` in this panel is
 * given one, and this was a `span` until it became the way into the queue. Turning it
 * into a button was enough to have it drawn as a field. */
.dock .queued {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  height: 2rem;
  padding: 0 0.375rem;
  border: none;
  background: transparent;
  color: var(--quiet);
  font-size: var(--lettering);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

.dock .queued .icon {
  width: 0.9375rem;
  height: 0.9375rem;
}

/* The bar across the foot of a phone, and the block in the column, are the same
 * player twice. Each exists only at the width the other does not: the column is a
 * drawer on a phone, and something you are listening to cannot live behind a
 * button. */
.afoot { display: none; }

@media (max-width: 45rem) {
  .dock { display: none; }

  /* Fixed to the window rather than at the end of the page: it has to stay put
   * while a listing of nine hundred tracks goes by underneath. */
  .afoot {
    display: block;
    position: fixed;
    /* What the line above straddles the top edge of. */
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 3;
    border-top: 1px solid var(--line);
    background: var(--paper);
    /* The strip a phone keeps for its own home indicator. Nothing on a machine
     * without one, so this is not a gap invented for every browser. */
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* Straddling the bar's own top edge, so the line reads as drawn on it while what
   * you press reaches eight pixels either side. Absolute, or it would add its
   * sixteen pixels to the height of the bar.
   *
   * Above the button beside it, since half of it overlaps the button's top edge and
   * a press there means the bar and not the sheet. */
  .afoot .along-track {
    position: absolute;
    top: -0.5rem;
    right: 0;
    left: 0;
    z-index: 1;
  }

  .afoot .holding {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.5rem 0.5rem 0.875rem;
  }

  .afoot .art {
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 3px;
  }

  .afoot .art .icon {
    width: 1.25rem;
    height: 1.25rem;
  }

  .afoot .what {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
    line-height: 1.3;
  }

  .afoot .what > * {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .afoot .by {
    color: var(--quiet);
    font-size: var(--aside);
  }

  /* Both 44px, which is the smallest a thumb hits reliably. Two of them and no
   * more: at this size a row of four is a row of mistakes. */
  .afoot .tap {
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ink);
    cursor: pointer;
  }

  .afoot .tap .icon {
    width: 1.125rem;
    height: 1.125rem;
  }

  /* Room under the last row for the bar to sit over nothing. Without it the foot of
   * every listing is behind the player. */
  .body { padding-bottom: 5.5rem; }
}

/* The player at full height, over whatever was on screen.
 *
 * A sheet and not a route, so what is behind it keeps its scroll and its search and
 * closing it puts somebody back where they were. Which is also why it is opaque
 * rather than a dialogue over a scrim: it is not asking anything, it is the same
 * player with room to say more.
 *
 * Three bands: a header that closes it, the middle that grows, and a foot naming the
 * file. Only the middle gives way when the screen is short. */
.sheet-player {
  display: none;
}

@media (max-width: 45rem) {
  .sheet-player {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 5;
    flex-direction: column;
    background: var(--paper);
    padding-bottom: env(safe-area-inset-bottom);
    /* The sheet takes the vertical gesture; there is nothing inside it to scroll. */
    touch-action: none;
  }

  /* Springs back when a push was not far enough, and follows the finger while it is
   * happening. */
  .sheet-player:not(.pushed) { transition: transform 200ms ease-out; }

  .sheet-player > header {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: space-between;
    padding: 0 0.5rem;
  }

  /* Which record it is from, in the small capitals every heading in this panel
   * uses. Centred, which is what the empty third cell of the row is for. */
  .sheet-player .whence {
    overflow: hidden;
    font-size: var(--lettering);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--quiet);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Both ends of the header row, and the two on the bar below: 44px, which is the
   * smallest a thumb hits reliably. */
  .sheet-player .tap {
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ink);
    cursor: pointer;
  }

  /* The chevron this panel already has, turned to point the way the sheet goes. */
  .downward {
    display: inline-flex;
    transform: rotate(90deg);
  }

  .sheet-player .tap .icon {
    width: 1.25rem;
    height: 1.25rem;
  }

  /* A width container, so the room for the cover below can start out as wide as this
   * is. Padded at the foot so that where the space is tight the round button does
   * not come to rest against the line over the foot. */
  .sheet-player .middle {
    display: flex;
    flex: 1;
    min-height: 0;
    flex-direction: column;
    padding: 0.5rem 1.75rem 1.25rem;
    container-type: inline-size;
  }

  /* The room the cover has: as tall as the sheet is wide, and less where the lines and
   * the controls under it would not otherwise fit. It gives way and never grows, so
   * everything else on the sheet is always on it and the slack of a tall screen is
   * still left under the controls.
   *
   * It has to be this way round because the sheet cannot scroll and must not: it
   * keeps the vertical gesture for itself, which is what pushing it down to close it
   * is, and a window no taller than it is wide — 720 by 890 — used to put the
   * transport under a cover as wide as the screen, out of reach and with nothing
   * that could bring it back.
   *
   * A size container, so the cover can ask how much room it was left both ways. */
  .sheet-player .cover-room {
    display: flex;
    flex: 0 1 100cqw;
    justify-content: center;
    min-height: 0;
    container-type: size;
  }

  /* The cover at the size a cover is worth looking at: the whole width where the
   * screen is tall enough, and square to whatever height is left where it is not. */
  .sheet-player .art {
    width: min(100cqw, 100cqh);
    height: auto;
    aspect-ratio: 1;
    border-radius: 6px;
  }

  .sheet-player .art .icon {
    width: 4rem;
    height: 4rem;
  }

  .sheet-player .naming { margin-top: 1.875rem; }

  /* A heading rather than a line, and allowed two of them: a title is the one thing
   * on this screen worth wrapping for. */
  .sheet-player h1 {
    margin: 0 0 0.25rem;
    font-size: 1.5rem;
    text-wrap: pretty;
  }

  .sheet-player .naming p { font-size: var(--name); }

  /* Both lines are buttons, and a button arrives at the size of a control. Here the
   * heading and the name above and below it are set by the two rules just above, so
   * what the button has to do is take the size of whatever it is inside. */
  .sheet-player .naming .bare { font: inherit; }

  .sheet-player .along { margin-top: 1.625rem; }

  /* Thicker than on the bar, because here it is the thing being read rather than a
   * mark on an edge — and taller to press, since this is the view somebody opened in
   * order to move about in a track. */
  .sheet-player .along-track { height: 1.5rem; }

  .sheet-player .along-track::before { height: 0.1875rem; }

  .sheet-player .clock {
    display: flex;
    justify-content: space-between;
    padding-top: 0.5rem;
    color: var(--quiet);
    font-size: var(--aside);
    font-variant-numeric: tabular-nums;
  }

  .sheet-player .transport {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2.125rem;
    margin-top: 1.25rem;
  }

  .sheet-player .step {
    width: 3.25rem;
    height: 3.25rem;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ink);
    cursor: pointer;
  }

  /* The one control somebody reaches for without looking, so it is the one with a
   * shape to aim at and the size to match. */
  .sheet-player .toggle {
    width: 4.5rem;
    height: 4.5rem;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 50%;
    background: transparent;
    color: var(--ink);
    cursor: pointer;
  }

  .sheet-player .step .icon, .sheet-player .toggle .icon {
    width: 1.625rem;
    height: 1.625rem;
  }

  /* The way into the queue on the left, what the file is on the right. */
  .sheet-player .foot-note {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin: 0 1.75rem;
    border-top: 1px solid var(--line);
    font-size: var(--aside);
    font-variant-numeric: tabular-nums;
  }

  /* Everything on the bar but its two buttons opens the sheet. A button of its own
   * would be a fourth target on a bar with room for three. */
  .afoot .most {
    display: flex;
    flex: 1;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
    padding: 0;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
  }
}

/* --- what is coming after this -------------------------------------------- */

/* A drawer down the right of a window, and the whole of a phone's screen. One element
 * laid out twice rather than two, because it holds the same list either way.
 *
 * The one raised surface outside a dialogue, and for the same reason a dialogue is
 * one: it sits over the screen rather than in it, so it needs an edge and a shadow to
 * say so. Narrower than the drawers the collection will open — 440px against 520 —
 * because the rows here are a title and a length. */
.queue {
  display: flex;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 6;
  flex-direction: column;
  width: min(27.5rem, 100%);
  border-left: 1px solid var(--line);
  background: var(--paper);
  box-shadow: -24px 0 60px light-dark(rgb(0 0 0 / 18%), rgb(0 0 0 / 55%));
}

/* Fixed while the body between them scrolls, so the count and the way out stay put
 * however long the queue is. */
.queue > header {
  display: flex;
  flex: none;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.375rem 1.625rem 1.125rem;
  border-bottom: 1px solid var(--line);
}

.queue h2 {
  font-size: 1.25rem;
  font-weight: 400;
  letter-spacing: -0.01em;
}

.queue > header p {
  margin-top: 0.25rem;
  font-size: var(--aside);
  font-variant-numeric: tabular-nums;
  text-wrap: pretty;
}

.queue .tap {
  display: grid;
  flex: none;
  gap: 0;
  place-content: center;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  margin: -0.5rem -0.75rem 0 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  cursor: pointer;
}

.queue .tap:hover { color: var(--ink); }

.queue .tap .icon {
  width: 1.125rem;
  height: 1.125rem;
}

.rolling {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 1.625rem 1.625rem;
}

/* The small capitals every run of rows in this panel is headed with. What they all
 * share is in `.lettering` itself; this is only how much air this one wants. */
.rolling .lettering { margin: 1.25rem 0 0.25rem; }

/* Air over the second heading, so the two runs read as two rather than as one list
 * with a word in the middle of it. And a row rather than a line, because on a phone
 * this is what carries the way to mix what is under it. */
.rolling .next {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 1.75rem;
}

/* Mixes what is coming, and puts it back. Quiet like everything else that only offers
 * something, and in the accent while what is coming is mixed — the same way the count
 * in the sidebar says the queue is open.
 *
 * It undoes the small capitals of the heading it shares a line with. Wearing them it
 * read as a second word of that heading; in its own voice it reads as the control it
 * is, which is what the line needs at that end. */
.mix {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 0.4375rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--aside);
  letter-spacing: normal;
  text-transform: none;
  cursor: pointer;
}

.mix .icon {
  width: 1.0625rem;
  height: 1.0625rem;
}

.mix:hover, .mix:focus-visible { color: var(--accent); }

.mix:focus-visible { outline: none; }

.mix.mixing { color: var(--accent); }

.rolling .waiting {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* What is sounding and what is waiting share their shape; only the last cell differs,
 * and the row that is playing takes the accent because it is the same row the
 * listings colour. */
.rolling .row, .rolling .waiting > li {
  display: grid;
  grid-template-columns: 1.125rem minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--line);
}

/* The row that is sounding has no handle and nothing to drop it with, so its first and
 * last cells stand empty rather than its columns being different ones: the two runs
 * have to line up down the drawer. */
/* Hands the browser the vertical scroll and keeps the horizontal for the swipe, which
 * is what stops a list from feeling like it is fighting the finger. Declared here and
 * not in the handler, because a default the browser has already committed to cannot be
 * taken back. */
.rolling .waiting > li {
  touch-action: pan-y;
  will-change: transform;
}

/* While a gesture is running the row follows the finger, so no transition; when it
 * ends the offset goes back to nought and this is what makes it spring rather than
 * jump. */
.rolling .waiting > li:not(.sliding, .lifted) {
  transition: transform 160ms ease-out, opacity 160ms ease-out;
}

/* Lifted off the list while it is being moved, so it is plainly the thing travelling
 * and not the list scrolling under it. */
.rolling .lifted {
  position: relative;
  z-index: 1;
  background: var(--paper);
  box-shadow: 0 6px 20px light-dark(rgb(0 0 0 / 16%), rgb(0 0 0 / 45%));
}

/* What the row is dragged by, and the only part of it that takes a vertical gesture —
 * `none` keeps both axes here, where everywhere else on the row the vertical one is
 * the list being scrolled.
 *
 * `--quiet` and never `--line`: that is the hairline token, and a handle drawn in it
 * disappears against the paper it sits on. */
.rolling .handle {
  display: grid;
  gap: 0;
  place-content: center;
  place-items: center;
  width: 1.125rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  touch-action: none;
  cursor: grab;
}

.rolling .handle:active { cursor: grabbing; }

.rolling .handle .icon {
  width: 1.125rem;
  height: 1.125rem;
}

.rolling .now { color: var(--accent); }

/* The glyph in the column a waiting row keeps its handle in.
 *
 * The same size as the handle's own glyph, and centred in the column. Both of those are
 * needed and for different reasons. The handle is a button that fills the column and
 * centres its glyph inside itself, while this is a bare span placed straight into the
 * grid — and a grid item with a width of its own sits at the *start* of its track, not
 * in the middle of it, so at 14px against an 18px column it stood two pixels left of the
 * grip above it. Matching the size hides that at this width and nowhere else: on a phone
 * the column grows to 28px and the handle grows with it, so only `justify-self` keeps the
 * two in line there. */
.rolling .now .icon {
  justify-self: center;
  width: 1.125rem;
  height: 1.125rem;
}

/* Two lines that both give way rather than wrap: a drawer is narrow and a queue is
 * read down its left edge. */
.rolling .what {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.3;
}

.rolling .what > * {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rolling .by {
  color: var(--quiet);
  font-size: var(--aside);
}

.rolling .now .by { color: inherit; }

.rolling .figure {
  color: var(--quiet);
  font-size: var(--aside);
  font-variant-numeric: tabular-nums;
}

.rolling .now .figure { color: inherit; }

/* Takes a track out of what is coming. Quiet until the row is under the pointer,
 * because a column of crosses down a queue reads as a list of things to undo. */
.rolling .drop {
  display: grid;
  gap: 0;
  place-content: center;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  opacity: 0;
  cursor: pointer;
}

.rolling .waiting > li:hover .drop, .rolling .drop:focus-visible { opacity: 1; }

.rolling .drop:hover { color: var(--alarm); }

.rolling .drop .icon {
  width: 0.875rem;
  height: 0.875rem;
}

/* Lit while the queue is open, so the button says where the thing on screen came
 * from. */
.dock .queued.showing { color: var(--accent); }

@media (max-width: 45rem) {
  /* The whole screen rather than a drawer, and no scrim: there is nothing beside it
   * to see. This is the sheet's second page, so it comes over the sheet. */
  .queue {
    inset: 0;
    z-index: 7;
    width: 100%;
    border-left: none;
    box-shadow: none;
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* Everything the phone's own bar and sheet already gave 44px targets keeps them
   * here: this is the same thumb. The handle is the one that needs it most — it is
   * what a row is held by, and holding is harder to aim than tapping. */
  .rolling .drop {
    width: 2.75rem;
    height: 2.75rem;
    opacity: 1;
  }

  .rolling .handle {
    width: 1.75rem;
    height: 2.75rem;
  }

  .rolling .row, .rolling .waiting > li {
    grid-template-columns: 1.75rem minmax(0, 1fr) auto auto;
  }

  /* And the same for the one control on the heading, which is a word and an icon and
   * would otherwise be twenty pixels of target. It grows into the air around the
   * heading instead of pushing it about: the row keeps the height it had and the
   * thumb gets the 44px everything else here has. */
  .mix {
    margin-block: -0.75rem;
    padding-block: 0.75rem;
  }
}

/* The way into the queue, at the foot of the opened player. */
.into-queue {
  display: inline-flex;
  align-items: center;
  gap: 0.5625rem;
  height: 2.75rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--control);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

.into-queue .icon {
  width: 1.125rem;
  height: 1.125rem;
}

/* Lifted into the accent rather than ringed. Nothing in this player is a rectangle
 * with a line round it — the only bordered control is the round one that starts the
 * music — so the ring every button in the panel gets on focus read as a box that had
 * appeared out of nowhere. The colour is the signal instead, and it answers to the
 * pointer as well as to the keyboard, so it says the thing is pressable before it is
 * pressed. */
.into-queue:hover, .into-queue:focus-visible { color: var(--accent); }

.into-queue:focus-visible { outline: none; }

/* The same on the sidebar's own count, which until now only lit once the queue was
 * already open. */
.dock .queued:hover, .dock .queued:focus-visible { color: var(--accent); }

.dock .queued:focus-visible { outline: none; }

/* What is waiting past the last row drawn. Centred and quiet: it is not a row and does
 * not pretend to be one — it is the list saying where it stops. */
.beyond {
  padding: 1.125rem 0 0.25rem;
  font-size: var(--aside);
  text-align: center;
  text-wrap: pretty;
}

/* --- more about the thing you pressed ------------------------------------- */

/* A panel down the right of the window, over what you were looking at rather than
 * instead of it: the list keeps its place and the search keeps its words, which
 * matters because looking at one track out of nine hundred is something people do
 * several times in a row.
 *
 * The second raised surface in the panel, and it takes its edge and its shadow from
 * the first — the queue is the same thing in a narrower width, and the two are never
 * on screen together. Wider than that one, 520px against 440, because these hold a
 * name against a value and a path is as long as it likes. */
.drawer {
  display: flex;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 7;
  flex-direction: column;
  width: min(32.5rem, 100%);
  border-left: 1px solid var(--line);
  background: var(--paper);
  box-shadow: -24px 0 60px light-dark(rgb(0 0 0 / 18%), rgb(0 0 0 / 55%));
}

/* Catches a press anywhere else, which is a way out that needs no aiming — and it
 * needs one most where the panel is at its widest and the close button at its
 * furthest. Tinted, unlike the invisible veil a menu closes on: this dims what it is
 * covering, because what is covering it is a whole panel and not a short list. */
.scrim {
  position: fixed;
  inset: 0;
  z-index: 6;
  background: light-dark(rgb(20 20 20 / 22%), rgb(0 0 0 / 45%));
}

/* Fixed while the body between them scrolls, like the queue's. */
.drawer > header {
  display: flex;
  flex: none;
  align-items: flex-start;
  gap: 1rem;
  padding: 1.5rem 1.75rem 1.25rem;
  border-bottom: 1px solid var(--line);
}

/* What kind of thing this is, in a frame rather than as a bare glyph: it is the one
 * mark on the panel and a 22px icon on its own at this size reads as a stray. */
.emblem {
  display: grid;
  flex: none;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border: 1px solid var(--line);
  color: var(--quiet);
}

/* Round for the one of these that is a person. */
.emblem.round { border-radius: 50%; }

.emblem .icon {
  width: 1.375rem;
  height: 1.375rem;
}

/* The record's own cover where there is one, filling the frame the glyph sat in the
 * middle of. Cropped rather than fitted: sleeves are square and the ones that are not
 * are better cropped than letterboxed inside a hairline. */
.emblem .art {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.drawer > header > div {
  flex: 1;
  min-width: 0;
}

.drawer h2 {
  font-size: 1.25rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}

/* One line, clipped: who made it and what record it is off, which on a compilation
 * is longer than any panel. */
.drawer > header p {
  overflow: hidden;
  margin-top: 0.25rem;
  font-size: var(--aside);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The two pieces of that line that are somewhere to go: the artist and the record.
 *
 * Inline rather than the button's own inline-block, so the line still clips with an
 * ellipsis — a row of blocks is cut off square where the text would have trailed off,
 * and the whole point of that line is that it is allowed to be too long.
 *
 * Not in the accent, which every other link here is: the line is quiet on purpose and
 * a compilation would light three names up in it, over the title it sits under. What
 * says it can be pressed is the underline, which is what a name in a list does. */
.toward {
  display: inline;
  padding: 0;
  border: none;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.toward:hover {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.1875rem;
}

.drawer .tap {
  display: grid;
  flex: none;
  gap: 0;
  place-content: center;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  margin: -0.625rem -0.875rem 0 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  cursor: pointer;
}

.drawer .tap:hover { color: var(--ink); }

.drawer .tap .icon {
  width: 1.125rem;
  height: 1.125rem;
}

/* Three answers from three places, so three tabs rather than one scroll. Outside the
 * scrolling body: which one you are reading must not move while you read it. */
.tabs {
  display: flex;
  flex: none;
  gap: 1.375rem;
  padding: 0 1.75rem;
  border-bottom: 1px solid var(--line);
}

/* No border of its own, like every other text button in the player and the drawers.
 * What says which one is showing is the accent and the two pixel rule under it —
 * two pixels because it measures rather than divides, the same as the mark on the
 * section you are in. */
.tabs button {
  padding: 0.75rem 0;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--control);
  cursor: pointer;
}

.tabs button:hover { color: var(--ink); }

.tabs .chosen {
  border-bottom-color: var(--accent);
  color: var(--accent);
}

/* Lifted into the accent rather than ringed, like every other borderless control in
 * the player and these panels: a box appearing round a bare word reads as something
 * that had not been there a moment ago. */
.tabs button:focus-visible {
  outline: none;
  color: var(--accent);
}

/* Everything between the head and the foot, and the only part that moves. */
.leafing {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 1.625rem 1.75rem 2rem;
  /* Timed words scroll this box themselves, to keep the line that is sounding in
   * view. How that looks is decided here rather than where it is asked for, so a
   * reader who wants less motion is answered in the one place readers are. */
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  .leafing { scroll-behavior: auto; }
}

/* The small capitals over a run of rows, wherever that run is. Was written inside
 * the queue and needed here word for word, so what every one of them shares is here
 * and what each changes about it is beside it. */
.lettering {
  font-size: var(--lettering);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--quiet);
}

.leafing .lettering { margin: 1.875rem 0 0.125rem; }

/* And not over the first one, which already has the tab strip above it. */
.leafing > .lettering:first-child { margin-top: 0; }

/* A name against a value, spelt out. Not `.facts`, which puts its value hard right
 * and sets it in figures: half of what goes in here is a path or an identifier, and
 * hanging those off the right edge is how a column of them becomes unreadable. So
 * the name gets a column of its own and the value starts where the next one does. */
.spelt {
  margin: 0;
  padding: 0;
  list-style: none;
}

.spelt > div {
  display: grid;
  grid-template-columns: 8.125rem minmax(0, 1fr);
  gap: 1rem;
  padding: 0.625rem 0;
  border-top: 1px solid var(--line);
}

/* The last of a run closes it, so a section reads as a block rather than as rows
 * that stopped. */
.spelt > div:last-child { border-bottom: 1px solid var(--line); }

.spelt dt {
  color: var(--quiet);
  font-size: var(--control);
}

.spelt dd {
  min-width: 0;
  margin: 0;
}

/* An identifier rather than words: an ISRC, a MusicBrainz id, a path. Allowed to
 * break anywhere, because none of them has a space to break at and a path that
 * cannot break is a panel that scrolls sideways. */
.typed {
  font-family: ui-monospace, monospace;
  font-size: var(--aside);
  overflow-wrap: anywhere;
}

/* Every tag in the file, which is a hundred rows of names nobody reads unless they
 * came looking for one. Monospaced throughout, because both halves are what the file
 * literally says.
 *
 * The name column is as wide as the widest name and no wider. It cannot be a measure:
 * ID3v2 names its frames in four characters and Vorbis writes
 * `REPLAYGAIN_TRACK_GAIN`, so anything fixed enough for one leaves the other with a
 * column of air where the values should be — which is what a set width did, putting
 * `TIT2` against the middle of the panel.
 *
 * `auto` alone would not do it either: the grid is each row, so every row would size
 * its own column and the names would come out ragged. So the list is the grid, the
 * rows borrow its columns with `subgrid`, and the width is settled once across all of
 * them. The gutter moves to the list for the same reason — in a subgridded axis the
 * gutters are the parent's. */
/* Capped, because a name can be arbitrarily long and this column is shared: real
 * files carry `MusicBrainz Album Release Country` beside `TIT2`, and the widest name
 * is what every row's column is sized to. Past the cap a long name wraps — which
 * costs one row two lines — rather than every value in the list being squeezed into
 * what a name left over. */
.frames {
  display: grid;
  grid-template-columns: fit-content(35%) minmax(0, 1fr);
  column-gap: 0.875rem;
}

.frames > div {
  grid-column: span 2;
  grid-template-columns: subgrid;
}

.frames dt, .frames dd {
  font-family: ui-monospace, monospace;
  font-size: var(--aside);
  overflow-wrap: anywhere;
}

/* The same run of figures over their words the last scan is reported with, which is
 * what these are — so it is that rule, and this is only what a panel changes about
 * it.
 *
 * `auto-fit` rather than the fixed four: how many figures there are here depends on
 * what the file said about itself, and a file that reports no bitrate should leave
 * three across the panel rather than a gap where the fourth was.
 *
 * No margin over the rule: what is above is either the label that names the block —
 * which leaves the same hairline's worth of room a label leaves over `.spelt`, so
 * these now begin where every other run of rows in a drawer begins — or the top of
 * the scrolling body, which has the padding for it. The air the figures need is
 * inside them, on each cell, and not above the line. */
.leafing .figures {
  grid-template-columns: repeat(auto-fit, minmax(min(6.5rem, 100%), 1fr));
  gap: 0 0.875rem;
  padding-top: 0;
}

.leafing .figures > div { padding: 0.8125rem 0; }

/* And a rule under them, for the one drawer where nothing under them draws its own:
 * a genre has no facts to spell out, so what follows the figures is a label over a
 * listing rather than a run of rows that closes itself. Everywhere else the first row
 * of `.spelt` brings the line, and a block that closed itself as well put two
 * hairlines a pixel apart. */
.leafing .figures.ruled { border-bottom: 1px solid var(--line); }

/* Four figures that will not fit on one line break two and two rather than three and
 * one. `auto-fit` cannot do it: the number of columns comes out of a division, and any
 * minimum wide enough to rule out three rules out four as well. So the width is asked
 * for once, and the figure it is asked about is what four columns take — four minimums,
 * three gaps and the panel's padding — rather than a width somebody liked the look of.
 *
 * Asked of the fourth figure and not of the drawer it is in, because how many there are
 * is the file's to say: a track that reports no bitrate has three, and so does an artist
 * nobody has played. Three fit across a phone with room to spare, and dropping them to
 * two and one would break what is not broken. */
@media (max-width: 30.875rem) {
  .leafing .figures:has(> :nth-child(4)) { grid-template-columns: 1fr 1fr; }
}

/* One sentence on the left saying where what you are reading came from, and what can
 * be done with it on the right. */
.drawer > footer {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.75rem;
  border-top: 1px solid var(--line);
}

.drawer > footer > .quiet {
  min-width: 0;
  font-size: var(--aside);
  text-wrap: pretty;
}

.deeds {
  display: flex;
  flex: none;
  gap: 1.25rem;
}

/* Text and not pills. There are two of these at most and one of them is always the
 * point of the panel; a pair of filled buttons at the foot of a reading panel reads
 * as a form waiting to be submitted.
 *
 * Its own children and not every button under it. One of these opens a sheet, and a
 * sheet is a child of the button that opened it however far above the page it floats —
 * so without the `>` this reached inside and took the padding off a pill in there,
 * leaving the accent drawn tight around its word. */
.deeds > button {
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--control);
  cursor: pointer;
}

.deeds > button:hover { color: var(--ink); }

/* The one that is the point of it. */
.deeds .leading { color: var(--accent); }

.deeds .leading:hover { color: var(--accent); }

/* The heart, and whatever joins it later: a glyph rather than a word, because the
 * corner beside it belongs to the deed that is the point of the panel and two more
 * words there would read as three things of equal rank.
 *
 * Square and 2.5rem, so the target is the button rather than the 1rem glyph inside
 * it, and pulled out on the left by its own padding so the mark lines up with the
 * text above rather than sitting a hand's width in from it. */
.deeds .mark {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  margin-left: -0.75rem;
  margin-right: -0.5rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  cursor: pointer;
}

.deeds .mark .icon {
  width: 1.0625rem;
  height: 1.0625rem;
}

.deeds .mark:hover { color: var(--ink); }

/* The one control at this foot that carries state, so the one that takes the accent.
 * The glyph itself changes too — outlined to filled — because a colour on its own is
 * an answer somebody may not be able to see. */
.deeds .marked { color: var(--accent); }

.deeds .marked:hover { color: var(--accent); }

.deeds .mark:focus-visible {
  outline: none;
  color: var(--accent);
}

/* The word that stands where a button stood, for the two seconds after queueing a
 * track. It has to measure the same as the button it replaced — same size, same box —
 * or the foot of the panel changes height the moment somebody presses it and changes
 * back afterwards. Worst on a phone, where the button is a 44px target and a line of
 * text is half that. */
.deeds > .quiet {
  display: inline-flex;
  align-items: center;
  font-size: var(--control);
}

/* A note in its own words rather than a row, in the two directions a note comes in.
 * `.remark` follows what it is about and `.preface` comes before it — which is the
 * whole difference between them, and the reason they are two names: one rule with one
 * margin was air above the line that introduces a list and none between it and the
 * list.
 *
 * No width of its own. These live inside a panel that is already `min(32.5rem, 100%)`
 * wide, so the panel is the measure and a second, narrower one only made these notes
 * stop short of the rows they are about. */
.remark, .preface {
  font-size: var(--aside);
  text-wrap: pretty;
}

.remark { margin-top: 0.875rem; }

.preface { margin-bottom: 1.125rem; }

/* What somebody wrote into the file, which is prose and not a value. Its own hairline
 * block, because it is the one thing in here that is a paragraph. */
.quoted {
  margin-top: 0.75rem;
  padding: 0.875rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  color: var(--quiet);
  font-size: var(--control);
  line-height: 1.5;
  text-wrap: pretty;
}

/* The file is not where it was. Not an alarm — a scan marks rather than deletes, and
 * this is the one row in a listing somebody opens because something is wrong with it
 * — so it is said in the alarm's colour on nothing, at the top, and then the panel
 * carries on saying what it knows. */
.absent {
  padding: 0.75rem 0.875rem;
  border-left: 2px solid var(--alarm);
  background: color-mix(in srgb, var(--alarm) 7%, transparent);
  font-size: var(--control);
  text-wrap: pretty;
}

/* Where the words came from and what can be done with them, over the words. */
.told {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.875rem;
}

.told p {
  font-size: var(--aside);
  text-wrap: pretty;
}

.told button {
  flex: none;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--aside);
  text-decoration: underline;
  text-underline-offset: 0.1875rem;
  cursor: pointer;
}

.told button:hover { color: var(--ink); }

/* Timed words: the moment on the left, the line on the right, and the one at the
 * playhead lit. Bled by the same twelve pixels every other run of rows in this panel
 * is, so the lit line reaches the edges of the reading rather than stopping short. */
.timed { margin: 0 -0.75rem; }

.timed > div {
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr);
  gap: 0.875rem;
  padding: 0.4375rem 0.75rem;
}

.timed .figure {
  font-size: var(--aside);
  font-variant-numeric: tabular-nums;
  color: var(--quiet);
}

.timed > div > :last-child { line-height: 1.5; }

/* Only while this is the song that is sounding. Reading one track's words while
 * another plays is an ordinary thing to do, and a line following somebody else's
 * playhead would be a lie about which song this is. */
.timed .sounding {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
}

.timed .sounding .figure { color: inherit; }

/* Words with no timings: a block of text and no column of blanks beside it. An empty
 * column reads as a value that is missing rather than as one that was never there,
 * which is why this is not the layout above with the times left out.
 *
 * The drawings capped this at a reading measure and it is not capped here, for the
 * same reason nothing else in the panel is: the panel is 520px wide, so the width is
 * already a reading measure, and a second one inside it only looks like a column that
 * failed to fill. */
.verses {
  padding: 1.125rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  font-size: var(--name);
  line-height: 1.65;
  white-space: pre-line;
  text-wrap: pretty;
}

/* No words at all, which is not a fault: no alarm colour anywhere. What it says
 * instead is everywhere the server looked, because on a machine somebody runs
 * themselves that is the actionable part. */
.wordless {
  padding: 1.875rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.wordless p { font-size: var(--name); }

.wordless .quiet {
  margin-top: 0.5rem;
  font-size: var(--control);
  line-height: 1.55;
  text-wrap: pretty;
}

@media (max-width: 45rem) {
  /* The whole screen rather than a panel down one side, and no scrim: there is
   * nothing beside it left to see. */
  .drawer {
    inset: 0;
    width: 100%;
    border-left: none;
    box-shadow: none;
    padding-bottom: env(safe-area-inset-bottom);
  }

  .scrim { display: none; }

  /* Narrower air, like every other screen at this width. */
  .drawer > header { padding: 1.125rem 1.125rem 1rem; }

  .tabs { padding: 0 1.125rem; }

  .leafing { padding: 1.25rem 1.125rem 1.75rem; }

  .drawer > footer { padding: 0.875rem 1.125rem; }

  /* The name column goes, because at this width it leaves the value four characters
   * of room. The name sits over its value instead, which is what every other pair in
   * this panel does when it runs out of width.
   *
   * And the tag list stops being a grid, which is what lets its rows stop borrowing
   * columns from it: a `subgrid` under a block parent is not a thing. */
  .frames { display: block; }

  .spelt > div, .frames > div {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.125rem;
  }

  /* Every one of these is a 44px target on a phone. */
  .tabs button { padding: 0.9375rem 0; }

  .deeds > button, .deeds > .quiet { min-height: 2.75rem; }

  /* Square at 44px, since this one is a target and not a word: the height comes from
   * the rule above and only the width is left to say. */
  .deeds .mark { width: 2.75rem; }
}

/* --- the playlists -------------------------------------------------------- */

/* Written as one block on purpose, and touching nothing else in this sheet. An earlier
 * attempt reached into the queue's rules and added `.order` to a dozen of its selectors,
 * on the grounds that the two are almost the same row. They are — and sharing them that
 * way made both impossible to read and neither safe to change. What repeats here is
 * about thirty declarations; what it buys is that everything these two screens do is in
 * one place, and that the queue is exactly as it was. */

/* One row per list: what it is, how much of it there is, when it changed, whose it is,
 * and what may be done to it. Rows and not cards, because a list has no artwork to put
 * under and its length is what it is judged by, which reads in a line. */
.made {
  margin: 0 0 2.5rem;
  padding: 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

/* The columns are the frame's own — 120, 110, 130 and 88 pixels for the four figures,
 * 24 for the dots, 20 of gap and 14/12 of padding — with one change: the frame gives the
 * name a floor of 200px, and here it has none. With the floor the row cannot be narrower
 * than 772px, which is more than the content column has to give between about 1120 and
 * 1150 pixels of window, and a row wider than what holds it is the thing that broke this
 * screen. Without it the name gives way instead, and below 55rem the row stops being a
 * row at all. */
.made > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 7.5rem 6.875rem 8.125rem 5.5rem 1.5rem;
  align-items: baseline;
  gap: 1.25rem;
  margin: 0 -0.75rem;
  padding: 0.875rem 0.75rem;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}

.made > li:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }

/* The name over what it says about itself: the same two-line shape a track row uses for
 * a title over its artist. */
.made .what {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 0.125rem;
}

/* The name, which is the first of the two lines in that cell. No class of its own:
 * `named` and `naming` are both taken in this sheet and both mean something else, and a
 * third word for "the name" would be one more thing to look up. Ink at rest and the
 * accent under the pointer — the row is not a link, it is a row that opens something. */
.made .what > span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.made > li:hover .what > span:first-child { color: var(--accent); }

.made .what .quiet {
  font-size: var(--aside);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Four cells of the grid, wrapped only so they can become one line together when the
 * columns go. On a wide screen the wrapper is not there as far as layout is concerned. */
.made .sums { display: contents; }

.made .figure {
  color: var(--quiet);
  font-variant-numeric: tabular-nums;
}

.made .owning {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Public, in the accent: the one thing about a list that other people can notice, so
 * the one thing on the row drawn in a colour. */
.shared {
  color: var(--accent);
  font-size: var(--aside);
}

/* At the end of the row whether or not there is a menu in it, so the rows either side of
 * a list that is not yours do not step sideways. */
.made .doing {
  display: flex;
  justify-content: flex-end;
}

/* Where six columns stop fitting: the five beside the name take 400 pixels with their
 * gaps, the column of sections 240 of whatever there is, and the page's own padding
 * another 112 — so below this the name is down to a word and a half. The row becomes two
 * lines instead, which is what the roster of accounts does at its own width. */
@media (max-width: 55rem) {
  .made > li {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.375rem 0.875rem;
  }

  .made .what { grid-area: 1 / 1; }

  /* Down the right of both lines and centred between them, rather than pinned to the
   * line the name is on. What it opens is about the whole row, and the roster of
   * accounts anchors its own to the first line only because a row there begins with a
   * 30px avatar that gives that line a height of its own. */
  .made .doing {
    grid-area: 1 / 2 / 3 / 3;
    align-self: center;
  }

  /* What were four columns become one line of prose under the name, at the size every
   * second line in this panel is set at. */
  .made .sums {
    grid-area: 2 / 1 / 3 / 3;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.125rem 0.75rem;
    font-size: var(--aside);
  }
}

/* A heading that is also a field, which is the panel about a playlist of your own: the
 * name is one of the two things a list is, and it is edited where it is read. Everything
 * about it says heading — the size and weight come from `.drawer h2`, which it sits
 * beside — and what says field is the line that appears under it when the pointer is on
 * it or the cursor is in it. */
.calling {
  width: 100%;
  padding: 0;
  border: none;
  border-bottom: 1px solid transparent;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 1.25rem;
  font-weight: 400;
  letter-spacing: -0.01em;
}

.calling:hover { border-bottom-color: var(--line); }

.calling:focus {
  border-bottom-color: var(--accent);
  outline: none;
}

/* Who may see it: the word it is now, and under it what that means. */
.privacy {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin: 0 0 1.625rem;
}

/* A switch that stands alone, so it says which way it is with its colour and nothing
 * else — the shape Shuffle has at the foot of the queue. The line under a word is for
 * choosing one of several, where the ones not chosen have to look like they are still
 * there to choose; there is nothing beside this to choose instead. */
.privacy button {
  align-self: flex-start;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  font: inherit;
  font-size: var(--control);
  cursor: pointer;
}

.privacy button:hover { color: var(--ink); }

/* After the hover on purpose: the two weigh the same, so the later one wins and a
 * pointer resting on it does not make a public list look private for a moment. */
.privacy button.shared { color: var(--accent); }

.privacy .quiet { font-size: var(--aside); }

/* The running order, in the panel about one list. The queue's row, near enough — a
 * handle, what it is, how long it runs, a way out — with its own numbering in place of
 * the queue's empty first cell.
 *
 * The air is the queue's too: twelve pixels either side of two lines at 1.3, which is
 * the shape every row in this panel has. No height is set — what a row measures is what
 * that comes to, 61px, and the same number is written as `ROW` in
 * `drawer/playlist.rs`, which is what turns a drag's distance into a number of rows. */
.order {
  margin: 0 -0.75rem;
  padding: 0;
  list-style: none;
}

.order > li {
  display: grid;
  grid-template-columns: 1.125rem 1.75rem minmax(0, 1fr) auto 1.5rem;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem;
  border-bottom: 1px solid var(--line);
  touch-action: pan-y;
  will-change: transform;
}

/* A list you are only reading has no handle and no cross, and no columns standing empty
 * where they would have been. */
.order > li.borrowed { grid-template-columns: 1.75rem minmax(0, 1fr) auto; }

.order > li:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }

/* While a row is being dragged nothing lights up under the pointer: what is under it is
 * a gap travelling, and a row tinting as it passes reads as a row answering something it
 * is not part of. */
.order.reordering > li:hover { background: none; }

/* While the gesture runs the row follows the finger, so no transition; when it ends the
 * offset goes back to nought and this is what makes it spring rather than jump. */
.order > li:not(.lifted) { transition: transform 160ms ease-out; }

/* Lifted off the list while it is moved, so it is plainly the thing travelling and not
 * the list scrolling under it. */
.order > li.lifted {
  position: relative;
  z-index: 1;
  background: var(--paper);
  box-shadow: 0 6px 20px light-dark(rgb(0 0 0 / 16%), rgb(0 0 0 / 45%));
}

/* What the row is dragged by, and the only part of it that takes a vertical gesture:
 * `none` keeps both axes here, where everywhere else the vertical one is the panel being
 * scrolled. `--quiet` and never `--line`, which is the hairline token and disappears
 * against the paper. */
.order .handle {
  display: grid;
  place-content: center;
  place-items: center;
  gap: 0;
  width: 1.125rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  touch-action: none;
  cursor: grab;
}

.order .handle:active { cursor: grabbing; }

.order .handle .icon {
  width: 1.125rem;
  height: 1.125rem;
}

/* Its place in the list, and the way to play from there. The button itself is styled
 * with every other one of these in the sheet; what is left is when the glyph over the
 * number shows. */
.order > li:hover .starter .acting, .order > li.sounding .starter .acting { opacity: 1; }

.order .what {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.3;
}

.order .what > * {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.order .by, .order .figure {
  color: var(--quiet);
  font-size: var(--aside);
}

.order .figure { font-variant-numeric: tabular-nums; }

/* Sounding, and gone: the two words every listing in this panel uses, said the same
 * way. */
.order > li.sounding { color: var(--accent); }

.order > li.sounding .by, .order > li.sounding .figure { color: inherit; }

.order > li.gone { opacity: 0.55; }

/* Takes a track out of the list. Quiet until the row is under the pointer, because a
 * column of crosses down a list reads as a list of things to undo. */
.order .drop {
  display: grid;
  place-content: center;
  place-items: center;
  gap: 0;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  opacity: 0;
  cursor: pointer;
}

.order > li:hover .drop, .order .drop:focus-visible { opacity: 1; }

.order .drop:hover { color: var(--alarm); }

.order .drop .icon {
  width: 0.875rem;
  height: 0.875rem;
}

/* Every target on a phone is 44px, and the row grows to hold them. */
@media (max-width: 45rem) {
  .order > li {
    grid-template-columns: 1.75rem 2.75rem minmax(0, 1fr) auto 2.75rem;
  }

  .order > li.borrowed { grid-template-columns: 2.75rem minmax(0, 1fr) auto; }

  .order .handle {
    width: 1.75rem;
    height: 2.75rem;
  }

  .order .drop {
    width: 2.75rem;
    height: 2.75rem;
  }
}

/* The foot of the queue: keep it, or be rid of it. The two are at opposite ends of the
 * line on purpose — one makes a playlist and the other throws the music away — and the
 * glyph is the destructive one, which is the way round that keeps a mis-tap from being
 * the expensive way round. */
.queue > footer {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.875rem 1.625rem;
  border-top: 1px solid var(--line);
}

.queue > footer .tap:hover { color: var(--alarm); }

/* The lists to add something to, in the sheet a drawer's foot opens. One row each, at a
 * height a thumb can hit, and the ones it is already in are said and not offered. */
.choosing {
  margin: 0;
  padding: 0;
  list-style: none;
}

.choosing > li + li { border-top: 1px solid var(--line); }

.choosing button {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  min-height: 2.75rem;
  padding: 0.5rem 0;
  border: none;
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.choosing button:hover:not(:disabled) { color: var(--accent); }

.choosing button:disabled { cursor: default; }

.choosing .quiet {
  flex: none;
  font-size: var(--aside);
}

/* The receipt in the foot of the queue, which has no provenance line of its own to
 * borrow: it sits in the space between the two buttons, the same distance from both, and
 * says nothing at all the rest of the time. */
.queue > footer .receipt {
  flex: 1;
  min-width: 0;
  font-size: var(--aside);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
}

/* --- how loud ------------------------------------------------------------- */

/* The speaker at the end of the player's row, and the narrow panel it opens over
 * itself. A block of its own, which is why it repeats what it shares with the rest of
 * the player rather than joining its selectors: nothing above this line changes for
 * it, and nothing here reaches into the player's own rules.
 *
 * What the panel hangs off, so it opens over the speaker wherever the speaker is. */
.loudness {
  position: relative;
  display: block;
}

/* The same quiet glyph as back and forward, lit while its panel is open so the button
 * says where the thing on screen came from — the way the queue's count does. */
.speaker, .hush {
  display: grid;
  gap: 0;
  place-content: center;
  place-items: center;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--quiet);
  cursor: pointer;
}

.speaker {
  width: 2rem;
  height: 2rem;
}

.speaker:hover, .hush:hover { color: var(--ink); }

.speaker.lit { color: var(--accent); }

.speaker .icon {
  width: 1rem;
  height: 1rem;
}

/* Upright, and over the speaker rather than beside it: the level at the top, the line
 * in the middle, silence at the foot. Over whatever is above it while it is open —
 * the title of the song, in the column — which is the one layer it costs. */
.volume-box {
  position: absolute;
  bottom: 2.5rem;
  left: 50%;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.625rem;
  width: 2.75rem;
  padding: 0.75rem 0 0.5rem;
  transform: translateX(-50%);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  box-shadow: 0 10px 28px light-dark(rgb(0 0 0 / 12%), rgb(0 0 0 / 35%));
}

.volume-figure {
  color: var(--ink);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
}

/* The line along the song, stood up: two pixels drawn by the wrapper and a
 * transparent `range` over it that is the width of a finger. */
.volume-track {
  position: relative;
  display: block;
  width: 1.5rem;
  height: 6.875rem;
  cursor: pointer;
}

.volume-track::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 0.125rem;
  transform: translateX(-50%);
  background: linear-gradient(to top, var(--accent) var(--at), var(--line) var(--at));
}

/* Silent, the line keeps where the level is and stops being the accent: it is where
 * the sound will come back to, not what is sounding. */
.volume-track.hushed::before {
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--quiet) 45%, transparent) var(--at),
    var(--line) var(--at)
  );
}

/* Upright by writing mode, and read from the bottom by direction: the way a range
 * stands up without being rotated, which would leave its box lying on its side. */
.volume-track input {
  appearance: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  writing-mode: vertical-lr;
  direction: rtl;
  cursor: pointer;
}

/* No handle at rest, as along the song: it appears under the pointer and on focus. */
.volume-track input::-webkit-slider-thumb {
  appearance: none;
  width: 0.625rem;
  height: 0.625rem;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
}

.volume-track input::-moz-range-thumb {
  width: 0.625rem;
  height: 0.625rem;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
}

.volume-track:hover input::-webkit-slider-thumb,
.volume-track input:focus-visible::-webkit-slider-thumb { opacity: 1; }

.volume-track:hover input::-moz-range-thumb,
.volume-track input:focus-visible::-moz-range-thumb { opacity: 1; }

.volume-track input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.hush {
  width: 2rem;
  height: 1.75rem;
}

.hush .icon {
  width: 0.9375rem;
  height: 0.9375rem;
}

/* On the phone it sits at the foot of the opened player, after what the file is, and
 * everything grows to the size of a thumb. The speaker reaches past the foot's own
 * margin so its glyph lines up with the edge while its target stays 44px. */
.file-end {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}

.sheet-player .speaker {
  width: 2.75rem;
  height: 2.75rem;
  margin-right: -0.75rem;
}

.sheet-player .speaker .icon {
  width: 1.1875rem;
  height: 1.1875rem;
}

.sheet-player .volume-box {
  bottom: 3.125rem;
  width: 3.25rem;
  padding: 0.875rem 0 0.5rem;
  border-radius: 10px;
}

.sheet-player .volume-figure { font-size: 0.8125rem; }

.sheet-player .volume-track {
  width: 2.75rem;
  height: 9.375rem;
}

.sheet-player .volume-track::before { width: 0.1875rem; }

.sheet-player .hush {
  width: 2.75rem;
  height: 2.75rem;
}

.sheet-player .hush .icon {
  width: 1.125rem;
  height: 1.125rem;
}
