/* SAB Portal — The recordings page: the picture and the card beside it, the hand-off to the
 * acoustic script, the copy controls, one recording as a tile, and the calendar.
 *
 * One of the files listed in layers.css; read that first. Every rule below
 * is in the `app` cascade layer, so it beats Pico without needing to be
 * more specific than Pico. Sizes come from tokens.css and nowhere else. */

@layer app {

.sab-more.htmx-request .sab-more-link {
  opacity: 0.4;
  pointer-events: none;
}

/* --- The detail row: a picture, and a card beside it -------------------- */

/* Both detail pages open the same way — where the thing is on the left, what
 * is known about it on the right — so the row and the card are declared once
 * here and each page says only what is different about its own. */

.sab-detail-top {
  display: grid;
  /* The pipe's card holds two columns of its own, so it is given room to keep
   * them side by side; below that width the whole row stacks instead. */
  grid-template-columns: minmax(0, 0.8fr) minmax(24rem, 1.2fr);
  gap: var(--sab-gap-loose);
  margin-bottom: var(--sab-gap-wide);
}

/* A box's picture answers one question — *where* — and answers it at any size.
 * The reading is done in the words beside it and in the tabs below, so the
 * square is held to a corner of the row and the rest of the width goes to
 * what is read.
 *
 * A fixed width rather than a fraction. The square is a glance, and a glance
 * does not want to be twice the size on a wide screen. */
.sab-detail-top-brief {
  grid-template-columns: minmax(0, var(--sab-map-brief)) minmax(0, 1fr);
  align-items: start;
}

.sab-detail-top-brief .sab-detail-map {
  flex: none;
  aspect-ratio: 1 / 1;
  min-height: 0;
}

/* --- The card -----------------------------------------------------------
 *
 * A bordered stack of bands, each band one idea. Two pages use it — the pipe's
 * hand-off to the acoustic script and the box's own facts — so the border, the
 * rhythm and the rule between bands are here and not in either of them. A band
 * added later cannot arrive with spacing of its own. */
.sab-card {
  /* Sides only. The vertical rhythm belongs to the bands, so that every gap in
   * the card is the same gap and the rules between them land on it. */
  padding: 0 var(--sab-gap-loose);
  /* Pico's `section` margin would leave it short of the map column's bottom. */
  margin: 0;
  display: grid;
  align-content: start;
}

.sab-card > * {
  margin: 0;
  padding-block: var(--sab-gap-loose);
  min-width: 0;
}

.sab-card > * + * {
  border-top: var(--sab-hairline) solid var(--sab-rule);
}

/* Notes inside a card are asides to what is beside them, not statements about
 * the page, so they sit tighter than the ones between sections. */
.sab-card .sab-note {
  margin: 0;
  font-size: var(--sab-text-meta);
}

/* --- The hand-off to the acoustic script -------------------------------- */

/* Two things are being handed over: three numbers, and two audio files. The
 * card is laid out so that neither has to be retyped — the values carry a copy
 * control each, the script block copies whole, and the night that addresses
 * the audio is a control rather than a caption. */

/* --- What is known, and what to take away ------------------------------ */

/* Two bands, and the card is two things to look at rather than six. The
 * measurements are read; the values below them are taken. */

/* Four measurements, four columns, always. `auto-fit` would give three and a
 * gap at some widths, and a row of facts that reflows to a different shape
 * every few pixels is the thing that makes a card look untidy. The phone width
 * takes two, once, at the breakpoint. */
.sab-handoff-facts {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sab-gap-loose);
  margin: 0;
}

/* Where each number came from, at the width of the rule above it. Words say it
 * too — "wall", "assumed — not from SCADA" — so the colour is a second signal
 * and never the only one. */
.sab-handoff-facts .sab-fact {
  border-top-color: var(--sab-rule);
}

.sab-handoff-facts .sab-fact-forward {
  border-top-color: var(--sab-map-forward);
}

.sab-handoff-facts .sab-fact-return {
  border-top-color: var(--sab-map-return);
}

/* The one number on the card nobody measured. Dashed rather than coloured: a
 * fifth hue would have to be learnt, and a broken line is already read as
 * provisional everywhere else in the portal. */
.sab-handoff-facts .sab-fact-assumed {
  border-top-style: dashed;
}

/* The unit, at the size of a caption. It belongs to the number and must never
 * compete with it — but a number without it is not information, so it is
 * quietened and never dropped. */
.sab-handoff-facts .sab-fact dd small {
  font-size: var(--sab-text-small);
  color: var(--pico-muted-color);
}

.sab-handoff-facts .sab-fact dd {
  font-variant-numeric: tabular-nums;
}

/* The block that gets pasted, and nothing beside it: the three values were
 * once repeated to its right one at a time, and the card was two lists of the
 * same numbers. */
.sab-handoff-take {
  display: grid;
}

/* One save for the pair, across the top of the two tiles it takes. Tinted so
 * that the band opens with an offer rather than with a grey slab, and so the
 * one action in the card that moves files is the one thing in it wearing a
 * colour of its own. */
.sab-handoff-bundle {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sab-gap-tight);
  width: 100%;
  min-height: 2.37rem;
  margin: 0;
  padding: var(--sab-gap-tight) var(--sab-gap-loose);
  border: var(--sab-hairline) solid var(--sab-action-edge);
  border-radius: var(--pico-border-radius);
  background: var(--sab-action-bg);
  color: var(--sab-action);
  font-size: var(--sab-text-label);
  font-weight: var(--sab-weight-medium);
  text-align: center;
  text-decoration: none;
}

/* The arrow is drawn in the markup, at the stroke weight the tiles below
 * use — see portal/components/icon_download.html. A little larger here,
 * because this button is a step up in the hierarchy from the two under it. */
.sab-handoff-bundle .sab-get-icon {
  width: 0.9rem;
  height: 0.9rem;
}

.sab-handoff-bundle:hover,
.sab-handoff-bundle:focus-visible {
  border-color: var(--sab-action);
  background: var(--sab-action-edge);
}

/* --- The copy controls -------------------------------------------------- */

/* Both are buttons, and Pico styles buttons as full-width blocks, so both
 * start by giving that back. */
.sab-copy,
.sab-copy-block {
  width: auto;
  margin: 0;
  font-family: inherit;
  color: inherit;
  cursor: pointer;
}

.sab-copy {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.35rem;
  width: 100%;
  /* Fills its chip, so three values sit on one rhythm beside the block they
   * were taken from. */
  height: 100%;
  padding: 0.3rem var(--sab-gap);
  border: var(--sab-hairline) solid var(--sab-rule);
  border-radius: var(--pico-border-radius);
  background: var(--sab-quiet-bg);
  color: var(--pico-muted-color);
  transition: background-color 0.12s ease-in-out;
}

.sab-copy:hover,
.sab-copy:focus-visible {
  background: var(--sab-quiet-bg-hover);
  border-color: var(--pico-primary);
  color: var(--pico-primary);
}

.sab-copy-icon {
  align-self: center;
  width: 0.85rem;
  height: 0.85rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

/* The whole block is one target. Hovering darkens it and names the action:
 * something that looks like a code listing has to say it is a button.
 *
 * Two rows: the strip that names the action, ruled off, and the script under
 * it — a code listing under its own caption. The strip used to be floated over
 * the corner, which meant the lines carried a padding chosen to clear it — a
 * measurement that had to be remembered every time either changed. Rows push
 * apart instead, and a row cannot be collided with. */
.sab-copy-block {
  display: grid;
  grid-template-rows: auto auto;
  align-content: space-between;
  gap: var(--sab-gap);
  text-align: left;
  padding: var(--sab-gap) var(--sab-gap-loose);
  /* The action colour, a shade lighter than the pair download under it: the
   * two are the card's two takes, and the same family says so; the block is
   * the bigger of the two, so it wears less of the tint. A grey block read as
   * a listing to look at, not a button to press. */
  border: var(--sab-hairline) solid var(--sab-action-edge);
  border-radius: var(--pico-border-radius);
  background: color-mix(in srgb, var(--sab-action-bg) 55%, var(--pico-card-background-color));
  transition: background-color 0.12s ease-in-out;
}

.sab-copy-block:hover,
.sab-copy-block:focus-visible {
  background: var(--sab-action-bg);
  border-color: var(--sab-action);
}

/* A span rather than a <pre>, which a button may not contain. The whitespace
 * is kept here instead, and the lines are exactly the lines that get copied.
 *
 * The largest mono on the page, and the only place it is set in the medium
 * weight: these three lines are the point of the card, they are read digit by
 * digit against a script, and they are what the click takes. They sit on the
 * floor of the block, under the strip that captions them. */
.sab-copy-source {
  display: block;
  align-self: end;
  white-space: pre;
  overflow-x: auto;
  font-family: var(--sab-mono);
  font-size: var(--sab-text-base);
  font-weight: var(--sab-weight-medium);
  font-variant-numeric: tabular-nums;
  line-height: var(--sab-leading);
  /* Not `--pico-color`: inside a button that is the colour of text on a filled
   * primary button, which on this panel is all but invisible. */
  color: var(--sab-ink);
}

/* The block's caption: a rule across the whole width with the action named at
 * the end of it. A row of its own rather than an overlay, so it can never sit
 * on top of a long line. */
.sab-copy-hint {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sab-gap-tight);
  padding-bottom: var(--sab-gap-tight);
  border-bottom: var(--sab-hairline) solid var(--sab-action-edge);
  font-size: var(--sab-text-micro);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  /* The action's own colour, present at rest: an affordance nobody can see
   * is no affordance, and a touch reader never hovers. */
  color: var(--sab-action);
  font-weight: var(--sab-weight-medium);
}

/* Copied, in words. Colour alone would not say what happened. */
.sab-copy-said {
  font-size: var(--sab-text-micro);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.is-copied .sab-copy-said { color: var(--sab-online); }
.is-refused .sab-copy-said { color: var(--sab-stale); }
.is-copied .sab-copy-word { display: none; }

/* --- One recording, as a tile ------------------------------------------ */

/* Row 2 of the hand-off card: two ends, two columns, and the pair download
 * across the top of them. One pipe's night is two files and they are read
 * across, not down. The box page lays the same tiles out in `.sab-recordings`,
 * as many to a row as it has width for. */
.sab-handoff-audio {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sab-gap-loose);
}

/* Two pages show these — one night from each end of a pipe, and every night a
 * box has on disk — and they are the same object on screen, so the tile is
 * declared once here and each page says only how many go in a row.
 *
 * No border. A soft ground instead, which says "this is an object" without
 * chopping a card into boxes within a box. */
.sab-audio {
  min-width: 0;
  /* A column rather than a grid, so the last line can be pushed to the floor of
   * the tile with `margin-top: auto`. Two tiles side by side are the same
   * height whatever is in them, and their download arrows line up with each
   * other instead of floating at whatever height the picture above ended. */
  display: flex;
  flex-direction: column;
  gap: var(--sab-gap-tight);
  padding: var(--sab-gap);
}

/* The line under the two tiles: whether the two ends listen at the same time.
 * Quiet when they do; the warning note's own look when they do not, which
 * the markup switches on. Across both columns, under both times. */
.sab-handoff-caption {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--sab-text-meta);
  color: var(--pico-muted-color);
}

/* Which box, and when it listened. The time is the half of this heading that
 * gets read twice: two ends that captured at different times cannot make a
 * pair, and the two are compared right here, side by side. */
.sab-audio h4 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sab-gap);
  font-size: var(--sab-text-micro);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--pico-muted-color);
  margin: 0;
}

.sab-audio h4 span {
  font-family: var(--sab-mono);
  font-size: var(--sab-text-small);
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: 0;
  color: var(--sab-ink);
}

/* Drawn in the browser from the file itself, so it costs the server nothing.
 * Zero height until it has something in it: an empty strip reserving space
 * would be a picture that never arrives. */
.sab-waveform {
  display: block;
  margin: 0.4rem 0 0;
  /* `color` rather than a background: the canvas reads its own computed colour
   * so the ink is set here, in one place, with everything else. The pale stop
   * travels beside it as a custom property, read the same way — see
   * portal/js/waveform.js. */
  color: var(--sab-wave);
  --sab-wave-pale: var(--sab-wave-quiet);
}

/* Tall enough to have a shape. At 2.5rem the strip showed that there was a
 * recording; the point of drawing it at all is that a reader can see where in
 * the night the pipe got loud, and that needs vertical room to happen in. */
.sab-waveform.is-drawn {
  height: 4.5rem;
}

.sab-waveform canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* The spectrogram, when the archive holds one. Full bleed across the tile and
 * nothing around it — a frame would only compete with the picture. */
.sab-spectrogram {
  display: block;
  width: 100%;
  height: auto;
  margin: 0.4rem 0 0;
  border-radius: var(--pico-border-radius);
  /* The page's own ground, not the quiet panel: inside the hand-off card the
   * tile is already that colour, and a picture that has not arrived yet would
   * be invisible rather than merely empty. */
  background: var(--pico-background-color);
}

/* Inside a tile the rhythm is the tile's own grid gap, so nothing in it brings
 * a margin of its own — otherwise two tiles drift apart line by line and their
 * players stop lining up with each other. */
.sab-audio > * {
  margin: 0;
}

.sab-audio .sab-counts,
.sab-audio .sab-absent {
  font-size: var(--sab-text-small);
}

/* Not the sentence: a flex paragraph turns each inline code span into a column. */
.sab-audio .sab-counts {
  display: flex;
  align-items: center;
  gap: var(--sab-gap);
}

/* The tile's last line: what this file is, and the way to take it away. Pushed
 * to the floor of the tile, so it is in the same place on every tile in a row
 * however tall the picture above it turned out. */
.sab-audio-foot {
  display: flex;
  align-items: flex-start;
  gap: var(--sab-gap-tight);
  margin-top: auto;
  padding-top: var(--sab-gap-tight);
}

/* The download, as the arrow and nothing else, at the bottom right corner.
 *
 * A word was worth having while it sat beside a size and a player; with those
 * gone the tile has exactly two controls, and the arrow tells them apart faster
 * than a label does. The words are still there — on the accessible name, which
 * is what a screen reader announces — so nothing is lost by not drawing them. */
.sab-get {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  margin-left: auto;
  width: 1.75rem;
  height: 1.75rem;
  border: var(--sab-hairline) solid var(--sab-action-edge);
  border-radius: 50%;
  background: var(--sab-action-bg);
  color: var(--sab-action);
  text-decoration: none;
}

.sab-get:hover,
.sab-get:focus-visible {
  background: var(--sab-action-edge);
  border-color: var(--sab-action);
}

/* What kind of file, from the one part of a name that cannot be wrong about it:
 * the suffix the portal matched on to decide this was a recording at all. */
.sab-audio-kind {
  flex: none;
  align-self: flex-start;
  padding: 0.05rem 0.35rem;
  border-radius: var(--pico-border-radius);
  background: var(--sab-rule);
  font-family: var(--sab-mono);
  font-size: var(--sab-text-nano);
  letter-spacing: 0.06em;
  color: var(--sab-ink);
}

/* Which night, and which box. Two facts rather than the sixty characters of
 * filename they used to be read out of — see the tile's own comment for where
 * each of them came from. They wrap onto a second line rather than being cut:
 * an ICCID with its tail missing is a different box's ICCID. */
.sab-audio-facts {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--sab-gap-tight);
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The arrow into the tray. Drawn rather than set as a character so it carries
 * the same stroke weight as the copy icon two lines below it. */
.sab-get-icon {
  width: 0.8rem;
  height: 0.8rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

/* What the name says — the kind of file, the night, the box — and one click
 * still puts the whole name on the clipboard, because that is what an operator
 * pastes into a search of the archive by hand. The name itself is on the title
 * rather than on screen: sixty characters of it were the loudest thing in a
 * tile that had three quieter facts hidden inside them.
 *
 * A button, because it does something. Styled back down to a line of text:
 * the tile has one loud control already and it is the download. */
.sab-audio .sab-filename {
  display: flex;
  align-items: flex-start;
  gap: var(--sab-gap-tight);
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0.15rem;
  border: var(--sab-hairline) solid transparent;
  border-radius: var(--pico-border-radius);
  background: none;
  /* The line is now two facts and not a filename, so it is set in the page's
   * own face; the ICCID inside it keeps `.sab-mono`, which is the one part
   * that is read character by character. */
  font-family: var(--sab-font);
  font-size: var(--sab-text-micro);
  line-height: 1.35;
  color: var(--pico-muted-color);
  text-align: left;
  cursor: pointer;
}

.sab-audio .sab-filename .sab-copy-icon {
  width: 0.75rem;
  height: 0.75rem;
  margin-left: auto;
  /* On the first line of the name whatever the name does below it. */
  align-self: flex-start;
  opacity: 0;
  transition: opacity 0.12s ease-in-out;
}

.sab-audio .sab-filename:hover,
.sab-audio .sab-filename:focus-visible {
  border-color: var(--sab-rule);
  background: var(--sab-quiet-bg-hover);
  color: var(--sab-ink);
}

/* Shown on hover and always once focused: a keyboard reader never hovers, and
 * the icon is the only thing saying the line is pressable. */
.sab-audio .sab-filename:hover .sab-copy-icon,
.sab-audio .sab-filename:focus-visible .sab-copy-icon {
  opacity: 1;
}

/* Sits where the icon was, in words. Smaller than the shared rule sets it,
 * because at --sab-text-micro the confirmation was larger than the name it
 * confirmed and the line jumped when it appeared. */
.sab-audio .sab-filename .sab-copy-said {
  font-family: var(--sab-font);
  font-size: var(--sab-text-nano);
  white-space: nowrap;
}

.sab-audio .sab-filename.is-copied .sab-copy-icon {
  opacity: 1;
  color: var(--sab-online);
}

/* --- Row 3: which night ------------------------------------------------ */

.sab-handoff-night {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sab-gap);
  font-size: var(--sab-text-meta);
}

.sab-handoff-night label {
  margin: 0;
  color: var(--pico-muted-color);
}

/* One segmented control: the calendar's handle, the date, show.
 *
 * The panel the date field drops is the browser's own and takes no CSS. It is
 * left that way on purpose — it is the calendar the reader already knows, it
 * is keyboard- and screen-reader-correct without a line of our code, and a
 * hand-built replacement would be several hundred lines of JavaScript that
 * somebody has to keep working. What is styled here is everything around it. */
.sab-night-group {
  display: inline-flex;
  align-items: stretch;
  border: var(--sab-hairline) solid var(--sab-rule);
  border-radius: var(--pico-border-radius);
  background: var(--pico-background-color);
  overflow: hidden;
}

.sab-night-group:focus-within {
  border-color: var(--pico-primary);
  box-shadow: 0 0 0 2px var(--pico-primary-focus, rgba(37, 99, 235, 0.2));
}

/* The calendar's handle. Opens the panel with the script, and focuses the date
 * field without it. */
.sab-night-icon {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0 var(--sab-gap);
  border-right: var(--sab-hairline) solid var(--sab-rule);
  color: var(--pico-muted-color);
  cursor: pointer;
}

.sab-night-icon:hover {
  background: var(--sab-quiet-bg-hover);
  color: var(--pico-primary);
}

/* The archive could not be read to the end. Its own line, under the control it
 * qualifies. */
.sab-night-caveat {
  flex-basis: 100%;
}

/* Any input in the control, not `input[type="date"]`. calendar.js retypes the
 * field to text — that is the only way to suppress the browser's own panel —
 * and a selector keyed to the type stops matching the moment it does, which
 * drops the field back to Pico's full-width box inside the segmented control. */
.sab-night-group input {
  width: auto;
  margin: 0;
  padding: var(--sab-gap-tight) 0.3rem;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  font-size: var(--sab-text-label);
  font-variant-numeric: tabular-nums;
  height: auto;
  text-align: center;
}

/* Chromium draws its own calendar glyph in the field. Quietened, and brought
 * up on hover, so it reads as part of this control rather than beside it. */
.sab-night-group input[type="date"]::-webkit-calendar-picker-indicator {
  opacity: 0.45;
  cursor: pointer;
}

.sab-night-group input[type="date"]:hover::-webkit-calendar-picker-indicator {
  opacity: 1;
}

.sab-night-group button {
  width: auto;
  margin: 0;
  padding: var(--sab-gap-tight) 0.9rem;
  border: 0;
  border-left: var(--sab-hairline) solid var(--sab-rule);
  border-radius: 0;
  background: var(--sab-quiet-bg);
  color: var(--pico-primary);
  font-size: var(--sab-text-meta);
}

.sab-night-group button:hover,
.sab-night-group button:focus-visible {
  background: var(--sab-quiet-bg-hover);
}

/* --- The calendar panel ------------------------------------------------- */

/* Drawn by portal/js/calendar.js, because the browser's own panel is rendered
 * outside the page and takes no styling at all. Everything below is the price
 * of it looking like the rest of the portal. */

.sab-night-field {
  position: relative;
  display: inline-block;
}

/* Above the field, not below it. The night control is the last row of the
 * card, so a panel dropping downwards opens over the page's own footer or off
 * the bottom of the screen. `is-below` is the flip, and portal/js/calendar.js
 * sets it only when opening upwards would clip at the top of the viewport. */
.sab-calendar {
  position: absolute;
  z-index: var(--sab-layer-panel);
  bottom: calc(100% + 0.35rem);
  left: 0;
  padding: 0.6rem;
  border: var(--sab-hairline) solid var(--sab-rule);
  border-radius: var(--pico-border-radius);
  background: var(--pico-background-color);
  box-shadow: var(--sab-panel-shadow);
}

.sab-calendar.is-below {
  top: calc(100% + 0.35rem);
  bottom: auto;
}

.sab-calendar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sab-gap);
  margin-bottom: 0.4rem;
}

.sab-calendar-month {
  font-size: var(--sab-text-label);
  font-weight: var(--sab-weight-strong);
  text-transform: capitalize;
}

.sab-calendar-step {
  width: auto;
  margin: 0;
  padding: 0 var(--sab-gap);
  border: 0;
  border-radius: var(--pico-border-radius);
  background: none;
  color: var(--pico-muted-color);
  font-size: var(--sab-text-base);
  line-height: 1.6;
}

.sab-calendar-step:hover {
  background: var(--sab-quiet-bg-hover);
  color: var(--pico-primary);
}

.sab-calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 2rem);
  gap: 0.1rem;
}

.sab-calendar-weekday {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 1.4rem;
  font-size: var(--sab-text-micro);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--pico-muted-color);
}

.sab-calendar-day {
  position: relative;
  width: 2rem;
  height: 2rem;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--pico-border-radius);
  background: none;
  color: var(--sab-ink);
  font-size: var(--sab-text-meta);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  cursor: pointer;
}

.sab-calendar-day:hover:not(:disabled) {
  background: var(--sab-quiet-bg-hover);
}

.sab-calendar-day:focus-visible {
  outline: 2px solid var(--pico-primary);
  outline-offset: -2px;
}

/* Spill from the month either side. Kept rather than blanked so the grid is a
 * calendar and not a ragged block, and quiet enough not to be misread. */
.sab-calendar-day.is-outside {
  color: var(--sab-absent);
  opacity: 0.55;
}

.sab-calendar-day:disabled {
  color: var(--sab-rule);
  cursor: default;
}

/* Today: a ring, not a fill. The fill means "this is the night you are
 * looking at", and two solid marks in one grid is one too many. */
.sab-calendar-day.is-today {
  box-shadow: inset 0 0 0 1px var(--pico-primary);
}

.sab-calendar-day.is-chosen {
  background: var(--pico-primary);
  color: var(--pico-primary-inverse);
  font-weight: var(--sab-weight-strong);
}

/* A night the archive holds audio for. The same two marks as the strip, in the
 * one place a reader goes looking for a night that is not in the last week —
 * an unmarked calendar makes them open days at random. Never the only signal:
 * the day's accessible name says what it holds, in words. */
.sab-calendar-mark {
  position: absolute;
  left: 50%;
  bottom: 0.15rem;
  width: 0.35rem;
  height: 0.35rem;
  transform: translateX(-50%);
  border-radius: 50%;
  background: var(--sab-held);
}

.sab-calendar-day.has-one .sab-calendar-mark {
  background: var(--sab-part-held);
}

/* On the chosen day the fill is the primary colour, and a green dot on it is
 * neither legible nor meaningful. */
.sab-calendar-day.is-chosen .sab-calendar-mark {
  background: var(--pico-primary-inverse);
}

/* The field is driven by the panel once the script has run. The text cursor
 * would promise typing that does nothing. */
.sab-calendar-input {
  cursor: pointer;
}

/* How old the last recording is, beside the control that chooses a night —
 * a date on its own does not say whether it is recent. */

}
