/* ─────────────────────────────────────────────────────────────
   Songs: site-specific blocks (BEM, CUBE composition)
   Sits on top of default.css (theme tokens + base + layout primitives).
   ───────────────────────────────────────────────────────────── */

/* ── Stacking scale ───────────────────────────────────────────
   Every layer that can overlap another lives on this ladder. Add to it here
   rather than bumping a number where it hurts, or the next sticky thing will
   paint over a menu again.

     auto  page content
     20    sticky song player      .song__player
     30    sticky feed filter rail .feed__rail
     50    masthead + everything inside it (the notifications bell, account and
           manage dropdowns) .masthead
     100   phone-pinned Chat switcher .group-nav, skip link .u-skip-link
           (both sit outside the masthead and must clear it)

   The masthead carries position: relative + z-index so it is a single stacking
   context: its dropdowns then compose against page chrome as one layer, and
   their own z-index values (film 9, panel 10) are purely local. That's the fix
   for a real report of the notifications panel disappearing under the sticky
   filter rail on a phone: the panel's 10 was competing with the rail's 30.
   ───────────────────────────────────────────────────────────── */

/* ── Masthead: full-bleed editorial header ───────────────── */
.masthead {
  position: relative;
  z-index: 50;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  justify-content: space-between;
  padding-block: var(--space-s);
  padding-inline: var(--space-l);
  border-block-end: var(--rule-thick);
}

.masthead__wordmark {
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.masthead__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m);
  align-items: center;
  font-family: var(--font-sans);
  font-size: var(--step--1);
}

/* Thin vertical rule fencing the management links (Manage/Groups) off from the
   account area (notifications + identity). The template renders one only when
   something precedes it, so the nav never opens on a rule. */
.masthead__divider {
  align-self: center;
  inline-size: 0;
  block-size: var(--step-1);
  border-inline-start: var(--rule-soft);
}

/* ── Site footer: narrow secondary strip ──────────────────────
   Deliberately quiet: one hairline rule, small mono type in muted ink, a
   single row of links. It is chrome, not content, so it never grows past a
   line. Sits after <main>, below the top-sticky player and filter rail. */
.site-footer {
  padding-block: var(--space-2xs);
  padding-inline: var(--space-l);
  margin-block-start: var(--space-l);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
  border-block-start: var(--rule-thin);
}

/* .l-cluster carries the flex row + wrap; only the gap is tightened, since
   two short links shouldn't read as far-flung. */
.site-footer__nav {
  gap: var(--space-2xs) var(--space-m);
}

.site-footer a {
  color: inherit;
}

/* Account menu: the name is a dropdown trigger holding Profile / Settings /
   Log out. Same details-popover pattern as .manage-menu below; only the
   summary's weight differs (the name reads as identity, not a nav item). */
.account-menu {
  position: relative;
}

.account-menu__summary {
  display: inline-flex;
  gap: var(--space-3xs);
  align-items: baseline;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--color-ink);
  cursor: pointer;
  list-style: none;
}
.account-menu__summary::-webkit-details-marker {
  display: none;
}

.account-menu[open]::before {
  position: fixed;
  inset: 0;
  z-index: 9;
  content: "";
  background: color-mix(in srgb, var(--color-ink) 8%, transparent);
}

.account-menu[open] > .account-menu__summary {
  position: relative;
  z-index: 10;
}

.account-menu__chevron::before {
  display: inline-block;
  font-size: var(--step--2);
  content: "\25BE"; /* ▾ */
  transition: transform 0.12s ease;
}
.account-menu[open] .account-menu__chevron::before {
  transform: rotate(180deg);
}

.account-menu__panel {
  position: absolute;
  inset-block-start: calc(100% + var(--space-2xs));
  inset-inline-end: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  min-inline-size: 10rem;
  padding: var(--space-2xs);
  background: var(--color-bg);
  border: var(--rule-thin);
}

.account-menu__link {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink);
  text-decoration: none;
}
.account-menu__link:hover {
  color: var(--color-accent);
}

/* The log-out button sits in the same list as the links; strip the button
   chrome so it reads as a menu item, left-aligned like the anchors. */
.account-menu__logout {
  margin: 0;
}
.account-menu__logout-button {
  padding: 0;
  text-align: start;
  cursor: pointer;
  background: none;
  border: none;
}

/* ── Prompt strip: three-column ruled strip ──────────────── */
.prompt-strip {
  display: grid;
  grid-template-columns: minmax(8rem, 12rem) 1fr minmax(14rem, 20rem);
  gap: var(--space-l);
  align-items: start;
  padding-block: var(--space-l);
  padding-inline: var(--space-l);
  border-block-end: var(--rule-thin);
}

.prompt-strip__week {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  padding-inline-end: var(--space-l);
  border-inline-end: var(--rule-soft);
}

.prompt-strip__week-marker {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink);
}

.prompt-strip__week-date {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.prompt-strip__lede {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

.prompt-strip__heading {
  max-inline-size: 30ch;
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 400;
  line-height: 1.05;
}

.prompt-strip__heading-accent {
  font-style: italic;
  color: var(--color-accent);
}

.prompt-strip__brief {
  max-inline-size: 50ch;
  margin: 0;
  font-size: var(--step-0);
  color: var(--color-ink-soft);
}

/* Requirements: a quieter footnote under the brief — small label + smaller,
   more muted text so it reads as fine print, not part of the prompt itself. */
.prompt-strip__requirements {
  margin-block-start: var(--space-2xs);
}

.prompt-strip__requirements-heading {
  margin-block: 0 var(--space-3xs);
  font-family: var(--font-sans);
  font-size: var(--step--2);
  font-weight: 600;
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.prompt-strip__requirements-text {
  max-inline-size: 50ch;
  margin: 0;
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.prompt-strip__cta {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding-inline-start: var(--space-l);
  border-inline-start: var(--rule-soft);
}

.prompt-strip__submit {
  display: inline-block;
  align-self: start;
  padding: var(--space-xs) var(--space-m);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  text-decoration: none;
  background: var(--color-accent);
}

.prompt-strip__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

.prompt-strip__meta {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

/* Single-column collapse for narrow viewports */
@media (max-width: 50rem) {
  .prompt-strip {
    grid-template-columns: 1fr;
    gap: var(--space-m);
  }
  .prompt-strip__week,
  .prompt-strip__cta {
    padding-inline: 0;
    border: 0;
  }
  .prompt-strip__cta {
    padding-block-start: var(--space-m);
    border-block-start: var(--rule-soft);
  }
}

/* ── Feed: documentary two-column layout below the strip ─── */
.feed {
  padding-block: var(--space-l);
  padding-inline: var(--space-l);
}

/* Per-week archive page (week.html): the two upward links (back to the feed,
   up to the archive index) above the reused prompt-strip + entry cards. The
   heavy lifting is borrowed from .prompt-strip / .feed; .l-cluster spaces the
   links. */
.week__back {
  padding-block-start: var(--space-m);
  padding-inline: var(--space-l);
  margin-block-end: 0;
}
.week__back-link {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}
.week__back-link:hover {
  color: var(--color-accent);
}

.feed__rail {
  /* z-index + an explicit composited layer (translateZ) so native <audio>
     controls, which Chrome promotes to their own layer, scroll *behind* the
     sticky rail instead of painting over the filters. 30 on the stacking scale
     at the top of this file: above the sticky player, below the masthead. */
  position: sticky;
  inset-block-start: 0;
  z-index: 30;
  align-self: start;
  padding-block: var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  background: rgb(from var(--color-bg) r g b / 0.72);
  isolation: isolate;
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  backdrop-filter: saturate(180%) blur(14px);
  transform: translateZ(0);
}

/* On narrow viewports the rail spans the full feed width. Bleed the
   blurred backdrop to the section's edges so the frosted effect reads
   as a band, not a floating pill. */
@media (max-width: 40rem) {
  .feed__rail {
    padding-inline: var(--space-l);
    margin-inline: calc(var(--space-l) * -1);
  }
}

.feed__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
}

/* ── Filter strip: lives in the feed rail, anchors navigate ─── */
.filter-strip {
  font-family: var(--font-sans);
  font-size: var(--step--1);
}

.filter-strip__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  padding: 0;
  margin: 0;
  list-style: none;
}

.filter-strip__item {
  margin: 0;
}

.filter-strip__option {
  display: inline-block;
  padding-block: var(--space-3xs);
  padding-inline: var(--space-2xs);
  color: var(--color-ink-soft);
  text-decoration: none;
  border-inline-start: 2px solid transparent;
}

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

.filter-strip__option--active {
  font-weight: 500;
  color: var(--color-ink);
  border-inline-start-color: var(--color-accent);
}

/* Per-filter count. */
.filter-strip__count {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-variant-numeric: tabular-nums;
  color: var(--color-ink-muted);
}

/* A filter with nothing to show: dimmed and not interactive. */
.filter-strip__option--disabled {
  color: var(--color-ink-muted);
  pointer-events: none;
  cursor: default;
  opacity: 0.5;
}
.filter-strip__option--disabled .filter-strip__count {
  color: inherit;
}

/* On narrow viewports the rail stacks above the list, so flow the
   filters horizontally to keep the layout compact. */
@media (max-width: 40rem) {
  .filter-strip__list {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--space-2xs);
  }
  .filter-strip__option {
    border-block-end: 2px solid transparent;
    border-inline-start: 0;
  }
  .filter-strip__option--active {
    border-block-end-color: var(--color-accent);
  }
}

.feed__empty {
  padding-block: var(--space-2xl);
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  font-style: italic;
  color: var(--color-ink-muted);
  text-align: center;
}

/* Shown in place of the prompt strip + feed when no week is live. */
.feed-no-week {
  padding-block: var(--space-3xl);
  padding-inline: var(--space-l);
  text-align: center;
}

.feed-no-week__heading {
  margin-block: 0 var(--space-2xs);
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 400;
}

.feed-no-week__copy {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  font-style: italic;
  color: var(--color-ink-muted);
}

/* ── Catch-up nudge: the feed's doorway to the archive ─────────
   Sits under the prompt strip when past weeks hold songs the viewer hasn't
   played. A ruled band, not a dialog: it's a signpost, not an interruption. */
.catchup {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-m);
  align-items: baseline;
  justify-content: space-between;
  padding-block: var(--space-xs);
  padding-inline: var(--space-l);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  background: var(--color-highlight);
  border-block-end: var(--rule-thin);
}

.catchup__copy {
  margin: 0;
  color: var(--color-ink-soft);
}

.catchup__link {
  color: var(--color-accent);
  text-decoration: none;
  border-block-end: 1px solid currentcolor;
}

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

/* ── Week archive index (/g/{slug}/weeks) ──────────────────────
   The catch-up surface: a ruled ledger, newest week first. Each row carries
   its prompt as the headline and the viewer's own outstanding counts in mono
   underneath — the same register as the feed's filter counts, deliberately not
   a dashboard. Past weeks recede in ink; the live one keeps full contrast. */
.weeks {
  max-inline-size: 56rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.weeks__head {
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thick);
}

.weeks__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.weeks__lede {
  max-inline-size: var(--measure);
  margin-block: var(--space-2xs) 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

/* Nothing outstanding anywhere in the group's history. */
.weeks__caught-up {
  padding: var(--space-xs) var(--space-s);
  margin-block: var(--space-m) 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  background: var(--color-highlight);
  border-inline-start: var(--rule-thick);
}

.weeks__list {
  padding: 0;
  margin-block: var(--space-m) 0;
  list-style: none;
}

.week-row {
  padding-block: var(--space-s);
  border-block-start: var(--rule-soft);
}

.week-row:last-child {
  border-block-end: var(--rule-soft);
}

.week-row__marker {
  gap: var(--space-2xs) var(--space-s);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.week-row__number {
  color: var(--color-ink);
}

/* The live week, called out in the same oxide as the schedule's current slot. */
.week-row__current {
  padding: var(--space-3xs) var(--space-2xs);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

.week-row__prompt {
  margin-block: var(--space-2xs) 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  font-weight: 400;
  line-height: 1.3;
}

.week-row__link {
  color: inherit;
  text-decoration: none;
}

.week-row__link:hover {
  color: var(--color-accent);
}

.week-row__stats {
  gap: var(--space-2xs) var(--space-m);
  margin-block: var(--space-2xs) 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.week-row__count {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-variant-numeric: tabular-nums;
}

/* An outstanding count is the one thing on the row worth chasing, so it takes
   full ink; the entry total stays quiet beside it. */
.week-row__stat--open {
  color: var(--color-ink-soft);
}

.week-row__stat--open .week-row__count {
  color: var(--color-accent);
}

.week-row__stat--done {
  font-style: italic;
}

.week-row[data-status="past"] .week-row__prompt {
  color: var(--color-ink-soft);
}

.weeks__empty {
  padding-block: var(--space-2xl);
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  font-style: italic;
  color: var(--color-ink-muted);
  text-align: center;
}

/* ── Group chooser: / for members of more than one group ── */
.group-chooser {
  max-inline-size: var(--measure);
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.group-chooser__heading {
  margin-block: 0 var(--space-m);
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 400;
}

.group-chooser__list {
  padding: 0;
  margin: 0;
  list-style: none;
}

.group-chooser__item {
  border-block-start: var(--rule-thin);
}

.group-chooser__item:last-child {
  border-block-end: var(--rule-thin);
}

.group-chooser__link {
  display: block;
  padding-block: var(--space-s);
  font-family: var(--font-serif);
  font-size: var(--step-1);
  text-decoration: none;
}

.group-chooser__link:hover {
  color: var(--color-accent);
}

.group-chooser__empty {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  font-style: italic;
  color: var(--color-ink-muted);
}

/* ── Song detail page: full lyrics + audio + comments shell ── */
.song {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
  max-inline-size: 70ch;
  padding-block: var(--space-l);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.song__back {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

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

.song__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-block-end: var(--space-m);
  border-block-end: var(--rule-thin);
}

.song__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 500;
  line-height: 1.05;
}

.song__byline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.song__contributor {
  color: var(--color-ink-soft);
}

.song__role-badge {
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.4;
  color: var(--color-ink);
  letter-spacing: 0.04em;
  border: var(--rule-thin);
}

/* Sticky so the wave, markers, and transport ride along while reading the
   feedback below: chips seek without scrolling anywhere, and the pin
   gestures (plus the moment composer, which lives inside this box) stay
   reachable mid-thread. The z-index must clear the natively-layered <audio>
   controls in the content below, same story as the feed's filter rail: 20 on
   the stacking scale at the top of this file, under the masthead's menus. */
.song__player {
  position: sticky;
  inset-block-start: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-block: var(--space-2xs);
  background: var(--color-bg);

  /* The bottom rule earns its keep when stuck: content scrolling under the
     player clips against it, and without an edge the clipped remainder
     reads as debris inside the player block. */
  border-block-end: var(--rule-soft);
}

/* Server-rendered peaks. fill is inherited by the bare <rect> bars. Stretches
   to the player width via the svg's preserveAspectRatio="none". */
.song__waveform {
  display: block;
  inline-size: 100%;
  block-size: var(--space-2xl);
  fill: var(--color-ink);
}

/* player.js wraps the waveform in this track and overlays the played-so-far
   clone + playhead. With no JS the track never appears: the bare waveform and
   the native controls remain the whole player. */
.song__waveform-track {
  position: relative;
  inline-size: 100%;
  cursor: pointer;
}

/* The played layer is a tinted clone of the same bars, revealed left-to-right
   by --played (0..1, set per frame from the audio's timeupdate). clip-path
   keeps the reveal to the bar shapes; the gaps stay clear. */
.song__waveform--played {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 100%;
  pointer-events: none;
  clip-path: inset(0 calc((1 - var(--played, 0)) * 100%) 0 0);
  fill: var(--color-accent);
}

.song__playhead {
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(var(--played, 0) * 100%);
  inline-size: 2px;
  pointer-events: none;
  background: var(--color-ink);
}

/* The wave box hosts the seek track plus the comment-marker layer. Markers
   sit beside (not inside) the track because the track's slider role makes
   its descendants presentational to assistive tech. */
.song__wave {
  position: relative;
}

.song__markers {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* One button per pinned second: a hairline accent tick with a square head,
   centred on its moment. The button box is wider than the tick so it is
   tappable; widths are hairline-scale like the playhead's, not type-scale. */
.song__marker {
  position: absolute;
  inset-block: 0;
  inline-size: 12px;
  padding: 0;
  margin-inline-start: -6px;
  pointer-events: auto;
  cursor: pointer;
  background: transparent;
  border: 0;
}

.song__marker::before {
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(50% - 1px);
  inline-size: 2px;
  content: "";
  background: var(--color-accent);
}

.song__marker::after {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: calc(50% - 3px);
  inline-size: 6px;
  block-size: 6px;
  content: "";
  background: var(--color-accent);
}

/* The transient pin while the moment composer is open: marker visuals,
   nothing else. Inert so a stray click passes through to the wave. */
.song__marker--pending {
  pointer-events: none;
  opacity: 0.55;
}

/* The pin-gesture row between the wave and the native controls. */
.song__pin-actions {
  align-items: baseline;
}

.song__pin-button {
  padding-block: 0;
  padding-inline: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.8;
  color: var(--color-ink);
  letter-spacing: 0.04em;
  cursor: pointer;
  background: transparent;
  border: var(--rule-thin);
}

.song__pin-button:disabled {
  color: var(--color-ink-muted);
  cursor: default;
}

.song__pin-button[aria-pressed="true"] {
  color: var(--color-bg);
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.song__place-hint {
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

/* Armed placement mode: the wave advertises that the next click pins. */
.song__waveform-track--placing {
  cursor: crosshair;
  outline: var(--focus-ring);
  outline-offset: 2px;
}

.song__audio {
  inline-size: 100%;
}

.song__section-heading {
  padding-block-end: var(--space-2xs);
  margin-block: 0 var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
  border-block-end: var(--rule-soft);
}

/* "About this song" prose: goldmark output, read as a short essay. */
.song__description {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--color-ink-soft);
}
.song__description p {
  margin-block: var(--space-s);
}
.song__description p:first-child {
  margin-block-start: 0;
}

/* Lyrics block is goldmark output. Hard-wraps mean every newline becomes a
   <br>, so paragraphs read like a printed lyric sheet, and ## headings turn
   into per-section labels. */
.song__lyrics {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--color-ink);
}
.song__lyrics p {
  margin-block: var(--space-s);
}
.song__lyrics h2 {
  margin-block: var(--space-l) var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}
.song__lyrics h2:first-child {
  margin-block-start: 0;
}

.song__edit-link {
  margin-inline-start: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
.song__delete-link {
  margin-inline-start: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-accent);
  letter-spacing: 0.04em;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

/* ── Feedback / comments ─────────────────────────────────── */
.song__comment-count {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-soft);
}

.comments {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  padding: 0;
  margin-block: 0 var(--space-l);
  list-style: none;
}

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

.comments__replies {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-inline-start: var(--space-m);
  margin-block: var(--space-2xs) 0;
  list-style: none;
  border-inline-start: var(--rule-soft);
}

.comment {
  font-family: var(--font-serif);
  color: var(--color-ink);
}

.comment__byline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  align-items: baseline;
  margin-block: 0 var(--space-3xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
}

/* Anchor jumps (the post-comment redirect lands on #comment-N) must clear
   the sticky player pinned to the viewport top; three wave-heights
   approximates the stuck stack of wave + pin row + native controls. */
.comment {
  scroll-margin-block-start: calc(3 * var(--space-2xl));
}

.comment__author {
  font-weight: 600;
  color: var(--color-ink);
}

.comment__role-badge {
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.4;
  color: var(--color-ink);
  letter-spacing: 0.04em;
  border: var(--rule-thin);
}

/* The m:ss pin on a timestamped comment: same square badge shape as the role
   badge, accent-coloured so it reads as a place in the track, not a label.
   It's a button (click seeks the player there), so flatten the UA styling. */
.comment__at {
  padding-block: 0;
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.4;
  color: var(--color-accent);
  letter-spacing: 0.04em;
  cursor: pointer;
  background: transparent;
  border: var(--rule-thin);
  border-color: currentColor;
}

.comment__posted,
.comment__edited {
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.comment__body {
  margin: 0;
  line-height: 1.5;
}
.comment__body p {
  margin-block: var(--space-3xs);
}

.comment__tombstone {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.comments__empty,
.comments__locked {
  margin-block: 0 var(--space-l);
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--color-ink-muted);
}

.comments__reply > summary {
  inline-size: max-content;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-accent);
  letter-spacing: 0.04em;
  cursor: pointer;
}

/* Post / reply form */
.comment-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-block-start: var(--space-s);
}

/* The moment composer sits in flow below the player (never floated over the
   wave; phones are primary), boxed by the same hairline rule as the badges;
   the accent m:ss readout inside ties it to the pin above. The [hidden]
   rule is load-bearing: .comment-form's display: flex would otherwise beat
   the UA stylesheet's display: none for the hidden attribute. */
.comment-form--moment {
  padding: var(--space-xs);
  border: var(--rule-thin);
}

.comment-form--moment[hidden] {
  display: none;
}

.moment-form__head {
  display: flex;
  gap: var(--space-2xs);
  align-items: baseline;
  margin: 0;
  font-size: var(--step--1);
}

.moment-form__time {
  font-family: var(--font-mono);
  color: var(--color-accent);
}

.moment-form__nudge {
  padding-block: 0;
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.6;
  color: var(--color-ink);
  cursor: pointer;
  background: transparent;
  border: var(--rule-thin);
}

.comment-form__cancel {
  align-self: start;
  padding: var(--space-2xs) var(--space-m);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink);
  letter-spacing: 0.04em;
  cursor: pointer;
  background: transparent;
  border: var(--rule-thin);
}
.comment-form--reply {
  margin-block-start: var(--space-2xs);
}

.comment-form__label {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.comment-form__input {
  inline-size: 100%;
  padding: var(--space-2xs);
  font-family: var(--font-serif);
  font-size: var(--step-0);
  color: var(--color-ink);
  resize: vertical;
  background: var(--color-bg);
  border: var(--rule-thin);
}

.comment-form__submit {
  align-self: start;
  padding: var(--space-2xs) var(--space-m);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-bg);
  letter-spacing: 0.04em;
  cursor: pointer;
  background: var(--color-ink);
  border: var(--rule-thin);
}

.comment-form__error {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent);
}

/* Per-comment edit / delete controls */
.comment__actions {
  display: flex;
  gap: var(--space-m);
  margin-block-start: var(--space-3xs);
}

.comment__action > summary {
  inline-size: max-content;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
  cursor: pointer;
}

.comment__delete-prompt {
  margin-block: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

/* ── Notifications bell (masthead) ───────────────────────── */
.notif {
  position: relative;
}

/* Inherits the masthead nav's sans so it sits flush with the page links;
   only the count badge keeps mono (numerals). */
.notif__summary {
  display: inline-flex;
  gap: var(--space-3xs);
  align-items: baseline;
  font-size: var(--step--1);
  color: var(--color-ink);
  cursor: pointer;
  list-style: none;
}
.notif__summary::-webkit-details-marker {
  display: none;
}

/* Dismiss film: while the popover is open, a faint wash over the rest of
   the page signals that clicking anywhere outside closes it. As a pseudo-
   element of the <details>, clicks on it hit-test to the details itself,
   which notifications.js reads as "outside" and closes. The summary is
   lifted above the film so the bell stays crisp and its native toggle
   keeps receiving clicks; the panel already sits at z-index 10. Both values
   are local to the masthead's stacking context (see the stacking scale at the
   top of this file), so the film covers the page and the panel clears it
   without either needing to know about the page's sticky chrome. */
.notif[open]::before {
  position: fixed;
  inset: 0;
  z-index: 9;
  content: "";
  background: color-mix(in srgb, var(--color-ink) 8%, transparent);
}

.notif[open] > .notif__summary {
  position: relative;
  z-index: 10;
}

/* Caret signalling the summary opens a dropdown; flips when the panel is open. */
.notif__chevron::before {
  display: inline-block;
  font-size: var(--step--2);
  content: "\25BE"; /* ▾ */
  transition: transform 0.12s ease;
}
.notif[open] .notif__chevron::before {
  transform: rotate(180deg);
}

.notif__count {
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.3;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border: var(--rule-thin);
}

.notif__panel {
  position: absolute;
  inset-block-start: calc(100% + var(--space-2xs));
  inset-inline-end: 0;
  z-index: 10;
  inline-size: 22rem;
  max-inline-size: 90vw;
  max-block-size: 24rem;
  padding: var(--space-2xs);
  overflow-y: auto;
  background: var(--color-bg);
  border: var(--rule-thin);
}

.notif__list {
  display: flex;
  flex-direction: column;
  padding: 0;
  margin: 0;
  list-style: none;
}

.notif__item {
  border-block-end: var(--rule-soft);
}
.notif__item:last-child {
  border-block-end: none;
}
.notif__item--unread {
  background: var(--color-accent-wash, transparent);
}

.notif__link {
  display: block;
  padding: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  line-height: 1.4;
  color: var(--color-ink);
  text-decoration: none;
}
.notif__link:hover {
  text-decoration: underline;
}

.notif__actor,
.notif__song {
  font-weight: 600;
}

.notif__empty {
  margin: var(--space-2xs);
  font-family: var(--font-serif);
  font-size: var(--step--1);
  font-style: italic;
  color: var(--color-ink-muted);
}

/* Foot of the panel: the explicit mark-all-read control. Ruled off from the
   entries above so it reads as an action on the list, not another entry. */
.notif__actions {
  display: flex;
  justify-content: flex-end;
  padding-block-start: var(--space-2xs);
  margin: 0;
  border-block-start: var(--rule-soft);
}
/* notifications.js hides this once the inbox is clear, and the display above
   beats the UA's [hidden] rule on specificity, so restate the hiding here. */
.notif__actions[hidden] {
  display: none;
}

.notif__mark-all {
  padding-block: var(--space-3xs);
  padding-inline: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
  border: var(--rule-thin);
}
.notif__mark-all:hover {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

/* ── Manage dropdown (masthead) ──────────────────────────── */
/* Same details-popover pattern as the notifications bell above; menu.js
   handles click-away via the [open]::before film. */
.manage-menu {
  position: relative;
}

.manage-menu__summary {
  display: inline-flex;
  gap: var(--space-3xs);
  align-items: baseline;
  font-size: var(--step--1);
  color: var(--color-ink);
  cursor: pointer;
  list-style: none;
}
.manage-menu__summary::-webkit-details-marker {
  display: none;
}

.manage-menu[open]::before {
  position: fixed;
  inset: 0;
  z-index: 9;
  content: "";
  background: color-mix(in srgb, var(--color-ink) 8%, transparent);
}

.manage-menu[open] > .manage-menu__summary {
  position: relative;
  z-index: 10;
}

.manage-menu__chevron::before {
  display: inline-block;
  font-size: var(--step--2);
  content: "\25BE"; /* ▾ */
  transition: transform 0.12s ease;
}
.manage-menu[open] .manage-menu__chevron::before {
  transform: rotate(180deg);
}

.manage-menu__panel {
  position: absolute;
  inset-block-start: calc(100% + var(--space-2xs));
  inset-inline-end: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  min-inline-size: 12rem;
  padding: var(--space-2xs);
  background: var(--color-bg);
  border: var(--rule-thin);
}

.manage-menu__link {
  font-size: var(--step--1);
  color: var(--color-ink);
  text-decoration: none;
}
.manage-menu__link:hover {
  color: var(--color-accent);
}

/* On phones the masthead nav wraps, so the bell sits mid-row and a panel
   anchored to the bell's right edge runs off the left of the screen. Anchor
   it to the masthead instead and span the viewport (with gutters) so it
   always stays on-screen. The manage menu gets the same treatment. */
@media (max-width: 40rem) {
  /* The masthead is position: relative site-wide now (it carries the stacking
     layer), so the panels below can anchor to it here without repeating it. */

  /* The nav wraps on phones (flex-wrap), so the decorative vertical rules
     would dangle at row breaks and their gaps eat width the admin nav can't
     spare. Drop them here; they still fence page links from the account area
     on wider screens where the nav stays on one row. */
  .masthead__divider {
    display: none;
  }
  .notif,
  .manage-menu,
  .account-menu {
    position: static;
  }
  .notif__panel,
  .manage-menu__panel,
  .account-menu__panel {
    inset-block-start: calc(100% + var(--space-3xs));
    inset-inline: var(--space-s);
    inline-size: auto;
    max-inline-size: none;
  }
}

/* ── Auth pages: signup and login forms ──────────────────── */
.auth {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  max-inline-size: 30rem;
  padding-block: var(--space-xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.auth__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-3);
  font-weight: 500;
}

.auth__lede {
  margin: 0;
  font-family: var(--font-serif);
  color: var(--color-ink-soft);
}

.auth__error {
  padding: var(--space-s);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent);
  background: rgb(from var(--color-accent) r g b / 0.05);
  border: var(--rule-thin);
  border-color: var(--color-accent);
}

.auth__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}

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

.auth__label {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-soft);
}

.auth__input {
  padding: var(--space-2xs) var(--space-xs);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  color: var(--color-ink);
  background: var(--color-surface);
  border: var(--rule-thin);
}

.auth__input:focus {
  outline: var(--focus-ring);
  outline-offset: -2px;
}

.auth__hint {
  font-family: var(--font-sans);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.auth__submit {
  align-self: start;
  padding: var(--space-xs) var(--space-m);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

.auth__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* Passkey sign-in: a secondary (outline) button so the password "Sign in"
   stays the primary accent-filled action. The "or" sets it apart. */
.auth__passkey {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  align-items: start;
}

.auth__or {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.auth__passkey-button {
  padding: var(--space-xs) var(--space-m);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-ink);
  cursor: pointer;
  background: var(--color-bg);
  border: var(--rule-thin);
}

.auth__passkey-button:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* Signup's password fields sit behind a disclosure once the passkey option
   is revealed. Rendered open with the summary hidden (a plain form) until
   signup-passkey.js flips both. */
.auth__password > * + * {
  margin-block-start: var(--space-s);
}

.auth__password-summary {
  width: fit-content;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  cursor: pointer;
}

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

.auth__alt {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.auth__note {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-0);
  line-height: 1.5;
  color: var(--color-ink-soft);
}

/* ── Landing: anonymous home page ─────────────────────────── */
.landing {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  max-inline-size: 36rem;
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.landing__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-5);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.landing__lede {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--color-ink-soft);
}

.landing__cta {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-0);
}

.landing__cta-divider {
  padding-inline: var(--space-2xs);
  color: var(--color-ink-muted);
}

/* ── About page ────────────────────────────────────────────── */
.about {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  max-inline-size: 36rem;
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.invite {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  max-inline-size: 36rem;
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}
.invite__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-3);
}
.invite__copy {
  margin: 0;
}
.invite__cancel {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.about__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.about__copy {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--color-ink-soft);
}

.about__aside {
  font-size: var(--step-0);
  color: var(--color-ink-muted);
}

/* ── Formatting guide (/formatting) ───────────────────────── */
.formatting {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
  max-inline-size: 44rem;
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.formatting__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.formatting__copy {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--color-ink-soft);
}

.formatting__aside {
  font-size: var(--step-0);
  color: var(--color-ink-muted);
}

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

/* Each example is a hairline-topped row; the label sits above a two-up
   "type this / get this" pair. */
.formatting__example {
  padding-block-start: var(--space-m);
  border-block-start: var(--rule-thin);
}

.formatting__label {
  margin-block-end: var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.formatting__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-m);
  align-items: start;
  margin: 0;
}

.formatting__source {
  padding: var(--space-s);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.6;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
  background: var(--color-surface);
  border: var(--rule-thin);
}

/* song__lyrics supplies the type + spacing for headings/paragraphs/etc; the
   frame here just matches the source box beside it. */
.formatting__rendered {
  padding: var(--space-s);
  border: var(--rule-thin);
}

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

.about__toc {
  margin-block: var(--space-m) var(--space-l);
}

.about__toc-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-l);
  padding: 0;
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  list-style: none;
}

/* The three about-page sections (why / ownership / upcoming) share a top rule
   so they read as distinct, anchor-linked blocks. scroll-margin keeps the
   heading clear of the viewport top when reached from the contents links. */
.about__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding-block-start: var(--space-m);
  scroll-margin-block-start: var(--space-m);
  border-block-start: var(--rule-soft);
}

.about__subheading {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 400;
}

.about__upcoming-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-inline-start: var(--space-l);
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-0);
  line-height: 1.5;
  color: var(--color-ink-soft);
}

/* ── Privacy page (/privacy) ──────────────────────────────────
   Its own block, parallel to .about: a prose page with a table of
   contents and hairline-ruled, anchor-linked sections. */
.privacy {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  max-inline-size: 44rem;
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.privacy__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.privacy__copy {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--color-ink-soft);
}

.privacy__aside {
  font-size: var(--step-0);
  color: var(--color-ink-muted);
}

.privacy__toc {
  margin-block: var(--space-2xs) var(--space-s);
}

/* A traditional contents list: one entry per line, numbered, markers aligned in
   their own gutter. It was a wrapping flex row, which read as a tidy cluster at
   six entries and as a ragged block at ten. */
.privacy__toc-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  padding-inline-start: var(--space-l);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  list-style: decimal;
}

.privacy__toc-list li::marker {
  color: var(--color-ink-muted);
}

/* Each section shares a soft top rule so it reads as a distinct, anchor-linked
   block; scroll-margin keeps the heading clear of the viewport top when
   reached from the contents links. */
.privacy__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding-block-start: var(--space-m);
  scroll-margin-block-start: var(--space-m);
  border-block-start: var(--rule-soft);
}

.privacy__subheading {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 400;
}

.privacy__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-inline-start: var(--space-l);
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-0);
  line-height: 1.5;
  color: var(--color-ink-soft);
}

/* ── Group section switcher (Feed | Chat) ──────────────────────
   Two flush, equal square segments. The active one carries an oxide-red
   underline. On the feed it pins to the bottom on mobile (thumb zone); on the
   chat page it stays at the top (the composer owns the bottom there). */
.group-nav {
  display: flex;
  max-inline-size: 78rem;
  margin-inline: auto;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  border-block-end: var(--rule-thin);
}
.group-nav__seg {
  display: inline-flex;
  flex: 1 1 0;
  gap: var(--space-2xs);
  align-items: center;
  justify-content: center;
  padding: var(--space-xs) var(--space-m);
  color: var(--color-ink-muted);
  text-decoration: none;
  border-block-end: 2px solid transparent;
}
.group-nav__seg:hover {
  color: var(--color-accent);
}
.group-nav__seg--active {
  font-weight: 600;
  color: var(--color-ink);
  border-block-end-color: var(--color-accent);
}
/* Unread badge on the Chat segment: small square, oxide accent. */
.group-nav__count {
  display: inline-block;
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

@media (max-width: 40rem) {
  /* On the feed, pin the switcher to the bottom so Chat is in the thumb zone. */
  .group-nav[data-nav-active="feed"] {
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    /* High z-index + its own composited layer (translateZ) so native <audio>
       controls, which Chrome promotes to a compositing layer, don't paint over
       the pinned switcher. 100 on the stacking scale at the top of this file:
       it's pinned to the bottom of the viewport, outside the masthead, so it
       has to clear the masthead layer too. */
    z-index: 100;
    max-inline-size: none;
    padding-block-end: env(safe-area-inset-bottom);
    background: var(--color-bg);
    border-block-start: var(--rule-thin);
    border-block-end: 0;
    isolation: isolate;
    transform: translateZ(0);
  }
  /* Reserve room so the fixed switcher doesn't cover the end of the page. The
     footer is the last element in the flow, so the slack belongs to it. */
  body:has(.group-nav[data-nav-active="feed"]) .site-footer {
    padding-block-end: var(--space-2xl);
  }
}

/* ── Group chat (/g/{slug}/chat) ───────────────────────────────
   Ledger layout from sketch 007 variant A: one column, square monogram
   avatars, your own messages carry a highlight panel + oxide spine. Hard
   radius 0, identity by rule and tint, never rounded bubbles. */
.chat {
  display: flex;
  flex-direction: column;
  max-inline-size: 52rem;
  padding-block: var(--space-s) 0;
  padding-inline: var(--space-m);
  margin-inline: auto;
}

/* The composer + typing line, pinned to the bottom of the viewport while the
   message log scrolls above. Last child of .chat, so sticky bottom keeps it in
   view through the whole history. */
.chat__foot {
  position: sticky;
  inset-block-end: 0;
  padding-block: var(--space-2xs) var(--space-s);
  padding-block-end: max(var(--space-s), env(safe-area-inset-bottom));
  background: var(--color-bg);
}

.chat__header {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-m);
  align-items: baseline;
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thin);
}
.chat__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-3);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.chat__context {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
}

.chat__log {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: 0;
  margin-block: var(--space-s);
  list-style: none;
}

.chat__empty {
  font-family: var(--font-serif);
  font-size: var(--step-0);
  color: var(--color-ink-muted);
}

.chat__error {
  padding: var(--space-2xs) var(--space-s);
  margin-block: 0 var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent);
  border: 1px solid var(--color-accent);
}

/* A single message: square monogram + content. */
.msg {
  display: flex;
  gap: var(--space-s);
  align-items: flex-start;
}
.msg__avatar {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink);
  background: var(--color-bg);
  border: var(--rule-thin);
}
.msg__content {
  flex: 1 1 auto;
  min-inline-size: 0;
}
.msg__byline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  align-items: baseline;
  margin-block: 0 var(--space-3xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
}
.msg__author {
  font-weight: 600;
  color: var(--color-ink);
  text-decoration: none;
}
.msg__author:hover {
  color: var(--color-accent);
}
.msg__you {
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-accent-ink);
  letter-spacing: 0.04em;
  background: var(--color-accent);
}
.msg__role-badge {
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink);
  letter-spacing: 0.04em;
  border: var(--rule-thin);
}
.msg__time {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}
.msg__body {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-0);
  line-height: 1.5;
  color: var(--color-ink);
  overflow-wrap: anywhere;
}
.msg__tombstone {
  font-style: italic;
  color: var(--color-ink-muted);
}
.msg__edited {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

/* Inline edit/delete controls under a message: collapsed <details> that open to
   a small form, mirroring the comment actions. */
.msg__actions {
  display: flex;
  gap: var(--space-s);
  margin-block-start: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
}
.msg__action summary {
  color: var(--color-ink-muted);
  cursor: pointer;
}
.msg__action summary:hover {
  color: var(--color-accent);
}
.msg__action-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-block-start: var(--space-2xs);
}

/* Your own messages: pale highlight panel + an oxide-red spine, square. */
.msg--me .msg__body {
  padding: var(--space-2xs) var(--space-s);
  background: var(--color-highlight);
  border-inline-start: 2px solid var(--color-accent);
}
.msg--me .msg__avatar {
  color: var(--color-surface);
  background: var(--color-ink);
  border-color: var(--color-ink);
}

/* Variant-C grouping: a continuation of the same author's run drops the avatar
   and byline and tucks up closer to the message above it. */
.msg--cont {
  margin-block-start: calc(-1 * var(--space-2xs));
}
.msg--cont .msg__avatar {
  visibility: hidden;
}
.msg--cont .msg__byline {
  display: none;
}

/* An @mention autolink: bold + pale highlight, no underline, so it reads as a
   person rather than a generic link. */
.mention {
  padding-inline: 2px;
  font-weight: 600;
  color: var(--color-ink);
  text-decoration: none;
  background: var(--color-highlight);
}
.mention:hover {
  color: var(--color-accent);
}

/* A #week-N prompt reference, autolinked by renderChatBody to that week's
   archive page. Monospace + accent underline, with a ▸ lead-in, so it reads as
   a pointer to a past prompt rather than a generic link (ported from sketch 007). */
.prompt-ref {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-accent);
  white-space: nowrap;
  text-decoration: none;
  border-block-end: 1px solid var(--color-accent);
}
.prompt-ref::before {
  content: "▸ ";
}

/* Typing indicator: "X is typing" + animated square dots. */
.chat__typing {
  display: flex;
  gap: var(--space-2xs);
  align-items: center;
  min-block-size: 1.5rem;
  margin-block: 0 var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}
/* The class sets display:flex, which would override the UA [hidden] rule, so
   restore hiding explicitly. */
.chat__typing[hidden] {
  display: none;
}
.typing-dots {
  display: inline-flex;
  gap: 3px;
}
.typing-dots span {
  display: inline-block;
  inline-size: 5px;
  block-size: 5px;
  background: var(--color-ink-muted);
  animation: blink 1.2s infinite both;
}
.typing-dots span:nth-child(2) {
  animation-delay: 0.2s;
}
.typing-dots span:nth-child(3) {
  animation-delay: 0.4s;
}
@keyframes blink {
  0%,
  80%,
  100% {
    opacity: 0.2;
  }
  40% {
    opacity: 1;
  }
}

/* Composer: square text field + send button. */
.composer {
  position: relative;
  display: flex;
  gap: var(--space-2xs);
  align-items: stretch;
  padding-block-start: var(--space-s);
  border-block-start: var(--rule-thin);
}
.composer__field {
  flex: 1 1 auto;
  min-block-size: 3rem;
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-serif);
  font-size: var(--step-0);
  resize: vertical;
  background: var(--color-surface);
  border: var(--rule-thin);
}
.composer__btn {
  flex: 0 0 auto;
  padding-inline: var(--space-m);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink);
  background: var(--color-bg);
  border: var(--rule-thin);
}
.composer__btn--send {
  font-weight: 600;
  color: var(--color-surface);
  background: var(--color-ink);
  border-color: var(--color-ink);
}
.composer__btn--send:hover {
  background: var(--color-accent);
  border-color: var(--color-accent);
}

/* Editing a message in place (chat.js sets data-editing on it): the rendered body
   gives way to the edit field, so editing happens where the message sits rather
   than in a second box below it, and the Edit/Delete summaries step aside so
   nothing competes with Save and Cancel. The field takes a row of its own with
   the two buttons beneath it. Sits after .composer__field so the field override
   below wins on source order at higher specificity. With JS off, data-editing is
   never set and the disclosure behaves exactly as it always did. */
[data-editing] .msg__body,
[data-editing] .msg__actions summary {
  display: none;
}
[data-editing] .msg__actions {
  margin-block-start: 0;
}
[data-editing] .msg__action-form {
  flex-flow: row wrap;
  align-items: start;
  margin-block-start: 0;
}
[data-editing] .msg__action-form .composer__field {
  flex-basis: 100%;
}

/* @-mention typeahead: a member-pick dropdown anchored above the composer.
   Built by chat.js; hidden until "@" opens it. Hard radius 0 like everything. */
.mention-menu {
  position: absolute;
  inset-block-end: calc(100% + var(--space-2xs));
  inset-inline: 0;
  z-index: 5;
  max-block-size: 14rem;
  padding: 0;
  margin: 0;
  overflow-y: auto;
  list-style: none;
  background: var(--color-surface);
  border: var(--rule-thin);
}
.mention-menu__item {
  display: flex;
  gap: var(--space-xs);
  align-items: baseline;
  padding: var(--space-2xs) var(--space-s);
  cursor: pointer;
}
.mention-menu__item--active {
  color: var(--color-surface);
  background: var(--color-ink);
}
.mention-menu__handle {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 600;
}
.mention-menu__name {
  font-family: var(--font-sans);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}
.mention-menu__item--active .mention-menu__name {
  color: var(--color-surface);
}

/* ── Card: a single submission in the feed ─────────────────── */
.card {
  position: relative;
  display: grid;
  grid-template-areas:
    "head    meta"
    "audio   audio"
    "markers .";
  grid-template-columns: 1fr auto;
  gap: var(--space-2xs) var(--space-l);
  align-items: start;
  padding-block: var(--space-m);
  border-block-end: var(--rule-soft);
}

.card:last-child {
  border-block-end: 0;
}

.card__head {
  display: flex;
  flex-direction: column;
  grid-area: head;
  gap: var(--space-3xs);
}

.card__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 500;
  line-height: 1.15;
}

.card__title-link {
  color: var(--color-ink);
  text-decoration: none;
}

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

/* Stretch the title link over the whole card so clicking anywhere — markers,
   byline, footer — opens the song (people kept trying to). The only
   interactive child, the audio player, is raised above this overlay below. */
.card__title-link::after {
  position: absolute;
  inset: 0;
  content: "";
}

.card__byline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: baseline;
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.card__contributor {
  color: var(--color-ink-soft);
}

.card__role-badge {
  padding-block: 0;
  padding-inline: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: 1.4;
  color: var(--color-ink);
  letter-spacing: 0.04em;
  border: var(--rule-thin);
}

.card__posted {
  color: var(--color-ink-muted);
}

.card__audio {
  position: relative;
  z-index: 1;
  grid-area: audio;
  inline-size: 100%;
  /* Native player keeps its own visual chrome; nothing to override here
     beyond layout. Browsers will respect color-scheme: light so this
     reads coherently on the warm paper background. z-index lifts it above the
     stretched title-link overlay so play/seek still work. */
}

.card__markers {
  display: flex;
  flex-wrap: wrap;
  grid-area: markers;
  gap: var(--space-xs);
  padding: 0;
  margin: 0;
  list-style: none;
}

.card__marker {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.card__marker--needs-feedback,
.card__marker--needs-listening {
  color: var(--color-accent);
}

.card__meta {
  grid-area: meta;
  align-self: center;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.card__comments {
  font-variant-numeric: tabular-nums;
}

@media (max-width: 40rem) {
  .card {
    grid-template-areas:
      "head"
      "audio"
      "markers"
      "meta";
    grid-template-columns: 1fr;
    gap: var(--space-2xs);
  }
  .card__meta {
    align-self: start;
  }
}

/* ── Verify-email banner ───────────────────────────────────────
   Sits between the masthead and the prompt strip when the signed-in
   user hasn't confirmed their email yet. Resend button posts to
   /verify/resend; the page reloads with ?verification_sent=1 which
   swaps the banner to the confirmation copy. */

.verify-banner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m);
  align-items: center;
  justify-content: space-between;
  padding: var(--space-s) var(--space-m);
  background: var(--color-highlight);
  border-block-end: var(--rule-thin);
}

.verify-banner__copy,
.verify-banner__sent {
  max-inline-size: 60ch;
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink);
}

.verify-banner__sent {
  font-weight: 500;
}

.verify-banner__form {
  margin: 0;
}

.verify-banner__resend {
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-bg);
  letter-spacing: 0.04em;
  cursor: pointer;
  background: var(--color-ink);
  border: 0;
}

.verify-banner__resend:hover {
  background: var(--color-accent);
}

/* ── Submit page (sketch 004 variant B) ────────────────────────
   Two columns: rail with the week context + title + audio
   placeholder + submit, main column with the markdown editor.
   Collapses to single column under 64rem. */

.submit-page {
  display: grid;
  grid-template-columns: 24rem 1fr;
  gap: 0;
  min-block-size: calc(100vh - 5rem);
}

/* Shown instead of the form when the viewer already submitted this week. */
.submit-done {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  max-inline-size: 36rem;
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.submit-done__heading {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-3);
  font-weight: 400;
}

.submit-done__copy {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--color-ink-soft);
}

.submit-done__actions {
  display: flex;
  gap: var(--space-m);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-0);
}

.submit-page__rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  padding: var(--space-l) var(--space-m);
  background: var(--color-bg);
  border-inline-end: var(--rule-thin);
}

.submit-page__week {
  padding-block-end: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  border-block-end: var(--rule-thin);
}

.submit-page__week strong {
  display: block;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--color-ink);
}

.submit-page__prompt {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.3;
}

.submit-page__brief {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.submit-page__divider {
  margin: 0;
  border: 0;
  border-block-start: var(--rule-thin);
}

.submit-page__error {
  padding: var(--space-xs) var(--space-s);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

/* The window-closed notice on late submissions: informative, not alarming. */
.submit-page__late-notice {
  padding-block: var(--space-2xs);
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step--1);
  font-style: italic;
  color: var(--color-ink-muted);
  border-block-start: var(--rule-soft);
}

.submit-page__field {
  display: block;
}

.submit-page__label {
  display: block;
  margin-block-end: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
}

.submit-page__input {
  inline-size: 100%;
  padding: var(--space-xs) var(--space-s);
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.4;
  background: var(--color-surface);
  border: var(--rule-thin);
}

.submit-page__input:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}

/* Audio dropzone is a <label> wrapping the file input so clicking anywhere
   in the dashed box opens the system picker. The input is visually hidden
   but still focusable for keyboard users. */
.submit-page__dropzone {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding: var(--space-s);
  cursor: pointer;
  background: var(--color-surface);
  border: 2px dashed var(--color-ink);
  transition: background 0.15s;
}

.submit-page__dropzone:hover {
  background: var(--color-highlight);
}

.submit-page__file-input {
  font-family: var(--font-sans);
  font-size: var(--step--1);
}

.submit-page__dropzone-cue {
  font-family: var(--font-serif);
  font-size: var(--step-0);
}

.submit-page__dropzone-hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

/* ── In-browser recorder ──────────────────────────────────────────────── */
/* A second way to add audio, inline under the file picker on /submit. Same
   form language as the dropzone: hairline borders, mono labels, hard corners,
   tokens only. Server-rendered hidden; recorder.js reveals it only where
   recording is possible, so unsupported browsers keep the picker alone. */
.recorder {
  margin-block-start: var(--space-2xs);
}

/* Secondary to the picker above: a small outline button that opens the panel. */
.recorder__toggle {
  padding: var(--space-3xs) var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink);
  cursor: pointer;
  background: transparent;
  border: var(--rule-thin);
}

.recorder__toggle:hover {
  color: var(--color-accent-ink);
  background: var(--color-ink);
}

.recorder__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-s);
  background: var(--color-surface);
  border: var(--rule-thin);
}

/* display:flex beats the UA [hidden] rule; restate it so the panel stays
   closed until the toggle opens it. */
.recorder__panel[hidden] {
  display: none;
}

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

.recorder__canvas {
  inline-size: 100%;
  block-size: var(--space-xl);
  /* strokeStyle is read from this element's computed `color`, so the live
     trace tracks the theme instead of a hard-coded hex (see recorder.js). */
  color: var(--color-accent);
  background: var(--color-bg);
  border: var(--rule-soft);
}

.recorder__timer {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-soft);
  letter-spacing: 0.04em;
}

.recorder__button {
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-ink);
  cursor: pointer;
  background: transparent;
  border: var(--rule-thin);
}

.recorder__button:hover {
  color: var(--color-accent-ink);
  background: var(--color-ink);
}

/* Record + Use this take are the primary action in their states: fill accent. */
.recorder__button--record,
.recorder__button--use {
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.recorder__button--record:hover,
.recorder__button--use:hover {
  background: var(--color-ink);
  border-color: var(--color-ink);
}

.recorder__preview {
  inline-size: 100%;
}

/* The base `audio { display: block }` rule beats the UA [hidden] rule, so
   restate it: the preview only shows once a take exists, and Re-record hides
   it again. */
.recorder__preview[hidden] {
  display: none;
}

.recorder__attached {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-soft);
}

.recorder__message {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-accent);
}

.submit-page__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-block-start: auto;
}

/* On /edit the actions sit high in the rail (right after Title) so the primary
   action is above the fold; cancel the auto top-margin that pins them to the
   rail foot on /submit. */
.submit-page__actions--top {
  margin-block-start: 0;
}

.submit-page__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-xs) var(--space-m);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  cursor: pointer;
  background: var(--color-accent);
  border: 0;
}

.submit-page__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

.submit-page__cancel {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-align: center;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

.submit-page__cancel:hover {
  color: var(--color-accent);
}

.submit-page__meta {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.submit-page__main {
  display: flex;
  flex-direction: column;
  padding: var(--space-l) var(--space-m);
}

/* ── Edit page: the heading IS the title field ─────────────────────── */
/* No separate Title box: the title shown at the top of the rail is an
   in-place editable serif input, underlined like a heading; the rule turns
   accent while it's focused. */
.edit-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  padding-block-end: var(--space-2xs);
  border-block-end: var(--rule-thin);
}

.edit-head:focus-within {
  border-block-end-color: var(--color-accent);
}

.edit-head__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.edit-head__title {
  inline-size: 100%;
  padding: 0;
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 500;
  line-height: 1.15;
  color: var(--color-ink);
  background: transparent;
  border: 0;
}

.edit-head__title:focus {
  outline: 0;
}
/* Keyboard focus needs more than the hairline-colour shift on .edit-head; give
   the borderless title input the shared ring. :focus-visible keeps pointer
   clicks clean (the accent underline from :focus-within still cues those). */
.edit-head__title:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

/* ── Edit page: audio lives in the rail, like /submit ──────────────── */
/* A field group in the rail: current track + dropzone + a Replace button.
   It posts to its own form (form="edit-audio-form") so it can stay distinct
   from the Save-changes (title/lyrics) form it sits inside. */
.edit-audio {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.edit-audio__current {
  margin: 0;
  font-size: var(--step--1);
  color: var(--color-ink-soft);
}

/* Secondary to Save changes (the accent button at the foot of the rail): an
   outline button so the two don't compete. */
.edit-audio__submit {
  display: inline-flex;
  align-items: center;
  align-self: start;
  justify-content: center;
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-ink);
  cursor: pointer;
  background: transparent;
  border: var(--rule-thin);
}

.edit-audio__submit:hover {
  color: var(--color-accent-ink);
  background: var(--color-ink);
}

/* Extras: links out to the advanced per-song pages (recording snapshot). */
.edit-extras {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.edit-extras__link {
  align-self: start;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-ink);
}

.edit-extras__hint {
  margin: 0;
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

/* Delete control on the edit page: set apart with a divider so the
   destructive link reads as a deliberate, separate action from the edits
   above it. */
.edit-danger {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-block-start: var(--space-l);
}

.edit-danger__link {
  align-self: start;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-accent);
}

.edit-danger__hint {
  margin: 0;
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

/* Recording snapshot page: a narrow single-column page like /formatting.
   The drop zone + parsed preview grow into this shell. */
.snapshot-page {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
  max-inline-size: 44rem;
  padding-block: var(--space-2xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
}

.snapshot-page__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.snapshot-page__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.snapshot-page__privacy {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--color-ink-soft);
}

.snapshot-page__back {
  align-self: start;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink);
}

/* The drop target: same dashed-border language as the submit dropzone. */
.snapshot-drop__label {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding: var(--space-l) var(--space-s);
  cursor: pointer;
  background: var(--color-surface);
  border: 2px dashed var(--color-ink);
  transition: background 0.15s;
}

.snapshot-drop__label:hover,
.snapshot-drop--over .snapshot-drop__label {
  background: var(--color-highlight);
}

.snapshot-drop__input {
  font-family: var(--font-sans);
  font-size: var(--step--1);
}

.snapshot-drop__cue {
  font-family: var(--font-serif);
  font-size: var(--step-0);
}

.snapshot-drop__hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.snapshot-page__error {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

/* display:flex above beats the UA [hidden] rule; restate it (same as the
   preview block). Covers the error container and its toggled children. */
.snapshot-page__error[hidden],
.snapshot-report[hidden] {
  display: none;
}

.snapshot-page__error-text {
  margin: 0;
}

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

.snapshot-report__copy,
.snapshot-report__done {
  margin: 0;
  font-size: var(--step--2);
}

.snapshot-report__send {
  align-self: start;
  padding: var(--space-3xs) var(--space-xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-accent-ink);
  cursor: pointer;
  background: transparent;
  border: 1px solid currentcolor;
}

.snapshot-report__send[disabled] {
  cursor: default;
  opacity: 0.6;
}

/* The parsed-project review: what would upload, and nothing else. */
.snapshot-preview {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

/* An author display beats the UA's [hidden] rule, so restate it: the
   preview stays gone until the script has something to show. */
.snapshot-preview[hidden] {
  display: none;
}

.snapshot-preview__heading {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 700;
}

.snapshot-preview__subheading {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.snapshot-preview__source {
  margin: 0;
  font-size: var(--step--1);
  color: var(--color-ink-soft);
}

.snapshot-preview__facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-3xs) var(--space-s);
  margin: 0;
}

.snapshot-preview__fact-label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.snapshot-preview__fact-value {
  margin: 0;
  font-size: var(--step--1);
}

.snapshot-preview__tracks,
.snapshot-preview__channels {
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.6;
}

.snapshot-preview__kind {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.snapshot-preview__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-block-start: var(--space-s);
  border-block-start: var(--rule-thin);
}

.snapshot-preview__approve {
  align-self: start;
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-accent-ink);
  cursor: pointer;
  background: var(--color-accent);
  border: 1px solid var(--color-accent);
}

.snapshot-preview__approve[disabled] {
  cursor: default;
  opacity: 0.6;
}

.snapshot-preview__approve-note {
  margin: 0;
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

/* Track sheet on the song-detail page: an attached snapshot, rendered from
   stored JSON. The heading reuses .song__section-heading. */
.track-sheet__facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-3xs) var(--space-s);
  margin-block: 0 var(--space-s);
}

.track-sheet__fact-label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.track-sheet__fact-value {
  margin: 0;
  font-size: var(--step--1);
}

.track-sheet__subheading {
  margin-block: var(--space-s) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.track-sheet__tracks,
.track-sheet__channels {
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.6;
}

.track-sheet__kind {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

/* Signal-flow block diagram (sketch 006, variant D): a server-rendered SVG
   styled entirely with theme tokens (the markup carries no inline colour).
   The scroller keeps a wide graph from blowing out the column. */
.flow-scroller {
  padding: var(--space-s);
  overflow-x: auto;
  background: var(--color-surface);
  border: var(--rule-soft);
}

.flow {
  display: block;
  min-inline-size: 48rem;
  font-family: var(--font-mono);
  /* Raw 12px, not a --step token, on purpose: the server lays out box
     widths against a hard-coded 12px monospace metric (internal/flow
     fontW), so the rendered text size must match that constant or labels
     stop fitting their boxes. */
  font-size: 12px;
}

.flow__edge {
  fill: none;
  stroke: var(--color-ink-soft);
  stroke-width: 1;
}

.flow__edge--send {
  stroke: var(--color-accent);
}

.flow__node rect {
  fill: var(--color-bg);
  stroke: var(--color-rule);
}

.flow__node text {
  fill: var(--color-ink);
}

/* Returns (reverb/delay sends) read as dashed; the submix and output read
   as filled, inverting their label. */
.flow__node--ret rect {
  stroke-dasharray: 3 2;
}

.flow__node--mix rect,
.flow__node--out rect {
  fill: var(--color-ink);
}

.flow__node--mix text,
.flow__node--out text {
  fill: var(--color-bg);
}

/* Click-to-isolate (flow.js): a clicked channel lights its wiring and the
   rest recedes. .is-lit overrides the dim, so the focused subgraph stays at
   full strength. */
.flow__node {
  cursor: pointer;
}

.flow__node,
.flow__edge {
  transition: opacity 0.15s ease;
}

.flow--focus .flow__node,
.flow--focus .flow__edge {
  opacity: 0.15;
}

.flow--focus .is-lit {
  opacity: 1;
}

.submit-page__lyrics-label {
  margin-block-end: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

/* Textarea sits inside .lyric-editor__panel--write which owns the visible
   border + background; the textarea itself is borderless so the panel reads
   as a single field. */
.submit-page__lyrics {
  flex: 1;
  min-block-size: 22rem;
  padding: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step-0);
  line-height: 1.6;
  resize: vertical;
  outline: 0;
  background: transparent;
  border: 0;
}

.submit-page__lyrics:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}

/* "About this song" textarea in the rail: a modest multiline field, smaller
   than the main lyric editor. */
.submit-page__description {
  inline-size: 100%;
  min-block-size: 6rem;
  padding: var(--space-xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  line-height: 1.5;
  resize: vertical;
  background: transparent;
  border: var(--rule-thin);
}
.submit-page__description:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}

.submit-page__lyrics-hint {
  margin-block-start: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.submit-page__lyrics-hint code {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

/* The panel has no padding (the textarea owns its own), so without this the
   hint would sit flush against the panel's left border. Match the textarea's
   inset and give it room below. */
.lyric-editor__panel--write .submit-page__lyrics-hint {
  padding-block-end: var(--space-xs);
  padding-inline: var(--space-s);
}

/* ── Write/Preview tabs ─────────────────────────────────────
   CSS-only tab switching driven by two hidden radios. Preview
   panel is HTMX-populated with goldmark output. The `:has()`
   selector lets the parent react to the checked state without
   any JS. */

.lyric-editor {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-block-size: 0;
}

.lyric-editor__state {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  pointer-events: none;
  opacity: 0;
}

.lyric-editor__tabs {
  display: flex;
  gap: 0;
  border-block-end: var(--rule-thin);
}

.lyric-editor__tab {
  padding: var(--space-2xs) var(--space-s);
  margin-block-end: -1px;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
  cursor: pointer;
  background: var(--color-bg);
  border-block-start: var(--rule-thin);
  border-inline-start: var(--rule-thin);
  border-inline-end: var(--rule-thin);
}

.lyric-editor__tab:hover {
  color: var(--color-accent);
}

/* Active-tab styling derived from the corresponding radio's checked state. */
.lyric-editor:has(#editor-write:checked) .lyric-editor__tab--write,
.lyric-editor:has(#editor-preview:checked) .lyric-editor__tab--preview {
  color: var(--color-ink);
  background: var(--color-surface);
  border-block-end: 1px solid var(--color-surface);
}

/* Keyboard focus indicator. The focusable control is the visually-hidden
   radio, so the ring has to be drawn on its corresponding tab label. */
.lyric-editor:has(#editor-write:focus-visible) .lyric-editor__tab--write,
.lyric-editor:has(#editor-preview:focus-visible) .lyric-editor__tab--preview {
  outline: var(--focus-ring);
  outline-offset: -2px;
}

.lyric-editor__panel {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-block-size: 24rem;
  background: var(--color-surface);
  border-block-end: var(--rule-thin);
  border-inline: var(--rule-thin);
}

.lyric-editor__panel--write {
  display: flex;
}
.lyric-editor__panel--preview {
  display: none;
  padding: var(--space-s);
}

.lyric-editor:has(#editor-preview:checked) .lyric-editor__panel--write {
  display: none;
}
.lyric-editor:has(#editor-preview:checked) .lyric-editor__panel--preview {
  display: block;
}

.submit-page__preview-hint {
  margin: 0;
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--color-ink-muted);
}

/* Visually hide an element but keep it accessible to screen readers. */
.u-visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  white-space: nowrap;
  border: 0;
  clip: rect(0, 0, 0, 0);
}

@media (max-width: 64rem) {
  .submit-page {
    grid-template-columns: 1fr;
  }
  .submit-page__rail {
    border-block-end: var(--rule-thin);
    border-inline-end: 0;
  }
  /* Stacked layout: the editor would otherwise collapse to its content
     height. Let it fill the viewport so there's real room to write, and pin
     the Write/Preview tabs to the top so they stay reachable while scrolling
     the textarea. Background + z-index stop the textarea bleeding through the
     gap beside the tabs while sticky. */
  .submit-page__main {
    padding-block: 0;
  }
  .lyric-editor {
    min-block-size: 100dvh;
  }
  .lyric-editor__tabs {
    position: sticky;
    inset-block-start: 0;
    z-index: 2;
    background: var(--color-bg);
  }
}

/* ── Organiser: author a prompt ───────────────────────────── */
.admin-prompt {
  max-inline-size: 48rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.admin-prompt__title {
  margin-block: 0 var(--space-2xs);
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.admin-prompt__intro {
  max-inline-size: var(--measure);
  padding-block-end: var(--space-m);
  margin-block: 0 var(--space-l);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  border-block-end: var(--rule-thin);
}

.admin-prompt__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}

.admin-prompt__error {
  padding: var(--space-xs) var(--space-s);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

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

.admin-prompt__label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
}

.admin-prompt__hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.admin-prompt__input,
.admin-prompt__textarea {
  inline-size: 100%;
  padding: var(--space-xs) var(--space-s);
  margin-block-start: var(--space-3xs);
  background: var(--color-surface);
  border: var(--rule-thin);
}

.admin-prompt__input {
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.4;
}

.admin-prompt__textarea {
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.5;
  resize: vertical;
}

.admin-prompt__input:focus,
.admin-prompt__textarea:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}

.admin-prompt__submit {
  display: inline-flex;
  align-items: center;
  align-self: start;
  padding: var(--space-xs) var(--space-l);
  margin-block-start: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border: 0;
}

.admin-prompt__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* ── Manager: prompt schedule ─────────────────────────────── */
.admin-schedule {
  max-inline-size: 56rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.admin-schedule__back {
  display: inline-block;
  margin-block-end: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.admin-schedule__back:hover {
  color: var(--color-accent);
}

/* Groups the manage/admin section-switcher links in a named nav landmark
   without changing layout: display:contents leaves the links as direct flex
   children of the page header, so the existing spacing is untouched. */
.manage-nav {
  display: contents;
}

.admin-schedule__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  justify-content: space-between;
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thick);
}

.admin-schedule__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.admin-schedule__new {
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-decoration: none;
  background: var(--color-surface);
  border: var(--rule-thin);
}

.admin-schedule__new:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* Flash confirmation, shared across create/edit flows (see _flash.html).
   Symmetric block margins: flex+gap parents (.song, .admin-detail) space it
   anyway; block-flow parents (.profile, .settings) rely on the end margin. */
.flash {
  padding: var(--space-xs) var(--space-s);
  margin-block: var(--space-m);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  background: var(--color-highlight);
  border-inline-start: var(--rule-thick);
}

.admin-schedule__list {
  padding: 0;
  margin-block: var(--space-m) 0;
  list-style: none;
}

.admin-schedule__row {
  display: grid;
  grid-template-columns: 5rem 13rem 1fr 7rem;
  gap: var(--space-s);
  align-items: baseline;
  padding-block: var(--space-s);
  border-block-start: var(--rule-soft);
}

.admin-schedule__row:last-child {
  border-block-end: var(--rule-soft);
}

.admin-schedule__week {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.admin-schedule__dates {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.admin-schedule__prompt {
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.3;
}

.admin-schedule__status {
  justify-self: start;
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  white-space: nowrap;
  border: var(--rule-thin);
}

.admin-schedule__row[data-status="current"] .admin-schedule__status {
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.admin-schedule__row[data-status="past"] {
  color: var(--color-ink-muted);
}

.admin-schedule__row[data-status="past"] .admin-schedule__status {
  color: var(--color-ink-muted);
  border-color: var(--color-rule-soft);
}

.admin-schedule__empty {
  margin-block: var(--space-l) 0;
  color: var(--color-ink-muted);
}

@media (max-width: 48rem) {
  .admin-schedule__row {
    grid-template-columns: 1fr;
    gap: var(--space-3xs);
  }
}

/* Schedule prompt is a link to the detail; keep it reading as the headline. */
a.admin-schedule__prompt {
  text-decoration: none;
}
a.admin-schedule__prompt:hover {
  color: var(--color-accent);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

/* ── Manage members: roster + invites ─────────────────────── */
.manage-members {
  max-inline-size: 56rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.manage-members__back {
  display: inline-block;
  margin-block-end: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-members__back:hover {
  color: var(--color-accent);
}

.manage-members__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  justify-content: space-between;
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thick);
}

.manage-members__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.manage-members__prompts-link {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-members__prompts-link:hover {
  color: var(--color-accent);
}

.manage-members__section {
  margin-block-start: var(--space-l);
}

.manage-members__subtitle {
  margin: 0 0 var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--color-ink-muted);
}

.manage-members__list,
.manage-members__invites {
  padding: 0;
  margin: 0;
  list-style: none;
}

.manage-members__row,
.manage-members__invite {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  padding-block: var(--space-s);
  border-block-start: var(--rule-soft);
}

.manage-members__row:last-child,
.manage-members__invite:last-child {
  border-block-end: var(--rule-soft);
}

.manage-members__name {
  font-family: var(--font-serif);
  font-size: var(--step-1);
}

.manage-members__handle {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-members__role,
.manage-members__invite-role {
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  border: var(--rule-thin);
}

.manage-members__row[data-role="manager"] .manage-members__role,
.manage-members__invite[data-role="manager"] .manage-members__invite-role {
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.manage-members__invite-to {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.manage-members__invite-kind {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.manage-members__invite-url {
  flex: 1 1 18rem;
  min-inline-size: 0;
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  background: var(--color-surface);
  border: var(--rule-thin);
}

/* The remove/revoke forms sit at the row's end. */
.manage-members__remove,
.manage-members__revoke {
  margin-inline-start: auto;
}

.manage-members__remove-btn,
.manage-members__revoke-btn {
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  cursor: pointer;
  background: var(--color-surface);
  border: var(--rule-thin);
}

.manage-members__remove-btn:hover,
.manage-members__revoke-btn:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* Invite forms: label stacks over its control, laid out inline via l-cluster. */
.manage-members__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.manage-members__label {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.manage-members__input,
.manage-members__select {
  padding: var(--space-2xs) var(--space-xs);
  font: inherit;
  font-size: var(--step--1);
  background: var(--color-bg);
  border: var(--rule-thin);
}

.manage-members__submit {
  align-self: end;
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  cursor: pointer;
  background: var(--color-surface);
  border: var(--rule-thin);
}

.manage-members__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

.manage-members__empty {
  margin-block: var(--space-s) 0;
  color: var(--color-ink-muted);
}

/* ── Organiser: prompt detail ─────────────────────────────── */
.admin-detail {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  max-inline-size: 48rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.admin-detail__back {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.admin-detail__back:hover {
  color: var(--color-accent);
}

.admin-detail__head {
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thick);
}

.admin-detail__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  margin-block: 0 var(--space-xs);
}

.admin-detail__week {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.admin-detail__dates {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.admin-detail__status {
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  border: var(--rule-thin);
}

.admin-detail__status[data-status="current"] {
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.admin-detail__status[data-status="past"] {
  color: var(--color-ink-muted);
  border-color: var(--color-rule-soft);
}

.admin-detail__prompt {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.admin-detail__section {
  margin: 0;
}

.admin-detail__label {
  margin-block: 0 var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.admin-detail__text {
  max-inline-size: var(--measure);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.6;
  white-space: pre-wrap;
}

.admin-detail__edit {
  align-self: start;
  padding: var(--space-xs) var(--space-l);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  text-decoration: none;
  background: var(--color-accent);
}

.admin-detail__edit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* Detail actions: edit + remove-from-queue (upcoming only). */
.admin-detail__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m);
  align-items: start;
}

/* Expand-to-confirm remove: pure <details>, no JS, no one-click delete. */
.admin-detail__remove {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.admin-detail__remove-toggle {
  padding-block: var(--space-xs);
  color: var(--color-ink-muted);
  cursor: pointer;
  list-style: none;
}

.admin-detail__remove-toggle::-webkit-details-marker {
  display: none;
}

.admin-detail__remove-toggle:hover,
.admin-detail__remove[open] .admin-detail__remove-toggle {
  color: var(--color-accent);
}

.admin-detail__remove-confirm {
  max-inline-size: 28rem;
  padding: var(--space-s);
  margin-block-start: var(--space-2xs);
  border: 1px solid var(--color-accent);
}

.admin-detail__remove-warning {
  margin-block: 0 var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-soft);
}

.admin-detail__remove-button {
  padding: var(--space-2xs) var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-accent-ink);
  cursor: pointer;
  background: var(--color-accent);
  border: 0;
}

.admin-detail__remove-button:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* ── Live arrival cue ─────────────────────────────────────── */
/* A comment or a chat message that streamed in over SSE while you were watching
   (chat.js / song-comments.js stamp data-live-new on it) gets a pale highlight
   panel and an accent spine that fade away over a couple of seconds, so a new
   item reads as new instead of silently appearing mid-page. The spine is an
   inset box-shadow rather than a border so nothing reflows as it fades. The
   attribute stays put once the fade is done: it also records "this arrived while
   you were watching", which is the cue a reduced-motion viewer keeps instead
   (see the reduced-motion block below). */
[data-live-new] {
  animation: live-new 2.4s ease-out both;
}

@keyframes live-new {
  from {
    background-color: var(--color-highlight);
    box-shadow: inset 2px 0 0 0 var(--color-accent);
  }
  to {
    background-color: transparent;
    box-shadow: inset 2px 0 0 0 transparent;
  }
}

/* ── Loading / pending states ────────────────────────────── */
/* A small hard-edged (radius 0) square spinner, in keeping with the
   no-rounded-corners rule. Inherits currentColor so it tints to its context. */
.spinner {
  display: inline-block;
  inline-size: 0.7em;
  block-size: 0.7em;
  margin-inline-end: 0.45em;
  vertical-align: -0.06em;
  border: 2px solid currentColor;
  border-block-start-color: transparent; /* the gap makes the rotation legible */
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
/* Honour a reduced-motion preference. The canonical universal !important reset
   trips the project's no-!important lint, so instead still each motion source
   by name. This block sits after all of them in source order, so the matching
   selectors win on order alone. Add new animated selectors here when they land. */
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none;
  }
  /* No fade for a live arrival: hold the tint still instead, so the cue is
     there without motion. It reads as "arrived while you were watching" for the
     rest of the page's life, which is the honest meaning of the attribute. */
  [data-live-new] {
    background-color: var(--color-highlight);
    box-shadow: inset 2px 0 0 0 var(--color-accent);
    animation: none;
  }
  .account-menu__chevron::before,
  .notif__chevron::before,
  .manage-menu__chevron::before,
  .submit-page__dropzone,
  .snapshot-drop__label,
  .flow__node,
  .flow__edge {
    transition: none;
  }
}

/* A submit button mid-request (forms.js sets aria-busy + disabled): keep it
   legible but clearly inert. */
button[aria-busy="true"] {
  cursor: progress;
  opacity: 0.7;
}

/* HTMX swap indicator (feed filters, lyric preview). Hidden until htmx marks
   the request in flight, then shown — display toggling (not just opacity) so it
   reserves no space when idle and can't shift the layout. */
.htmx-indicator {
  display: none;
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}
.htmx-indicator.htmx-request {
  display: inline-flex;
}

/* ── Error pages (404 / 500 / 403) ───────────────────────── */
.error-page {
  max-inline-size: 32rem;
  padding-block: var(--space-2xl) var(--space-xl);
  padding-inline: var(--space-l);
  margin-inline: auto;
  text-align: center;
}
.error-page__status {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step-3);
  font-variant-numeric: tabular-nums;
  color: var(--color-ink-muted);
}
.error-page__heading {
  margin-block: var(--space-2xs) var(--space-s);
  font-family: var(--font-serif);
  font-size: var(--step-2);
  color: var(--color-ink);
}
.error-page__message {
  margin-block: 0 var(--space-l);
  font-family: var(--font-sans);
  color: var(--color-ink-soft);
}
.error-page__back {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-accent);
}

/* ── Member profile (/u/{handle}) — sketch 005, variant A ───── */
.profile {
  padding-block: var(--space-l) var(--space-2xl);
}
.profile__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-l);
  align-items: flex-start;
  padding-block-end: var(--space-l);
  border-block-end: var(--rule-thin);
}
.profile__identity {
  flex: 1 1 20rem;
}
.profile__name {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.01em;
}
.profile__handle {
  margin-block: var(--space-3xs) 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}
.profile__bio {
  margin-block: var(--space-l) 0;
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.4;
}

/* Avatar: auto initials on a handle-derived colour. Square (hard radius). */
.avatar {
  display: grid;
  flex-shrink: 0;
  place-items: center;
  inline-size: 120px;
  block-size: 120px;
  font-family: var(--font-serif);
  font-size: var(--step-3);
  font-weight: 600;
  line-height: 1;
  color: #fff;
  user-select: none;
  background: var(--avatar-bg, var(--color-ink-soft));
}
.avatar--0 {
  --avatar-bg: #4a6c6f;
}
.avatar--1 {
  --avatar-bg: #8a4b2f;
}
.avatar--2 {
  --avatar-bg: #5b6b73;
}
.avatar--3 {
  --avatar-bg: #6b6234;
}
.avatar--4 {
  --avatar-bg: #7a4a5a;
}
.avatar--5 {
  --avatar-bg: #3f5a4a;
}
.avatar--6 {
  --avatar-bg: #b8330a;
}
.avatar--7 {
  --avatar-bg: #4f5a6e;
}

/* Music links: labelled monochrome marks, oxide on hover. */
.links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-m);
  margin-block-start: var(--space-m);
}
.link {
  display: inline-flex;
  gap: var(--space-3xs);
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-decoration: none;
}
.link:hover .link__platform {
  color: var(--color-accent);
}
.icon {
  display: inline-block;
  flex-shrink: 0;
  inline-size: 1.15em;
  block-size: 1.15em;
  vertical-align: -0.15em;
  fill: currentColor;
}
.svg-sprite {
  display: none;
}

/* Catalogue: one row per song, reusing the feed/archive register. */
.catalogue {
  margin-block-start: var(--space-xl);
}
.catalogue__heading {
  margin: 0 0 var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--color-ink-muted);
  letter-spacing: 0.06em;
}
.catalogue__list {
  padding: 0;
  margin: 0;
  list-style: none;
}
.song-row {
  display: grid;
  grid-template-columns: 6rem 1fr auto;
  gap: var(--space-m);
  align-items: baseline;
  padding-block: var(--space-s);
  text-decoration: none;
  border-block-start: var(--rule-soft);
}
.song-row__week {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}
.song-row__title {
  font-family: var(--font-serif);
  font-size: var(--step-1);
}
.song-row:hover .song-row__title {
  color: var(--color-accent);
}
.song-row__meta {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  white-space: nowrap;
}
.catalogue__empty {
  padding-block: var(--space-s);
  font-family: var(--font-serif);
  color: var(--color-ink-muted);
  border-block-start: var(--rule-soft);
}

@media (max-width: 40rem) {
  .song-row {
    grid-template-columns: 1fr;
    gap: var(--space-3xs);
  }
}

/* Edit-profile affordance on own profile head. */
.profile__edit {
  align-self: start;
  padding: var(--space-3xs) var(--space-xs);
  margin-inline-start: auto;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  white-space: nowrap;
  text-decoration: none;
  border: var(--rule-thin);
}
.profile__edit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* ── Profile edit / account settings (/settings) ────────────── */
.settings {
  max-inline-size: 48rem;
  padding-block: var(--space-l) var(--space-2xl);
}
.settings__title {
  margin: 0 0 var(--space-m);
  font-family: var(--font-serif);
  font-size: var(--step-3);
  font-weight: 700;
}
.settings__error {
  padding: var(--space-2xs) var(--space-s);
  margin-block: 0 var(--space-m);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}
/* Every section's legend notches into its own top rule. The fieldset owns that
   rule on purpose: when the rule came from the wrapping form/section instead
   (via .settings + .settings), the legend of a section that started a new
   wrapper rendered *below* the line while the rest sat in it. */
.settings__section {
  padding-block: var(--space-m);
  margin: 0;
  border: 0;
  border-block-start: var(--rule-thin);
}
.settings__legend {
  padding: 0;
  margin-block-end: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  letter-spacing: 0.06em;
}
.settings__hint {
  max-inline-size: var(--measure);
  margin-block: 0 var(--space-s);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}
.settings__danger-link {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-accent);
}
.settings__avatar {
  display: flex;
  gap: var(--space-s);
  align-items: center;
  margin-block-end: var(--space-m);
}
.settings__avatar .avatar {
  inline-size: 64px;
  block-size: 64px;
  font-size: var(--step-1);
}
.settings__actions {
  display: flex;
  gap: var(--space-m);
  align-items: center;
  padding-block-start: var(--space-m);
  border-block-start: var(--rule-thin);
}
.settings__cancel {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}
.settings__passkeys-intro,
.settings__passkeys-empty {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}
.settings__passkey-list {
  padding: 0;
  margin: 0;
  list-style: none;
}
.settings__passkey-row {
  display: flex;
  gap: var(--space-s);
  align-items: baseline;
  justify-content: space-between;
  padding-block: var(--space-2xs);
  border-block-end: var(--rule-soft);
}
.settings__passkey-row:last-child {
  border-block-end: none;
}
.settings__passkey-label {
  font-family: var(--font-serif);
  font-size: var(--step-0);
}
.settings__passkey-meta {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}
.settings__passkey-rename {
  display: flex;
  gap: var(--space-2xs);
  align-items: baseline;
}
.settings__passkey-name {
  inline-size: 12rem;
  max-inline-size: 100%;
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  background: var(--color-surface);
  border: var(--rule-thin);
}
.settings__passkey-remove-btn,
.settings__passkey-save-btn {
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  cursor: pointer;
  background: var(--color-surface);
  border: var(--rule-thin);
}
.settings__passkey-remove-btn:hover,
.settings__passkey-save-btn:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}
.field {
  display: block;
}
.field + .field {
  margin-block-start: var(--space-m);
}
.field__label {
  display: block;
  margin-block-end: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
}
.field__hint {
  display: block;
  margin-block-start: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}
.input-text,
.textarea {
  inline-size: 100%;
  padding: var(--space-xs) var(--space-s);
  font-family: var(--font-serif);
  font-size: var(--step-0);
  line-height: 1.4;
  background: var(--color-surface);
  border: var(--rule-thin);
}
.textarea {
  min-block-size: 6rem;
  resize: vertical;
}
.input-text:focus,
.textarea:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}
.link-field {
  display: grid;
  grid-template-columns: 9rem 1fr;
  gap: var(--space-s);
  align-items: center;
}
.link-field + .link-field {
  margin-block-start: var(--space-2xs);
}
.link-field__label {
  display: flex;
  gap: var(--space-3xs);
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--step--1);
}
.button-primary {
  padding: var(--space-xs) var(--space-l);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  color: var(--color-bg);
  background: var(--color-ink);
  border: var(--rule-thin);
}
.button-primary:hover {
  background: var(--color-accent);
  border-color: var(--color-accent);
}

@media (max-width: 40rem) {
  .link-field {
    grid-template-columns: 1fr;
    gap: var(--space-3xs);
  }
}

/* Separate the change-password form from the profile form. */
/* No rule here: each .settings__section draws its own, so the legend notches
   into it. A rule on the wrapper would sit above the first legend instead. */
.settings + .settings {
  padding-block-start: 0;
}

/* ── Manager: shared prompt library ───────────────────────── */
.manage-library {
  max-inline-size: 56rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.manage-library__back {
  display: inline-block;
  margin-block-end: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-library__back:hover {
  color: var(--color-accent);
}

.manage-library__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  justify-content: space-between;
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thick);
}

.manage-library__title {
  margin: 0;
  margin-inline-end: auto;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.manage-library__nav-link {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-library__nav-link:hover {
  color: var(--color-accent);
}

.manage-library__intro {
  max-inline-size: var(--measure);
  margin-block: var(--space-m) 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.manage-library__section {
  margin-block-start: var(--space-l);
}

.manage-library__subtitle {
  margin: 0 0 var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--color-ink-muted);
}

.manage-library__list {
  padding: 0;
  margin: 0;
  list-style: none;
}

.manage-library__entry {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-block: var(--space-m);
  border-block-start: var(--rule-soft);
}

.manage-library__entry:last-child {
  border-block-end: var(--rule-soft);
}

.manage-library__entry-prompt {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1.3;
}

.manage-library__entry-brief {
  max-inline-size: var(--measure);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.5;
}

.manage-library__entry-reqs {
  max-inline-size: var(--measure);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

.manage-library__entry-meta {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.manage-library__entry-author {
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-library__entry-author:hover {
  color: var(--color-accent);
}

/* Per-entry actions, laid out inline via l-cluster. */
.manage-library__entry-controls {
  margin-block-start: var(--space-2xs);
}

.manage-library__use,
.manage-library__edit {
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  text-decoration: none;
  background: var(--color-surface);
  border: var(--rule-thin);
}

.manage-library__use:hover,
.manage-library__edit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

.manage-library__delete-btn {
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  cursor: pointer;
  background: var(--color-surface);
  border: var(--rule-thin);
}

.manage-library__delete-btn:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

.manage-library__empty {
  margin-block: var(--space-l) 0;
  color: var(--color-ink-muted);
}

/* Contribute form: mirrors the new-prompt form (.admin-prompt__*). */
.manage-library__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}

.manage-library__error {
  padding: var(--space-xs) var(--space-s);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

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

.manage-library__label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
}

.manage-library__hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.manage-library__input,
.manage-library__textarea {
  inline-size: 100%;
  padding: var(--space-xs) var(--space-s);
  margin-block-start: var(--space-3xs);
  background: var(--color-surface);
  border: var(--rule-thin);
}

.manage-library__input {
  font-family: var(--font-serif);
  font-size: var(--step-1);
  line-height: 1.4;
}

.manage-library__textarea {
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.5;
  resize: vertical;
}

.manage-library__input:focus,
.manage-library__textarea:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}

.manage-library__submit {
  display: inline-flex;
  align-items: center;
  align-self: start;
  padding: var(--space-xs) var(--space-l);
  margin-block-start: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border: 0;
}

.manage-library__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* ── Site admin: groups overview + create ─────────────────── */
.admin-groups {
  max-inline-size: 56rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.admin-groups__head {
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thick);
}

.admin-groups__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.admin-groups__section {
  margin-block-start: var(--space-l);
}

.admin-groups__subtitle {
  margin: 0 0 var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--color-ink-muted);
}

.admin-groups__intro {
  max-inline-size: var(--measure);
  margin-block: 0 var(--space-m);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
}

/* Create form: mirrors the new-prompt form (.admin-prompt__*). */
.admin-groups__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}

.admin-groups__error {
  padding: var(--space-xs) var(--space-s);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

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

.admin-groups__label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
}

.admin-groups__hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.admin-groups__input,
.admin-groups__select {
  inline-size: 100%;
  padding: var(--space-xs) var(--space-s);
  margin-block-start: var(--space-3xs);
  font: inherit;
  font-size: var(--step-0);
  background: var(--color-surface);
  border: var(--rule-thin);
}

.admin-groups__input:focus,
.admin-groups__select:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}

.admin-groups__submit {
  display: inline-flex;
  align-items: center;
  align-self: start;
  padding: var(--space-xs) var(--space-l);
  margin-block-start: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border: 0;
}

.admin-groups__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}

/* Group list: rows mirror the prompt schedule. */
.admin-groups__list {
  padding: 0;
  margin: 0;
  list-style: none;
}

.admin-groups__row {
  display: grid;
  grid-template-columns: 1fr 1fr 10rem 7rem;
  gap: var(--space-s);
  align-items: baseline;
  padding-block: var(--space-s);
  border-block-start: var(--rule-soft);
}

.admin-groups__row:last-child {
  border-block-end: var(--rule-soft);
}

.admin-groups__name {
  font-family: var(--font-serif);
  font-size: var(--step-1);
  text-decoration: none;
}

.admin-groups__name:hover {
  color: var(--color-accent);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

.admin-groups__slug,
.admin-groups__tz {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.admin-groups__count {
  justify-self: start;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  white-space: nowrap;
}

.admin-groups__empty {
  margin-block: var(--space-l) 0;
  color: var(--color-ink-muted);
}

@media (max-width: 48rem) {
  .admin-groups__row {
    grid-template-columns: 1fr;
    gap: var(--space-3xs);
  }
}

/* ── Manager: group settings (rename + timezone) ──────────── */
.manage-settings {
  max-inline-size: 56rem;
  padding: var(--space-l) var(--space-m) var(--space-2xl);
  margin-inline: auto;
}

.manage-settings__back {
  display: inline-block;
  margin-block-end: var(--space-s);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-settings__back:hover {
  color: var(--color-accent);
}

.manage-settings__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  align-items: baseline;
  padding-block-end: var(--space-s);
  border-block-end: var(--rule-thick);
}

.manage-settings__title {
  margin: 0;
  margin-inline-end: auto;
  font-family: var(--font-serif);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1.1;
}

.manage-settings__nav-link {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-ink-muted);
  text-decoration: none;
}

.manage-settings__nav-link:hover {
  color: var(--color-accent);
}

/* Form mirrors the new-prompt / create-group form idiom. */
.manage-settings__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  margin-block-start: var(--space-l);
}

.manage-settings__error {
  padding: var(--space-xs) var(--space-s);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--color-accent-ink);
  background: var(--color-accent);
}

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

.manage-settings__label {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
}

.manage-settings__hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--color-ink-muted);
}

.manage-settings__input,
.manage-settings__select {
  inline-size: 100%;
  padding: var(--space-xs) var(--space-s);
  margin-block-start: var(--space-3xs);
  font: inherit;
  font-size: var(--step-0);
  background: var(--color-surface);
  border: var(--rule-thin);
}

.manage-settings__input:focus,
.manage-settings__select:focus {
  outline: var(--focus-ring);
  outline-offset: -1px;
}

.manage-settings__submit {
  display: inline-flex;
  align-items: center;
  align-self: start;
  padding: var(--space-xs) var(--space-l);
  margin-block-start: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border: 0;
}

.manage-settings__submit:hover {
  color: var(--color-bg);
  background: var(--color-ink);
}
