:root {
  --bg: #1b1d21;
  --card: #26292e;
  --ink: #e8e8e8;
  --muted: #9aa0a6;
  --accent: #e0913a;
  --accent2: #4a9fd8;  /* importance / performance — blue counterpart to accent */
  --line: #3a3e44;
  /* Native controls — a dropdown's open list, scrollbars, date pickers — follow
     the page's dark scheme instead of the browser's light default. */
  color-scheme: dark;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  font: 16px/1.5 system-ui, sans-serif;
  background: var(--bg);
  color: var(--ink);
}
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: .75rem 1.25rem; background: #15171a;
  border-bottom: 1px solid var(--line);
}
.brand { font-weight: 700; text-decoration: none; color: var(--ink); }
.topbar nav { display: flex; align-items: center; gap: 1rem; }
.topbar a { color: var(--muted); text-decoration: none; }
.topbar a:hover { color: var(--ink); }
.who { color: var(--accent); font-weight: 600; }
.topbar .coffee { background: #ffdd00; color: #15171a; padding: .3rem .6rem; border-radius: 6px; font-weight: 600; white-space: nowrap; }
.topbar .coffee:hover { background: #ffe533; color: #15171a; }
main { max-width: 820px; margin: 0 auto; padding: 1.25rem; }
h1 { margin: .25rem 0 1rem; }
h1.tagline { font-size: 1.3rem; font-weight: 600; color: var(--accent); line-height: 1.3; }
h2 { margin: 0 0 .75rem; font-size: 1.05rem; color: var(--muted); }
.card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; padding: 1rem 1.1rem; margin-bottom: 1.1rem;
}
.card.narrow { max-width: 360px; margin: 3rem auto; }
/* New-user onboarding checklist (rendered by _tutorial.html). */
.card.tutorial { border-color: var(--accent); }
.tutorial-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.tutorial-head h2 { color: var(--accent); }
.tutorial-done { color: var(--accent); }
.tutorial-steps { list-style: none; margin: .75rem 0; padding: 0; }
.tutorial-step { display: flex; gap: .55rem; padding: .3rem 0; align-items: baseline; }
.tutorial-step .tick { color: var(--muted); flex: 0 0 1.1em; }
.tutorial-step.done .tick { color: var(--accent); }
.tutorial-step.done .step-text { color: var(--muted); text-decoration: line-through; }
.tutorial-step.optional .step-text { font-style: italic; }
/* The groups: bandspace, then the free Jam features, then premium. Each is a
   labelled block so the free half reads as a complete thing on its own. */
.tutorial-group + .tutorial-group { margin-top: 1.1rem; border-top: 1px solid var(--border); padding-top: .9rem; }
.tutorial-group-title { font-size: 1rem; margin: 0; }
.tutorial-blurb { margin: .2rem 0 0; }
/* Explanation under a step — what a bandspace is, that you can speak into the
   artist/title fields. Never struck through with the step it belongs to: the
   point of it survives ticking the box. */
.step-note { display: block; color: var(--muted); font-size: .88rem; margin-top: .2rem; text-decoration: none; }
.tutorial-step.done .step-note { text-decoration: none; }
label { display: block; margin: .5rem 0; }
input {
  width: 100%; padding: .5rem .6rem; margin-top: .2rem;
  background: #1b1d21; color: var(--ink);
  border: 1px solid var(--line); border-radius: 6px;
}
/* Dropdowns and text areas share the text inputs' look. Without this they were
   drawn in the browser's light default, the one control on the page that did
   not belong to it. :where() keeps the specificity at zero, so the per-module
   layout rules (widths, margins) still win. */
:where(select, textarea) {
  padding: .5rem .6rem; font: inherit; color: var(--ink);
  background-color: #1b1d21; border: 1px solid var(--line); border-radius: 6px;
}
:where(select) {
  appearance: none; -webkit-appearance: none;
  max-width: 100%; padding-right: 2rem; cursor: pointer; line-height: 1.3;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%239aa0a6' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .7rem center;
}
:where(select):hover { border-color: #565c64; }
:where(input, select, textarea):focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(224,145,58,.25);
}
:where(select) option { background: var(--card); color: var(--ink); }
:where(select):disabled { opacity: .6; cursor: default; }

button {
  cursor: pointer; padding: .45rem .8rem; border-radius: 6px;
  border: 1px solid var(--line); background: #2f3338; color: var(--ink);
}
button:hover { border-color: var(--accent); }
button.link, .link { background: none; border: none; color: var(--muted); padding: 0; }
.inline { display: inline; }
.error { color: #ff8080; }
.hint { color: var(--muted); font-size: .9rem; }
.members { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .6rem; }
.chip {
  display: inline-flex; align-items: center; gap: .35rem; margin: 0;
  padding: .3rem .6rem; background: #1b1d21;
  border: 1px solid var(--line); border-radius: 999px;
}
.chip input { width: auto; margin: 0; }
.filter-actions { display: flex; align-items: center; gap: 1rem; }
.add-song { display: flex; flex-wrap: wrap; gap: .5rem; }
.add-song input { flex: 1 1 140px; width: auto; }
/* Optional voice-input mic button injected by voice.js next to artist/title. */
.add-song .mic { flex: 0 0 auto; padding: .45rem .6rem; font-size: 1rem; line-height: 1; }
.add-song .mic.listening { border-color: var(--accent); background: var(--accent); color: #15171a; animation: micpulse 1s ease-in-out infinite; }
.link-status { margin-left: .4rem; color: var(--accent); }
@keyframes micpulse { 0%,100% { opacity: 1; } 50% { opacity: .55; } }
.list-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.order-controls { display: flex; gap: .4rem; margin-top: .5rem; }
.order-controls button { font-size: .8rem; }
.songlist { list-style: none; padding: 0; margin: 0; }
.song { padding: .7rem 0; border-top: 1px solid var(--line); }
.song:first-child { border-top: none; }
.song-main { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.song-title { font-weight: 600; }
/* Badge marking the band's own creations (set via "This is our own song"). */
.own-badge { font-size: .9em; white-space: nowrap; }
.own-toggle { cursor: pointer; flex: 0 0 auto; }
.own-toggle.on { border-color: var(--accent); color: var(--accent); }
.own-filter { flex: 0 0 auto; }
.links { display: flex; gap: .6rem; }
.links a { color: var(--accent); font-size: .85rem; }
.total, .tally { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }
.total { font-weight: 700; color: var(--ink); }
.rate, .suggest-actions { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.rate-btn { font-size: .85rem; }
.rate-btn.selected { background: var(--accent); color: #15171a; border-color: var(--accent); }
/* Performance / importance rating — blue counterpart to the orange rating. */
.rate.perf { margin-top: .3rem; }
.perf-btn.selected { background: var(--accent2); color: #15171a; border-color: var(--accent2); }
.total.importance { color: var(--accent2); }
/* Only the mode-relevant rating module is rendered (Like in Jam, Importance in
   Band); it shows open. The other is removed from the DOM (see _song_list.html). */
details.rating { margin-top: .5rem; }
details.rating.like[open] > summary { color: var(--accent); }
details.rating.perf[open] > summary { color: var(--accent2); }
/* Global mode switcher: 🎉 Jam / 🎤 Band / 🎫 Gig, one button per mode under the
   tagline. Replaced the old two-state slider when Gig mode made it three-way.
   A locked (premium) mode renders as a link to /premium, not a disabled button —
   see _mode_switch.html for why that isn't a hide-don't-collapse violation. */
.mode-switch { display: flex; align-items: center; gap: .4rem; margin: 0 0 1.1rem; flex-wrap: wrap; }
.mode-btn {
  display: inline-block; padding: .35rem .7rem; border: 1px solid var(--line);
  border-radius: 999px; background: var(--card); color: var(--muted);
  font-weight: 600; font-size: .9rem; text-decoration: none; cursor: pointer;
}
.mode-btn:hover { border-color: var(--accent); color: var(--ink); }
.mode-btn.active { background: var(--accent); border-color: var(--accent); color: #15171a; }
.mode-btn.locked { color: var(--muted); border-style: dashed; }
.mode-btn.locked:hover { border-color: var(--accent); color: var(--accent); }
.mode-btn .lock { font-size: .8rem; }
.importance-hint { color: var(--accent2); }
.promote { border-color: var(--accent); color: var(--accent); }

/* Plan link in the header. Premium reads as earned (accent), Basic as an offer.
   The plan belongs to the bandspace, so the label shows the band's days left. */
.topbar .plan-link { border: 1px solid var(--line); border-radius: 999px; padding: .2rem .55rem; font-size: .85rem; }
.topbar .plan-link.premium { color: var(--accent); border-color: var(--accent); }
.plan-features { margin: .5rem 0 1.2rem; }
.plan-features dt { font-weight: 700; color: var(--accent); margin-top: .7rem; }
.plan-features dd { margin: .2rem 0 0 0; color: var(--ink); }
.plan-badge { color: var(--muted); font-size: .8rem; }
.plan-badge.premium { color: var(--accent); font-weight: 600; }
/* Premium inside its last week: warn without shouting. */
.card.expiring { border-color: var(--accent); }

/* Per-bandspace recording storage quota. */
.storage-line.full { color: #e07a5f; }
.meter { height: 8px; border-radius: 999px; background: var(--line); overflow: hidden; margin: .3rem 0 .5rem; }
.meter-fill { display: block; height: 100%; background: var(--accent); }
.storage-line.full ~ .meter .meter-fill { background: #e07a5f; }

/* One-shot flash for a refused upload. Loud enough not to be missed, since the
   alternative it replaces was silence. */
.flash { padding: .6rem .8rem; border-radius: 8px; margin: 0 0 1rem; }
.flash.error { background: #3a2422; border: 1px solid #e07a5f; color: #ffd9cf; }
.flash.ok { background: #1f2e26; border: 1px solid var(--accent); color: var(--accent); }

/* Recently deleted recordings — the week they stay recoverable. Rendered only
   when there is something in it (app/recordings_trash.py). */
.trash { margin-top: .8rem; padding-top: .6rem; border-top: 1px dashed var(--border); }
.trash h4 { margin: 0 0 .2rem; font-size: .95rem; color: var(--muted); }
.trashlist { list-style: none; margin: .5rem 0 0; padding: 0; }
.trashed-recording { display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; padding: .3rem 0; }
.trashed-recording .rec-name { text-decoration: line-through; color: var(--muted); }

/* Gig mode: the setlist index, the build page, and the stage view. */
.setlists { list-style: none; padding: 0; margin: 0; }
.setlist-row { display: flex; align-items: baseline; gap: .7rem; flex-wrap: wrap; padding: .55rem 0; border-bottom: 1px solid var(--line); }
.setlist-name { font-weight: 600; color: var(--ink); text-decoration: none; font-size: 1.05rem; }
.setlist-name:hover { color: var(--accent); }
.setlist-meta { color: var(--muted); font-size: .85rem; }
.setlist-actions { margin-left: auto; display: flex; gap: .7rem; }
.setlist-head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; }
.stage-link { color: var(--accent); text-decoration: none; font-weight: 600; }
.setlist-items { margin: 0; padding-left: 1.4rem; }
.setlist-item { padding: .6rem 0; border-bottom: 1px solid var(--line); }
.setlist-item .song-pos { color: var(--muted); margin-right: .3rem; }
.add-to-setlist { display: flex; gap: .5rem; flex-wrap: wrap; }
.add-to-setlist select { flex: 1 1 260px; }
.cue-form { display: flex; gap: .5rem; margin-top: .4rem; }
.cue-form input { flex: 1 1 auto; }

/* Stage view — read in bad light, from a distance, mid-song. Big type, high
   contrast, and no controls at all. */
.stage { max-width: 720px; }
.stage-head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.stage-head h1 { margin: 0; }
.stage-head time { color: var(--muted); }
.stage-list { list-style: none; padding: 0; margin: 0; counter-reset: stage; }
.stage-song { display: flex; align-items: baseline; gap: .9rem; padding: .8rem 0; border-bottom: 1px solid var(--line); }
.stage-pos { color: var(--accent); font-size: 1.6rem; font-weight: 700; min-width: 2ch; text-align: right; }
.stage-title { display: flex; flex-direction: column; gap: .1rem; }
.stage-title strong { font-size: 1.45rem; line-height: 1.2; }
.stage-artist { color: var(--muted); font-size: 1rem; }
.stage-cue { color: var(--accent2); font-size: .95rem; }
.stage-links { margin-left: auto; display: flex; gap: .7rem; }
.stage-links a { color: var(--accent); }

/* Edit / delete + comments (FR: editable songs, per-song comment chronology) */
details.song-edit, details.comments { margin-top: .5rem; }
details summary {
  cursor: pointer; color: var(--muted); font-size: .85rem;
  list-style: none; user-select: none;
}
details summary:hover { color: var(--ink); }
details summary::-webkit-details-marker { display: none; }
details[open] > summary { margin-bottom: .5rem; }
.edit-song { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: .5rem; }
.edit-song input { flex: 1 1 140px; width: auto; }
.delete-song, .delete-comment { display: inline; }
button.danger { border-color: #a3434a; color: #ff9aa0; }
button.danger:hover { border-color: #ff8080; background: #3a2326; }

.commentlist { list-style: none; padding: 0; margin: 0 0 .6rem; }
.comment { padding: .5rem 0; border-top: 1px solid var(--line); }
.comment:first-child { border-top: none; }
.comment-meta { display: flex; gap: .6rem; font-size: .8rem; color: var(--muted); }
.comment-author { font-weight: 600; color: var(--accent); }
.comment-body { margin: .25rem 0; white-space: pre-wrap; }
.comment-edit { margin-top: .25rem; }
.add-comment, .edit-comment { display: flex; flex-direction: column; gap: .4rem; align-items: flex-start; }
.add-comment textarea, .edit-comment textarea {
  width: 100%; padding: .5rem .6rem; background: #1b1d21; color: var(--ink);
  border: 1px solid var(--line); border-radius: 6px; font: inherit; resize: vertical;
}
.add-comment button, .edit-comment button { align-self: flex-start; }

/* Private one-line personal note (author-only, collapsed like comments) */
details.notes { margin-top: .5rem; }
.note-form { display: flex; gap: .5rem; }
.note-form input { flex: 1 1 auto; width: auto; }

/* Recordings: phone audio upload, in-app playback, A–B loop */
details.recordings { margin-top: .5rem; }
.recording { padding: .6rem 0; border-top: 1px solid var(--line); }
.recording:first-child { border-top: none; }
.rec-meta { display: flex; gap: .6rem; font-size: .8rem; color: var(--muted); flex-wrap: wrap; }
.rec-author { font-weight: 600; color: var(--accent); }
.rec-name { color: var(--ink); }
.recording audio { width: 100%; margin: .4rem 0; }
.loops { margin: .3rem 0 .2rem; }
.looplist { list-style: none; padding: 0; margin: 0 0 .4rem; }
.loop-item { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; padding: .25rem 0; }
.loop-item.active { background: rgba(224,145,58,.12); border-radius: 6px; padding: .25rem .4rem; }
.play-loop { font-size: .85rem; border-color: var(--accent); color: var(--accent); }
.loop-name { font-weight: 600; }
.loop-range { color: var(--muted); font-size: .85rem; font-variant-numeric: tabular-nums; }
.delete-loop { display: inline; margin: 0 0 0 auto; }
.stop-loop { font-size: .85rem; margin-bottom: .5rem; }
.add-loop { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.add-loop input { width: auto; flex: 1 1 8rem; margin: 0; }
.delete-recording { display: block; margin: .4rem 0 0; }
.upload-recording { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-top: .5rem; }
.upload-recording input[type=file] { width: auto; flex: 1 1 auto; }

/* Bandspaces: the tenant switcher in the header and the /bands page */
.bandspace-link { font-weight: 600; }
.bandlist { list-style: none; padding: 0; margin: 0; }
.bandspace { padding: .7rem 0; border-top: 1px solid var(--line); }
.bandspace:first-child { border-top: none; }
.bandspace.active { background: rgba(224,145,58,.08); border-radius: 8px; padding: .7rem .6rem; }
.band-main { display: flex; flex-wrap: wrap; align-items: baseline; gap: .6rem; }
.band-name { font-weight: 600; font-size: 1.05rem; }
.band-meta { color: var(--muted); font-size: .85rem; }
.band-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-top: .4rem; }
.invite { margin-top: .5rem; }
.invite-code {
  display: inline-block; padding: .3rem .6rem; border: 1px dashed var(--accent);
  border-radius: 6px; font-size: 1.1rem; letter-spacing: .18em;
  font-variant-numeric: tabular-nums; user-select: all;
}
.create-band, .join-band { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.create-band input, .join-band input { flex: 1 1 12rem; width: auto; margin: 0; }
.join-band input { text-transform: uppercase; letter-spacing: .12em; }

/* Reporting: hero numbers, one single-series trend, tables. Bars carry the
   accent; every number and label stays in a text token, so identity never
   depends on colour alone. */
.kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: .8rem; margin-bottom: 1.1rem; }
.kpi {
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: .9rem 1rem; display: flex; flex-direction: column; gap: .15rem;
}
.kpi-value { font-size: 2rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.kpi-label { font-size: .9rem; color: var(--ink); }
.kpi-sub { font-size: .8rem; color: var(--muted); }

.trend { display: flex; align-items: flex-end; gap: 2px; height: 150px; padding-top: 1.2rem; }
.trend-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; position: relative; }
/* 4px rounded data-end, anchored flat to the baseline. */
.trend-bar { width: 100%; max-width: 26px; background: var(--accent); border-radius: 4px 4px 0 0; min-height: 2px; }
.trend-col:hover .trend-bar { background: #edac63; }
.trend-day { font-size: .68rem; color: var(--muted); margin-top: .35rem; font-variant-numeric: tabular-nums; }
.trend-peak { position: absolute; top: -1.1rem; font-size: .75rem; color: var(--ink); font-variant-numeric: tabular-nums; }

.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: .1rem 1.2rem; margin: 0; }
.metrics > div { display: flex; justify-content: space-between; gap: 1rem; padding: .3rem 0; border-bottom: 1px solid var(--line); }
.metrics dt { color: var(--muted); font-size: .9rem; }
.metrics dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }

.table-scroll { overflow-x: auto; }
.report-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.report-table th, .report-table td { padding: .4rem .5rem; border-bottom: 1px solid var(--line); text-align: left; white-space: nowrap; }
.report-table th { color: var(--muted); font-weight: 600; }
.report-table .num { text-align: right; font-variant-numeric: tabular-nums; }

.kindlist { list-style: none; padding: 0; margin: 0; }
.kindlist li { display: grid; grid-template-columns: 11rem 1fr 3rem; align-items: center; gap: .6rem; padding: .25rem 0; }
.kind-name { color: var(--ink); font-size: .9rem; }
.kind-track { background: rgba(255,255,255,.06); border-radius: 4px; height: 10px; }
.kind-bar { display: block; height: 100%; background: var(--accent2); border-radius: 4px; }
.kind-count { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; font-size: .9rem; }
@media (max-width: 560px) { .kindlist li { grid-template-columns: 8rem 1fr 2.5rem; } }

/* Infrastructure page: a diagram plus supporting prose. Text always wears text
   tokens; colour on the diagram carries role (request path vs. data services),
   and every box is labelled, so nothing depends on colour alone. */
.lede { color: var(--muted); max-width: 62ch; margin: 0 0 1.1rem; }
.diagram { margin: 0 0 .8rem; overflow-x: auto; }
.diagram svg { display: block; min-width: 620px; }
.d-title { fill: var(--ink); font: 600 13px system-ui, sans-serif; }
.d-sub { fill: var(--muted); font: 11px system-ui, sans-serif; }
.d-zone { fill: var(--muted); font: 600 12px system-ui, sans-serif; letter-spacing: .04em; }
.d-edge { fill: var(--muted); font: 10.5px system-ui, sans-serif; }

.fact-grid, .stack-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: .9rem 1.4rem; }
.fact h3, .stack-grid h3 { margin: 0 0 .3rem; font-size: .95rem; color: var(--accent); }
.fact p, .stack-grid p { margin: 0; color: var(--muted); font-size: .92rem; }

.pipeline { list-style: none; padding: 0; margin: 0; counter-reset: step; }
.pipeline li { display: flex; gap: .8rem; padding: .5rem 0; border-top: 1px solid var(--line); }
.pipeline li:first-child { border-top: none; }
.step-n {
  flex: 0 0 1.7rem; height: 1.7rem; border-radius: 50%; display: grid; place-items: center;
  background: rgba(224,145,58,.15); color: var(--accent); font-weight: 700; font-size: .85rem;
}
.step-b { color: var(--muted); font-size: .92rem; }
.step-b b { color: var(--ink); font-weight: 600; }

/* /fahrplan: the report table, but cells that hold sentences may wrap. */
.plan-table td { white-space: normal; vertical-align: top; min-width: 7rem; }
.plan-list { margin: .3rem 0 0; padding-left: 1.2rem; color: var(--muted); font-size: .92rem; }
.plan-list li { margin: .25rem 0; }
.plan-list b { color: var(--ink); font-weight: 600; }

/* --- account, legal pages and the site footer ---------------------------- */
/* A prompt, not a wall: an account with no confirmed address keeps working,
   and this is the only thing that says so. Its own toggle is the state of the
   address, so it is absent entirely once confirmed. */
.banner {
  margin: 0; padding: .6rem 1.25rem; background: #33291a; color: #f0c98a;
  border-bottom: 1px solid var(--line); font-size: .92rem;
}
.banner a { color: var(--accent); }

.notice { color: var(--accent); }
.facts { list-style: none; padding: 0; margin: 0 0 .8rem; color: var(--muted); font-size: .93rem; }
.facts li { padding: .15rem 0; }

/* A link that reads as an action — the export download and "set up 2FA". */
a.button {
  display: inline-block; padding: .45rem .8rem; border: 1px solid var(--line);
  border-radius: 8px; color: var(--ink); text-decoration: none; background: #2f333a;
}
a.button:hover { border-color: var(--accent); color: var(--accent); }

.inline-form { display: flex; align-items: flex-end; gap: .5rem; flex-wrap: wrap; margin: .5rem 0; }
.inline-form label { margin: 0; }
.check { display: flex; align-items: center; gap: .5rem; }
.check input { width: auto; margin: 0; }

/* Destructive, and it looks it. Ending a session and erasing an account are
   the two things on the account page that cannot be undone. */
.danger-zone { border-color: #a3434a; }
.danger-zone h2, .danger-zone h3 { color: #ff9aa0; }

/* Shown once, never again — only the hashes are stored. */
.recovery { border-color: var(--accent); }
.codes { list-style: none; padding: 0; margin: .6rem 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.codes code { background: #15171a; padding: .3rem .5rem; border-radius: 6px; letter-spacing: .06em; }
code.secret { font-size: 1.1rem; letter-spacing: .12em; }
code.wrap { word-break: break-all; font-size: .8rem; }

/* Impressum / privacy / terms: long prose, so a narrower measure. */
.legal { max-width: 70ch; }
.legal h2 { font-size: 1.05rem; color: var(--accent); margin: 1.4rem 0 .4rem; }
.legal h3 { font-size: .95rem; margin: 1rem 0 .3rem; }
.legal p, .legal li { color: var(--ink); font-size: .95rem; }
.legal a { color: var(--accent); }

/* Required on every page, signed in or not — a privacy policy nobody can find
   is not a privacy policy. */
.sitefoot {
  max-width: 820px; margin: 2rem auto 1.5rem; padding: 1rem 1.25rem 0;
  border-top: 1px solid var(--line); display: flex; gap: 1.2rem; flex-wrap: wrap;
  font-size: .88rem;
}
.sitefoot a { color: var(--muted); text-decoration: none; }
.sitefoot a:hover { color: var(--accent); }
.legal-warning { color: #ff9aa0; }

/* ------------------------------------------------------------------------- */
/* Band mode: the song arrangement editor (templates/arrange.html).           */
/*                                                                           */
/* The grid is a timeline, so it is the one page allowed more than the 820px  */
/* measure, and the one page that scrolls sideways. The member column is      */
/* sticky: on a phone in landscape you can see three or four parts at a time, */
/* and a grid whose row labels scroll away is a grid nobody can read.         */
/* ------------------------------------------------------------------------- */
main.arrange-wide { max-width: 1180px; }

.arrange-head { display: flex; align-items: baseline; gap: .9rem; flex-wrap: wrap; margin-bottom: .5rem; }
.arrange-head h2 { margin: 0; color: var(--ink); flex: 1 1 auto; }
.arrange-head .back { color: var(--muted); text-decoration: none; }
.arrange-head .back:hover { color: var(--accent); }
.wide-btn { flex: 0 0 auto; }

/* Portrait on a small screen only. A hint, never a wall: the grid below still
   works, it just wants the width. */
.rotate-hint { display: none; color: var(--accent); font-size: .9rem; margin: .2rem 0 .6rem; }
@media (orientation: portrait) and (max-width: 820px) { .rotate-hint { display: block; } }

/* The cell/move form lives outside the grid (a form cannot wrap it without
   nesting the section forms inside it) and its buttons reach it through the
   HTML form attribute. It has nothing to show. */
.offscreen-form { display: none; }

.arrange-scroll { overflow-x: auto; overscroll-behavior-x: contain; padding-bottom: .3rem; }
.arrange-grid { display: grid; gap: 2px; min-width: min-content; }

.agrid {
  padding: .4rem .45rem; border-radius: 6px; background: #1b1d21;
  border: 1px solid var(--line); min-height: 3rem;
}
/* Row labels and the corner stay put while the timeline scrolls under them. */
.agrid.corner, .agrid.name { position: sticky; left: 0; z-index: 2; background: #202329; }
.agrid.corner { color: var(--muted); font-size: .72rem; display: flex; align-items: flex-end; }
.agrid.name { display: flex; align-items: center; font-weight: 600; font-size: .9rem; overflow: hidden; }
.agrid.name .you { color: var(--muted); font-weight: 400; font-size: .8rem; }

.agrid.head { display: flex; flex-direction: column; gap: .1rem; position: relative; }
.agrid.head .sec-label { font-weight: 600; font-size: .88rem; line-height: 1.2; overflow-wrap: anywhere; }
.agrid.head .sec-bars { color: var(--muted); font-size: .75rem; }
.agrid.head .sec-cue { color: var(--accent2); font-size: .72rem; line-height: 1.2; overflow-wrap: anywhere; }
.agrid.head .sec-move { margin-top: auto; display: flex; gap: .2rem; }
.mini { padding: .1rem .35rem; font-size: .7rem; line-height: 1.4; }

/* touch-action: none so a drag on the handle moves the column instead of
   scrolling the page — the whole gesture depends on it. */
.drag-handle {
  position: absolute; top: .25rem; right: .3rem; cursor: grab;
  color: var(--muted); touch-action: none; user-select: none;
  padding: .15rem .3rem; font-size: .95rem; line-height: 1;
}
.drag-handle:active { cursor: grabbing; }
.agrid.head.dragging { z-index: 5; opacity: .92; box-shadow: 0 6px 18px rgba(0,0,0,.45); }
.agrid.head.drop-target { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

/* The row handle is a flex item, not absolutely positioned like the column one.
   A name cell is 7.5rem wide and `position: sticky` makes it a containing
   block, so an absolute handle would sit on top of the member's name. In the
   flow it takes its own space and the name simply starts after it. */
.agrid.name .drag-handle {
  position: static; flex: 0 0 auto; margin-right: .3rem; padding: .15rem .2rem;
}
.agrid.name.dragging { z-index: 5; opacity: .92; box-shadow: 0 6px 18px rgba(0,0,0,.45); }
.agrid.name.drop-target { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

/* Section kinds. Colour is decoration on top of the label, never the only thing
   carrying the meaning. */
.kind-intro  { border-left: 3px solid #6b8f71; }
.kind-verse  { border-left: 3px solid var(--accent2); }
.kind-chorus { border-left: 3px solid var(--accent); }
.kind-bridge { border-left: 3px solid #9b7fc4; }
.kind-solo   { border-left: 3px solid #d4708a; }
.kind-break  { border-left: 3px solid #7d8590; }
.kind-outro  { border-left: 3px solid #6b8f71; }
.kind-part   { border-left: 3px solid var(--line); }

/* A cell is a submit button. Big enough for a thumb, and it says the state in a
   glyph *and* a word — colour alone is not a way to tell somebody they are out. */
.agrid.cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .1rem; cursor: pointer; font: inherit; color: var(--ink);
}
.agrid.cell .glyph { font-size: 1.15rem; line-height: 1; }
.agrid.cell .cell-word { font-size: .68rem; color: var(--muted); }
.agrid.cell.pending { opacity: .55; }
.agrid.cell.state-play  { background: #1b1d21; }
.agrid.cell.state-lead  { background: #3a2c16; border-color: var(--accent); }
.agrid.cell.state-lead .glyph, .agrid.cell.state-lead .cell-word { color: var(--accent); }
.agrid.cell.state-quiet { background: #1b1d21; }
.agrid.cell.state-quiet .glyph, .agrid.cell.state-quiet .cell-word { color: var(--muted); }
.agrid.cell.state-out   { background: #2b1e20; border-color: #a3434a; }
.agrid.cell.state-out .glyph, .agrid.cell.state-out .cell-word { color: #ff9aa0; }

.arrange-legend { display: flex; flex-wrap: wrap; gap: .9rem; font-size: .8rem; color: var(--muted); margin: .6rem 0 1rem; }
.arrange-legend .state-lead { color: var(--accent); }
.arrange-legend .state-out { color: #ff9aa0; }

.empty-arrangement { margin: .8rem 0 1.2rem; }
.copy-arrangement { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: -.4rem 0 1.2rem; }
.copy-arrangement label { flex: 1 0 100%; color: var(--muted); font-size: .92rem; }
.copy-arrangement select { flex: 1 1 14rem; width: auto; min-width: 0; }

/* Adding: one tap per common part; the named form is for what a band calls its
   own. */
.add-section { border-top: 1px solid var(--line); padding-top: .9rem; }
.add-section h3, .arrange-sections h3, .arrange-members h3 { font-size: .95rem; color: var(--muted); margin: 0 0 .5rem; }
.kind-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .6rem; }
.kind-chips button { border-radius: 999px; font-size: .85rem; padding: .3rem .7rem; }
.named-section { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.named-section input[name="label"] { flex: 1 1 12rem; width: auto; }
.named-section input[name="bars"] { flex: 0 0 5rem; width: auto; }

/* Everything that needs typing, on rows as wide as the screen — the other half
   of the mobile trade-off. The grid never asks for a text field the size of a
   thumbnail. */
.arrange-sections { border-top: 1px solid var(--line); margin-top: 1rem; padding-top: .9rem; }
.seclist { list-style: none; margin: 0; padding: 0; }
.secitem { padding: .5rem .6rem; margin-bottom: .4rem; background: #1b1d21; border: 1px solid var(--line); border-radius: 6px; }
.sec-edit { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; }
.sec-edit .secnum { color: var(--muted); font-size: .85rem; min-width: 1.5ch; }
.sec-edit input[name="label"] { flex: 1 1 8rem; width: auto; margin: 0; }
.sec-edit input[name="bars"] { flex: 0 0 4.5rem; width: auto; margin: 0; }
.sec-edit .cue-input { flex: 2 1 14rem; width: auto; margin: 0; }
.sec-actions { display: flex; gap: .6rem; align-items: center; margin-top: .35rem; }

/* Who is in this song — the row axis of the same trade-off as .arrange-sections
   above: full-width targets down here, and only the drag handle in the grid. */
.arrange-members { border-top: 1px solid var(--line); margin-top: 1rem; padding-top: .9rem; }
.memberlist { list-style: none; margin: 0; padding: 0; }
.memitem {
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .6rem; margin-bottom: .4rem;
  background: #1b1d21; border: 1px solid var(--line); border-radius: 6px;
}
.memitem .memnum { color: var(--muted); font-size: .85rem; min-width: 1.5ch; }
.memitem .memname { flex: 1 1 auto; font-size: .92rem; overflow-wrap: anywhere; }
.memitem .memname .you { color: var(--muted); font-weight: 400; font-size: .8rem; }
.mem-actions { display: flex; gap: .45rem; align-items: center; flex: 0 0 auto; }
.add-members { margin-top: .7rem; }
.add-members h4 { font-size: .88rem; color: var(--muted); margin: 0 0 .3rem; font-weight: 600; }
.member-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin: .4rem 0 0; }
.member-chips button { border-radius: 999px; font-size: .85rem; padding: .3rem .7rem; }

@media (max-width: 560px) {
  .agrid.cell .cell-word { display: none; }
  .sec-edit .cue-input { flex: 1 1 100%; }
}

/* The way into the editor, on each song in Band mode. A link, not a module: the
   grid is a screen of its own and does not fit under a song in a list. */
.arrange-link {
  display: inline-block; margin: .5rem 0; padding: .35rem .7rem;
  border: 1px solid var(--line); border-radius: 6px;
  color: var(--accent); text-decoration: none; font-size: .9rem;
}
.arrange-link:hover { border-color: var(--accent); }

/* --------------------------------------------------------------------------
   The guided demo (app/demo.py). Two pieces: the front page a stranger lands
   on, and the tour strip that follows them through the real app.
   -------------------------------------------------------------------------- */
.front .lead { font-size: 1.05rem; }
.front-modes { margin: .8rem 0 0; padding-left: 1.1rem; }
.front-modes li { margin-bottom: .4rem; }
.front-actions { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; margin-top: .8rem; }
.card.demo-entry { border-color: var(--accent); }
.demo-form { display: flex; flex-wrap: wrap; gap: .6rem; align-items: flex-end; }
.demo-form label { display: flex; flex-direction: column; gap: .25rem; flex: 1 1 18rem; }
.demo-form input { margin: 0; }

/* The strip sits between the header and the page it is talking about, so it
   reads as part of the frame rather than as part of the band's material. */
.demo-strip {
  max-width: 820px; margin: 1rem auto 0; padding: .8rem 1rem;
  background: #22262c; border: 1px solid var(--accent); border-radius: 10px;
}
.demo-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .6rem; }
.demo-count { color: var(--accent); font-size: .85rem; font-weight: 700; letter-spacing: .03em; }
.demo-title { margin: 0; font-size: 1.05rem; color: var(--ink); }
.demo-body { margin: .5rem 0 0; }
.demo-note { margin: .45rem 0 0; color: var(--muted); font-size: .9rem; }
.demo-nav { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; margin-top: .7rem; }
.demo-nav .button.primary { border-color: var(--accent); color: var(--accent); }
.demo-clock { margin-left: auto; color: var(--muted); font-size: .85rem; white-space: nowrap; }
/* The showcase tour reuses the demo strip; the offer is one line, not a step. */
.tour-offer form { display: flex; flex-wrap: wrap; gap: .6rem .8rem; align-items: center; }

/* Read-outs that stand where a form stands for a member of the band. Deliberately
   not styled like a disabled control: it is a different module, not a greyed-out
   one — see the design contract in CLAUDE.md. */
.rating.readonly { margin: .35rem 0; color: var(--muted); font-size: .9rem; }
.rating.readonly strong { color: var(--ink); }
.note-body { margin: .3rem 0 0; }
.cue { margin: .35rem 0 0; color: var(--accent); font-size: .9rem; }
.sec-read { margin: 0; display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; }
.sec-read .secnum { color: var(--muted); font-size: .85rem; min-width: 1.5ch; }

@media (max-width: 560px) {
  .demo-clock { margin-left: 0; }
}

/* --------------------------------------------------------------------------
   Looking for band members (app/recruiting.py), and the shared inbox its
   replies land in. The one section that shows another bandspace's material, so
   an advert is styled as a card in a list rather than as part of your own page.
   -------------------------------------------------------------------------- */
.card.ours { border-color: var(--accent); }
.member-filter { display: flex; flex-wrap: wrap; gap: .8rem; align-items: flex-end; }
.member-filter label { display: flex; flex-direction: column; gap: .25rem; }
.member-filter input, .member-filter select { margin: 0; }

.advert-list { list-style: none; margin: 0; padding: 0; }
.advert-head { display: flex; align-items: baseline; gap: .6rem; }
.advert-head h2 { margin: 0; font-size: 1.05rem; color: var(--ink); }
.advert-head h2 a { color: var(--ink); text-decoration: none; }
.advert-head h2 a:hover { color: var(--accent); }
.advert-roles { margin: .4rem 0 .2rem; }
.advert-where { margin: .2rem 0; color: var(--muted); font-size: .9rem; }
.advert-genres { margin: .2rem 0; color: var(--accent2); font-size: .9rem; }
.advert-pitch { margin: .4rem 0; }
.advert-facts { display: grid; grid-template-columns: max-content 1fr; gap: .35rem .9rem; margin: 0; }
.advert-facts dt { color: var(--muted); font-size: .9rem; }
.advert-facts dd { margin: 0; }
.advert-pitch-full { border-top: 1px solid var(--line); margin-top: .9rem; padding-top: .8rem; }

.advert-form fieldset { border: 1px solid var(--line); border-radius: 8px; margin: 0 0 1rem; padding: .8rem .9rem; }
.advert-form legend { color: var(--muted); font-size: .9rem; padding: 0 .3rem; }
.advert-form label { display: block; margin-bottom: .6rem; }
.age-range { display: flex; gap: .8rem; align-items: flex-end; }
.age-range label { flex: 0 0 8rem; }
.reply-form textarea, .chat-form textarea { width: 100%; }

/* The inbox. Unread is a left edge and a word, not colour alone. */
.chatlist { list-style: none; margin: 0; padding: 0; }
.chat-row { padding: .5rem .6rem; margin-bottom: .4rem; border: 1px solid var(--line); border-left-width: 3px; border-radius: 6px; }
.chat-row.unread { border-left-color: var(--accent); }
.chat-row a { color: var(--ink); font-weight: 600; text-decoration: none; }
.chat-row a:hover { color: var(--accent); }
.chat-meta { display: block; color: var(--muted); font-size: .85rem; }
.chat-meta .new { color: var(--accent); margin-left: .4rem; }
.topbar .badge { background: var(--accent); color: #15171a; border-radius: 10px; padding: 0 .4rem; font-size: .8rem; font-weight: 700; }
.topbar a.has-unread { color: var(--ink); }

/* One thread. Your own messages are indented and tinted; a withdrawn one is
   plainly a marker rather than something somebody wrote. */
.messages { list-style: none; margin: 0; padding: 0; }
.message { padding: .55rem .7rem; margin-bottom: .5rem; border: 1px solid var(--line); border-radius: 8px; background: #1f2226; }
.message.mine { background: #23282d; margin-left: 1.5rem; border-color: #46505a; }
.message-meta { display: flex; gap: .6rem; align-items: baseline; color: var(--muted); font-size: .8rem; }
.message-author { color: var(--accent); font-weight: 600; }
.message-body { margin: .3rem 0 0; }
.message-body.withdrawn { color: var(--muted); font-style: italic; }
.message-recording { margin-top: .5rem; }
.message-recording audio { width: 100%; margin-top: .3rem; }
.chat-form .attach { display: block; margin: .6rem 0 .2rem; }
.attribution { margin-top: 1.2rem; font-size: .8rem; }

@media (max-width: 560px) {
  .advert-facts { grid-template-columns: 1fr; gap: .1rem .5rem; }
  .advert-facts dd { margin-bottom: .45rem; }
  .message.mine { margin-left: .5rem; }
}

/* The social-media card and the public advert page it points at. */
.share-card { display: block; width: 100%; max-width: 420px; height: auto; border-radius: 10px; border: 1px solid var(--line); margin: .6rem 0; }
.share-url code { display: block; overflow-wrap: anywhere; background: #1b1d21; border: 1px solid var(--line); border-radius: 6px; padding: .5rem .6rem; }
.public-advert h1 { font-size: 1.4rem; }

/* --------------------------------------------------------------------------
   The composer (app/composer.py). A timeline: lanes down, time across. Fixed
   scale in pixels per second — a timeline that rescales when the window
   changes is one where the thing you were dragging moves out from under the
   pointer.
   -------------------------------------------------------------------------- */
/* One number decides where the timeline begins, and both the lane names and the
   ruler's gutter are sized from it. They were two separate widths once and the
   ruler drew bar 1 a whole name-column to the left of where a clip at 0 ms
   sits. */
.compose-grid { --compose-gutter: 7.5rem; }
.card.compose { overflow: hidden; }
.compose-transport { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; margin: .6rem 0; }
.compose-transport .spacer { flex: 1 1 auto; }
.compose-clock { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 700; min-width: 5ch; }
.compose-status { color: var(--muted); font-size: .9rem; }

.compose-scroll { overflow-x: auto; overflow-y: hidden; border: 1px solid var(--line); border-radius: 8px; background: #1b1d21; }
.compose-grid { position: relative; min-width: 100%; }

.compose-ruler { display: flex; align-items: stretch; height: 34px; border-bottom: 1px solid var(--line); background: #15171a; }
/* Same width and the same sticky behaviour as .compose-lane-name below — that
   is the alignment, and it is structural rather than a margin somebody has to
   remember to keep in step. */
.compose-ruler-gutter {
  position: sticky; left: 0; z-index: 4; flex: 0 0 var(--compose-gutter);
  padding: 0 .5rem; background: #15171a; border-right: 1px solid var(--line);
  display: flex; align-items: center; overflow: hidden; white-space: nowrap;
}
.compose-ruler-gutter .hint { font-size: .7rem; }
/* The ticks are absolute inside this, so 0 ms is its left edge — the same left
   edge a clip at 0 ms is drawn from. */
.compose-ruler-scale { position: relative; flex: 1 1 auto; }
.compose-tick { position: absolute; top: 0; bottom: 0; width: 1px; background: #2b3036; }
.compose-tick.major { background: #454c55; }
.compose-tick.major::after { content: attr(data-label); position: absolute; top: 1px; left: 3px; font-size: .7rem; color: var(--muted); }
.compose-second { position: absolute; bottom: 1px; font-size: .68rem; color: #6d757e; transform: translateX(2px); }

.compose-lane { display: flex; align-items: stretch; border-bottom: 1px solid var(--line); }
.compose-lane-name {
  position: sticky; left: 0; z-index: 3; flex: 0 0 var(--compose-gutter); padding: .4rem .5rem;
  background: #15171a; border-right: 1px solid var(--line);
  display: flex; align-items: center; gap: .3rem; font-size: .85rem;
}
.compose-lane-track { position: relative; height: 64px; flex: 1 1 auto; }

/* The arrangement, laid over the background. Behind the clips and not
   interactive: it is a backdrop the composer never writes to. */
.compose-section { position: absolute; top: 0; bottom: 0; z-index: 0; opacity: .22; border-left: 1px solid rgba(255,255,255,.25); pointer-events: none; }
.compose-section span { position: absolute; top: 2px; left: 4px; font-size: .7rem; opacity: .85; white-space: nowrap; }
.compose-section.kind-intro { background: #4a9fd8; }
.compose-section.kind-verse { background: #6d757e; }
.compose-section.kind-chorus { background: #e0913a; }
.compose-section.kind-bridge { background: #9a6bd8; }
.compose-section.kind-solo { background: #d84a6b; }
.compose-section.kind-break { background: #3a3e44; }
.compose-section.kind-outro { background: #4ad8a0; }
.compose-section.kind-part { background: #6d757e; }

.compose-clip {
  position: absolute; top: 6px; bottom: 6px; z-index: 1;
  background: #2f6f4f; border: 1px solid #57a97d; border-radius: 5px;
  cursor: grab; touch-action: none; overflow: hidden;
}
.compose-clip.dragging { cursor: grabbing; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
/* A clip whose recording was deleted. Drawn, never dropped: silently removing
   part of somebody's work is worse than showing them what went. */
.compose-clip.missing { background: #3a2326; border-color: #a3434a; cursor: not-allowed; }
.clip-label { position: absolute; left: 5px; top: 3px; font-size: .72rem; color: #dff0e6; white-space: nowrap; }
.clip-handle { position: absolute; top: 0; bottom: 0; width: 8px; cursor: ew-resize; }
.clip-handle.left { left: 0; }
.clip-handle.right { right: 0; }
.clip-handle:hover { background: rgba(224,145,58,.5); }

.compose-head { position: absolute; top: 34px; bottom: 0; width: 2px; background: var(--accent); z-index: 4; pointer-events: none; }

.clip-lane-head { margin: .9rem 0 .3rem; color: var(--muted); font-size: .9rem; }
.clip-source { font-size: .85rem; color: var(--muted); flex: 1 1 8rem; }
.secitem.add-clip { border-style: dashed; }
.lane-muted { font-size: .8rem; }

@media (max-width: 560px) {
  /* One place again: the ruler's gutter follows because both read the variable. */
  .compose-grid { --compose-gutter: 5.5rem; }
  .compose-lane-name { font-size: .78rem; }
}
.material-row { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; }
.material-row audio { flex: 1 1 14rem; max-width: 22rem; height: 32px; }

/* The swap drilldown: click a clip, see the other takes of the same part. Lives
   inside the scrolling lane so it travels with the clip it belongs to. */
.clip-menu {
  position: absolute; z-index: 6; min-width: 14rem; max-width: 22rem;
  background: var(--card); border: 1px solid var(--accent); border-radius: 8px;
  padding: .5rem .6rem; box-shadow: 0 6px 20px rgba(0,0,0,.45);
}
.clip-menu-head { margin: 0 0 .2rem; font-weight: 600; overflow-wrap: anywhere; }
.clip-menu-part { margin: 0 0 .4rem; color: var(--muted); font-size: .85rem; }
.clip-menu ul { list-style: none; margin: 0; padding: 0; }
.clip-menu li { margin-bottom: .25rem; }
.clip-menu li button { width: 100%; text-align: left; overflow-wrap: anywhere; }

/* Versions of a song, as tabs. Plain links, so switching works with no script —
   the current one is marked by weight and an underline as well as colour. */
.variations { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: .6rem 0; }
.variation-tab {
  padding: .3rem .7rem; border: 1px solid var(--line); border-radius: 6px 6px 0 0;
  border-bottom-width: 3px; color: var(--muted); text-decoration: none; font-size: .9rem;
}
.variation-tab:hover { color: var(--ink); }
.variation-tab.active { color: var(--ink); font-weight: 700; border-bottom-color: var(--accent); background: var(--card); }

/* --------------------------------------------------------------------------- *
 * The lyrics sheet                                                             *
 *                                                                              *
 * A lyric sheet is read on a stand at arm's length, so the words are larger    *
 * than the rest of the app and the line spacing is generous. Every word is a   *
 * button; they are styled to look like words, because a sheet made of visible  *
 * buttons would be unreadable as a lyric.                                      *
 *                                                                              *
 * Colour never carries meaning alone — each colour has a name in the legend    *
 * and in the title of every control that applies it. The hues are picked for   *
 * the dark background and kept distinct in lightness as well as hue, so they   *
 * survive being read by somebody who cannot separate red from green.           *
 * --------------------------------------------------------------------------- */
.lyrics .sheet { margin: .8rem 0; }

.version-tabs { display: flex; flex-wrap: wrap; gap: .35rem; margin: .6rem 0; }
.vtab {
  display: inline-block; padding: .3rem .75rem; border-radius: 999px;
  border: 1px solid var(--line); background: #1b1d21; color: var(--fg);
  font-size: .85rem; text-decoration: none;
}
.vtab.current { border-color: var(--accent); color: var(--accent); }
.vtab.add { cursor: pointer; }
.version-actions { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; margin-bottom: .6rem; }
.rename-version input { width: auto; flex: 0 1 12rem; margin: 0; }

/* The pen: chosen once, then used many times. A radio group so it survives a
   page load and works with the script off. */
.pen-bar {
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
  padding: .5rem .6rem; margin: .6rem 0;
  background: #1b1d21; border: 1px solid var(--line); border-radius: 6px;
}
.pen-label { color: var(--muted); font-size: .85rem; }
.pen-swatch { display: inline-flex; align-items: center; gap: .3rem; cursor: pointer; font-size: .82rem; }
.pen-swatch input { margin: 0; }
.pen-dot { width: .9rem; height: .9rem; border-radius: 50%; border: 1px solid var(--line); display: inline-block; }
.pen-hint { flex: 1 1 100%; margin: .2rem 0 0; }

.stanza {
  padding: .6rem .7rem; margin-bottom: .7rem;
  background: #1b1d21; border: 1px solid var(--line); border-radius: 6px;
  border-left: 3px solid transparent;
}
.stanza-head { display: flex; align-items: center; gap: .5rem; }
.stanza-title { font-size: .95rem; margin: 0; flex: 1 1 auto; color: var(--muted); }
.from-arrangement { font-size: .8rem; opacity: .8; }
.from-arrangement.gone { color: var(--muted); }
.stanza-actions { display: flex; gap: .2rem; }
.swatch-btn { padding: .1rem .35rem; font-size: .8rem; line-height: 1.4; }

.lines { list-style: none; margin: .5rem 0 0; padding: 0; }
.line { display: flex; align-items: baseline; gap: .4rem; padding: .1rem 0; border-left: 3px solid transparent; }
.line-swatch { opacity: .35; }
.line:hover .line-swatch, .line:focus-within .line-swatch { opacity: 1; }
.words { display: flex; flex-wrap: wrap; gap: .1rem .35rem; }

/* A word is a button that reads as a word: no chrome until it is touched. */
.word {
  background: none; border: 1px solid transparent; border-radius: 4px;
  color: var(--fg); font: inherit; font-size: 1.05rem; line-height: 1.5;
  padding: 0 .15rem; cursor: pointer;
}
.word:hover, .word:focus { border-color: var(--line); }
.word.pending, .swatch-btn.pending { opacity: .55; }

.stanza-edit { margin-top: .6rem; }
.stanza-edit summary { color: var(--muted); font-size: .85rem; cursor: pointer; }
.lyric-edit { margin-top: .5rem; }
.lyric-edit textarea { width: 100%; font: inherit; line-height: 1.5; }
.lyric-edit-actions { display: flex; gap: .5rem; align-items: center; margin-top: .4rem; }
.label-edit { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.label-edit input { flex: 1 1 12rem; width: auto; margin: 0; }

/* The palette itself. Applied to a word (text), a line or a stanza (a left
   edge), and to the pen's dot — one class, three renderings. */
.color-red    { color: #ff9aa0; }
.color-amber  { color: #f0c070; }
.color-green  { color: #8fd694; }
.color-blue   { color: #8ec5ff; }
.color-violet { color: #c9a7f0; }
.color-grey   { color: #9aa0a6; }
.line.color-red    { border-left-color: #ff9aa0; }
.line.color-amber  { border-left-color: #f0c070; }
.line.color-green  { border-left-color: #8fd694; }
.line.color-blue   { border-left-color: #8ec5ff; }
.line.color-violet { border-left-color: #c9a7f0; }
.line.color-grey   { border-left-color: #9aa0a6; }
.stanza.color-red    { border-left-color: #ff9aa0; }
.stanza.color-amber  { border-left-color: #f0c070; }
.stanza.color-green  { border-left-color: #8fd694; }
.stanza.color-blue   { border-left-color: #8ec5ff; }
.stanza.color-violet { border-left-color: #c9a7f0; }
.stanza.color-grey   { border-left-color: #9aa0a6; }
.color-red    .pen-dot { background: #ff9aa0; }
.color-amber  .pen-dot { background: #f0c070; }
.color-green  .pen-dot { background: #8fd694; }
.color-blue   .pen-dot { background: #8ec5ff; }
.color-violet .pen-dot { background: #c9a7f0; }
.color-grey   .pen-dot { background: #9aa0a6; }

.lyrics-link { border-color: #7a6ea8; }

@media (max-width: 560px) {
  .word { font-size: 1rem; }
  .version-actions { gap: .4rem; }
}

/* --------------------------------------------------------------------------- *
 * The live lyric sheet                                                         *
 *                                                                              *
 * Read at two metres in bad light while holding an instrument. Type is much     *
 * larger than anywhere else in the app, contrast is pushed up, and the only     *
 * controls on the screen are the pace and the scroll — everything else is       *
 * absent from the DOM, not shrunk.                                             *
 * --------------------------------------------------------------------------- */
.live { max-width: 46rem; margin: 0 auto; }
.live-head { margin-bottom: .6rem; }
.live-head h1 { font-size: 1.3rem; margin: .2rem 0; display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; }
.live-artist { color: var(--muted); font-size: .9rem; font-weight: 400; }
.live-versions { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .3rem; }

/* Sticky, because the pace is wanted *during* the song and chasing it up the
   page is not something anybody can do while singing. */
.pace-bar {
  position: sticky; top: 0; z-index: 5;
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
  padding: .5rem .6rem; margin-bottom: 1rem;
  background: #16181c; border: 1px solid var(--line); border-radius: 6px;
}
.pace-btn { font-size: 1rem; padding: .5rem .9rem; }   /* thumb-sized on purpose */
.pace-btn.go.running { border-color: var(--accent); color: var(--accent); }
.pace-read { display: flex; flex-direction: column; align-items: center; line-height: 1.1; min-width: 5rem; }
.pace-read strong { font-size: 1.25rem; }
.pace-unit { color: var(--muted); font-size: .8rem; }
.pace-sub { color: var(--muted); font-size: .7rem; }
.pace-run { display: flex; gap: .4rem; }
.pace-hint { flex: 1 1 100%; margin: .2rem 0 0; }
.pace-bar.unsaved { border-color: #a3434a; }

.live-sheet { font-size: 1.5rem; line-height: 1.55; }
.live-stanza { margin: 0 0 1.6rem; padding-left: .6rem; border-left: 3px solid transparent; }
.live-heading {
  font-size: .8rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; margin: 0 0 .3rem;
}
.live-line { margin: 0 0 .25rem; border-left: 3px solid transparent; padding-left: .4rem; }
.live-word { }
/* Enough empty space under the last line that it can be scrolled up to eye
   level instead of stopping glued to the bottom edge. */
.live-tail { height: 60vh; }

@media (max-width: 560px) {
  .live-sheet { font-size: 1.3rem; }
  .pace-hint { display: none; }   /* the bar is for playing, not for reading */
}

/* Chat with the developers (app/devchat.py): bottom right, above the page.
   The minimised state is a button and nothing else — the window is not in the
   DOM until it is opened. */
.devchat { position: fixed; right: 1rem; bottom: 1rem; z-index: 50; max-width: calc(100vw - 2rem); }
.devchat-button {
  display: inline-flex; align-items: center; gap: .4rem; padding: .55rem .9rem;
  background: var(--card); color: var(--ink); border: 1px solid var(--accent);
  border-radius: 999px; box-shadow: 0 4px 14px rgba(0,0,0,.45); font: inherit;
  cursor: pointer; text-decoration: none;
}
.devchat-button:hover { color: var(--accent); }
.devchat .badge, .devchat-thread .badge {
  background: var(--accent); color: #15171a; border-radius: 10px; padding: 0 .45rem;
  font-size: .8rem; font-weight: 700;
}
.devchat-window {
  width: 22rem; max-width: 100%; display: flex; flex-direction: column;
  max-height: min(34rem, calc(100vh - 2rem)); background: var(--card);
  border: 1px solid var(--accent); border-radius: 12px; box-shadow: 0 8px 28px rgba(0,0,0,.55);
}
.devchat-head { display: flex; align-items: center; gap: .5rem; padding: .55rem .8rem; border-bottom: 1px solid var(--line); }
.devchat-head h2 { margin: 0; font-size: 1rem; flex: 1; }
.devchat-icon { font-size: 1.1rem; color: var(--muted); text-decoration: none; padding: 0 .2rem; }
.devchat-log { overflow-y: auto; padding: .6rem .8rem; flex: 1; min-height: 4rem; }
.devchat-log .message.mine { margin-left: 1rem; }
.devchat-note { margin: 0; padding: .45rem .8rem; color: var(--muted); font-size: .85rem; border-top: 1px solid var(--line); }
.devchat-form { display: flex; flex-direction: column; gap: .4rem; padding: .6rem .8rem; border-top: 1px solid var(--line); }
.card .devchat-form { border-top: 0; padding: 0; }
.devchat-form textarea { width: 100%; box-sizing: border-box; resize: vertical; }
.devchat-foot { display: flex; align-items: center; justify-content: space-between; gap: .6rem; flex-wrap: wrap; }
.devchat-foot .hint { font-size: .8rem; }
.devchat-kind { border: 1px solid var(--line); border-radius: 6px; padding: 0 .35rem; font-size: .75rem; }
.devchat-page { margin: .25rem 0 0; font-size: .8rem; }
.devchat-thread-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.devchat-thread-head h2 { margin: 0; }
.devchat-thread.waiting { border-color: var(--accent); }
/* Room at the bottom of every page so the corner never covers the last line. */
body:has(.devchat) main { padding-bottom: 4.5rem; }
@media (max-width: 600px) {
  .devchat { right: .5rem; left: auto; bottom: .5rem; max-width: calc(100vw - 1rem); }
  .devchat-window { width: calc(100vw - 1rem); max-height: calc(100vh - 1rem); }
}
.devchat-form select { align-self: flex-start; }
