/* Brand: Playlist Tools logo kit (brand/README.md). The logo's coral #FF5A36 is also the
   interface accent. Surfaces are frosted glass on a warm, nearly neutral grey: translucent
   cards with a thin light edge (--card, --edge), and lighter tiles inside them (--tile).
   Each token is light-dark(light, dark): the page follows the system unless Settings stores
   a choice (js/theme.js sets data-theme). Text on --accent uses --accent-text; accent-coloured
   text uses --accent-ink (both WCAG AA). Contrast is measured against the cards as they look. */

@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url(fonts/bricolage-grotesque-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url(fonts/bricolage-grotesque-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- Tokens ---------- */

:root {
  color-scheme: light dark;
  --coral: #FF5A36;
  --bg: light-dark(#E9E6E2, #1B1A19);
  --glow-1: light-dark(#FBFAF8, #3A3634); /* soft light behind the glass, top left */
  --glow-2: light-dark(#F1E6DF, #2C2522); /* a hint of the coral, top right */
  --card: light-dark(rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0.07));
  --edge: light-dark(rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.12));
  --tile: light-dark(rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0.055));
  --tile-edge: light-dark(rgba(33, 30, 28, 0.08), rgba(255, 255, 255, 0.09));
  --raised: light-dark(rgba(33, 30, 28, 0.05), rgba(255, 255, 255, 0.09));
  --solid: light-dark(#F7F5F3, #2A2827); /* dialogs: no glass, nothing behind to blur */
  --field: light-dark(rgba(255, 255, 255, 0.85), rgba(0, 0, 0, 0.2));
  --text: light-dark(#211E1C, #ECE8E3);
  --muted: light-dark(#5E5853, #B4AEA8);
  --border: light-dark(rgba(33, 30, 28, 0.1), rgba(255, 255, 255, 0.1));
  --field-border: light-dark(#857F7A, #857F7A); /* 3.18:1 on the light page (fields sit on it now) */
  --accent: light-dark(#C63D16, #FF6A47);
  --accent-text: light-dark(#FFFFFF, #1E1C1B);
  --accent-ink: light-dark(#B5370F, #FF8466);
  --accent-soft: light-dark(rgba(198, 61, 22, 0.1), rgba(255, 106, 71, 0.16));
  --tonal: light-dark(#2B2826, #E6E1DB);
  --tonal-text: light-dark(#FFFFFF, #1E1C1B);
  --bar: light-dark(#7A746E, #827D78); /* chart bars: at least 3:1 on the page in both themes (3.7 and 4.3) */
  --mark-ink: var(--text);
  --mark-ring: light-dark(#5A5551, #D7D2CC);
  /* The What was that song? record: dark in both modes; on dark a fine light edge shows its shape. */
  --record: light-dark(#211E1C, #16142B);
  --record-edge: light-dark(transparent, #D8D4E8);
  --record-groove: light-dark(#5A5551, #4A4766);
  --warn-bg: light-dark(#FBEBC8, rgba(245, 185, 74, 0.16));
  --warn-text: light-dark(#7A4D00, #F5C76A);
  --err-bg: light-dark(#FBE0D8, rgba(255, 106, 71, 0.16));
  --err-text: light-dark(#A12A10, #FFA088);
  --ok: light-dark(#23753A, #7BD992);
  --ok-soft: light-dark(rgba(35, 117, 58, 0.12), rgba(123, 217, 146, 0.16));
  --rail: light-dark(rgba(255, 255, 255, 0.4), rgba(0, 0, 0, 0.25));
  --header-bg: light-dark(rgba(240, 238, 235, 0.72), rgba(27, 26, 25, 0.62));
  --shadow: inset 0 0 0 1px var(--edge), 0 1px 2px light-dark(rgba(60, 50, 40, 0.05), rgba(0, 0, 0, 0.2)), 0 16px 40px light-dark(rgba(60, 50, 40, 0.1), rgba(0, 0, 0, 0.28));
  --glass: blur(28px) saturate(1.4);
  --backdrop: light-dark(rgba(33, 30, 28, 0.4), rgba(0, 0, 0, 0.6));
  --paper: #FFFFFF; /* behind QR codes, in both modes: scanners need dark on light */
  /* One radius scale: controls 10px, panels 16px, floating things (player, dialogs) 22px. */
  --radius-control: 10px;
  --radius: 16px;
  --radius-float: 22px;
  --row-pad: 10px; /* above and below a row in a list */
  --radius-sm: 12px;
  --heading-font: 'Bricolage Grotesque', 'Helvetica Neue', Arial, sans-serif;
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* ---------- Base ---------- */

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100vh;
  background:
    radial-gradient(1100px 700px at 8% -12%, var(--glow-1), transparent 62%),
    radial-gradient(900px 600px at 104% 0%, var(--glow-2), transparent 58%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { font-family: var(--heading-font); letter-spacing: -0.02em; line-height: 1.2; }
h2 { margin: 0 0 6px; font-size: 28px; font-weight: 800; }
/* Headings in four sizes: the page 28px, a result's, item's or section's title 20px, a part in it 16px, a label 14px. */
h3 { margin: 0 0 14px; font-size: 16px; font-weight: 700; }
h4 { margin: 18px 0 8px; font-size: 16px; font-weight: 700; } /* a part: the same size as the tool pages' (.result h3, .group h3) */
/* A section of a page: 20px, whatever its level (h3 under a tab, h4 inside Patterns), on every page. */
.section-title, .home-section > h3, .pattern > h4, .home-card-head h3, #tool-settings .card > h3, #tool-admin .card > h3, #tool-about .card > h3 { font-size: 20px; }
/* One style for links in text: the accent, underlined (thicker on hover). Links that are names in
   a list (songs, playlists, artists, the menu) keep the text's colour, underlined on hover. */
a { color: var(--accent-ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }
.range-tracks .track a, .song-with-art a, .check-title a, .player-track a, .library-table a, .library-albums a, .home-ranked a { color: inherit; }
.home-ranked a { text-decoration: none; }
.home-ranked a:hover, .library-albums a:hover { text-decoration: underline; }
.library-albums a { font-weight: 600; text-decoration: none; }
:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
.small { font-size: 14px; }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; animation: none !important; } }
.muted { color: var(--muted); margin: 0; }
.hint { font-size: 13px; margin-top: 10px; }

/* ---------- The shell: menu, content; on a phone a top bar and a tab bar ---------- */

.shell { display: flex; min-height: 100vh; }
/* A column as high as the window, so the player bar can sit at its bottom on short pages too */
.content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.content > main { width: 100%; }

/* The menu: the brand, the five places (and Admin), your account at the bottom. It never scrolls. */
.menu {
  flex: 0 0 232px; position: sticky; top: 0; height: 100vh;
  display: flex; flex-direction: column; gap: 18px; padding: 18px 12px 14px;
  background: var(--rail); border-right: 1px solid var(--border);
}
.menu[hidden] { display: none; }
.brand { display: flex; align-items: center; gap: 8px; padding: 2px 10px; border-radius: 12px; color: var(--text); text-decoration: none; }
.brand .logo-mark { width: 34px; height: 34px; }
.brand .wordmark { font-size: 19px; }
.menu-icon { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nav { display: grid; gap: 4px; }
.nav a {
  display: flex; align-items: center; gap: 12px; min-height: 44px; padding: 0 12px; border-radius: 14px;
  color: var(--text); font-weight: 550; text-decoration: none;
}
.nav a:hover { background: var(--raised); }
.nav a.active { background: var(--accent-soft); color: var(--accent-ink); font-weight: 650; }
.nav-divider { display: block; height: 1px; margin: 8px 12px; background: var(--border); }
.nav-divider[hidden], .nav a[hidden] { display: none; }
.nav .menu-count { margin-left: auto; }
/* Your account: picture, name and email; opens the account menu. */
.account-button {
  margin-top: auto; justify-content: flex-start; gap: 10px; height: auto; min-height: 56px; padding: 8px 10px; border-radius: 16px;
  background: transparent; color: var(--text); font-weight: 550; text-align: left;
}
.account-button:hover:not(:disabled), .account-button[aria-expanded="true"] { background: var(--raised); filter: none; }
.account-button:active:not(:disabled) { transform: none; }
.account-button .avatar { width: 36px; height: 36px; font-size: 15px; }
.account-names { flex: 1; min-width: 0; display: grid; }
.account-name { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-email { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu-chevron { width: 18px; height: 18px; color: var(--muted); }
/* The account menu: above the account button, or under the top bar's picture on a phone. */
.account-menu {
  position: fixed; left: 12px; bottom: 76px; z-index: 40; width: 208px;
  display: grid; gap: 2px; padding: 6px; border-radius: var(--radius-sm);
  background: var(--solid); box-shadow: var(--shadow);
}
.account-menu[hidden] { display: none; }
.account-menu a, .account-menu .signout {
  display: flex; align-items: center; justify-content: flex-start; gap: 10px; width: 100%; min-height: 44px; padding: 0 10px;
  border-radius: 10px; color: var(--text); font-weight: 550; text-decoration: none; background: transparent;
}
.account-menu a:hover, .account-menu .signout:hover:not(:disabled) { background: var(--raised); color: var(--text); filter: none; }
.account-menu a.active { color: var(--accent-ink); }
.account-menu .menu-count { margin-left: auto; }
.account-admin { display: none !important; } /* on a phone only: the menu has Admin elsewhere */
.app-version { margin: 4px 10px 2px; font-size: 12px; color: var(--muted); }

/* Your work going on (core.js watchActivity): in the menu above your account; on a phone above
   the tab bar. A link to the page about it. */
.activity {
  display: grid; gap: 6px; margin: auto 0 0; padding: 10px 12px; border-radius: 14px;
  background: var(--raised); color: var(--text); text-decoration: none; font-size: 13px;
}
.activity[hidden] { display: none; }
.activity:not([hidden]) + .account-button { margin-top: 8px; } /* else the account button is at the bottom by itself */
.activity:hover { box-shadow: inset 0 0 0 1px var(--accent-ink); }
.activity-label { display: flex; align-items: center; gap: 8px; font-weight: 650; }
.activity-dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--accent); animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .35; } }
.activity-bar { display: block; height: 4px; border-radius: 2px; background: var(--accent-soft); overflow: hidden; }
.activity-bar > span { display: block; height: 100%; width: calc(var(--done, 0) * 100%); background: var(--accent-ink); border-radius: 2px; transition: width .3s; }
.activity.unknown .activity-bar > span { width: 30%; }
.activity-count { font-size: 12px; }
.activity-count:empty { display: none; }
.activity-phone { display: none; }

/* A message when your work is done: at the bottom of the window, for a while. */
.toast {
  position: fixed; right: 20px; bottom: 20px; z-index: 45; max-width: min(420px, calc(100vw - 32px));
  display: flex; align-items: center; gap: 12px; padding: 10px 8px 10px 16px; border-radius: var(--radius-sm);
  background: var(--solid); color: var(--text); box-shadow: var(--shadow); font-size: 14px;
}
.toast[hidden] { display: none; }
body:has(#player:not([hidden])) .toast { bottom: 120px; } /* above the player, not over it */
.toast a { font-weight: 650; white-space: nowrap; }
.toast button { flex: none; width: 36px; height: 36px; padding: 0; }

/* On a phone: a top bar with the page's name and your picture, and the places along the bottom. */
.topbar { display: none; }
.topbar-title { font: 700 20px/1.2 var(--heading-font); }
.tabbar { display: none; }

/* Tabs at the top of a page (Stats, Library, Admin, Gigs): one row, swiped on a phone. */
.page-tabs {
  display: flex; gap: 4px 24px; margin: 14px 0 12px; border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none;
}
.page-tabs::-webkit-scrollbar { display: none; }
.page-tabs a {
  flex: none; display: inline-flex; align-items: center; min-height: 44px; padding: 0 2px; margin-bottom: -1px;
  border-bottom: 2px solid transparent; color: var(--muted); font-weight: 600; text-decoration: none; white-space: nowrap;
}
.page-tabs a:hover { color: var(--text); }
.page-tabs a.active { color: var(--text); border-bottom-color: var(--accent); }
.page-tabs a:focus-visible { outline-offset: -2px; }
.page-tabs .menu-count { margin-left: 6px; }
/* Above a tool's name: back to all tools. */
/* On a phone, inside a place (a tool, one playlist): back in the top bar. */
.topbar-back { display: flex; align-items: center; gap: 2px; min-height: 44px; margin-left: -8px; padding-right: 6px; color: var(--accent-ink); font-weight: 600; text-decoration: none; }
.topbar-back[hidden] { display: none; }
.topbar-back + .topbar-title { margin-left: 4px; }

/* The Tools page: a card per tool. */
.tool-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 230px), 1fr)); gap: 16px; }
.tool-card { display: grid; align-content: start; gap: 8px; margin: 0; color: var(--text); text-decoration: none; }
.tool-card:hover { box-shadow: inset 0 0 0 1px var(--accent-ink), var(--shadow); }
.tool-card-title { display: flex; align-items: center; gap: 10px; font: 700 16px/1.3 var(--heading-font); }
.tool-card-title .menu-icon { color: var(--accent-ink); }

/* Settings › Connected services: a row per service. */
.service-list { list-style: none; margin: 12px 0 0; padding: 0; border-top: 1px solid var(--border); }
.service-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; min-height: 60px; padding: 8px 2px; border-bottom: 1px solid var(--border); }
.service-row .service-icon { margin: 0; width: 26px; height: 26px; }
.service-row-name { font-weight: 650; }
.service-row-state { display: inline-flex; align-items: center; gap: 6px; flex: 1 1 auto; }
.service-row-actions { display: flex; flex: none; gap: 8px; }
.service-dot { width: 9px; height: 9px; border-radius: 50%; box-shadow: inset 0 0 0 2px var(--muted); }
.connected .service-dot { background: var(--ok); box-shadow: none; }
.needs .service-dot { background: var(--warn-text); box-shadow: none; } /* connected, but a permission is missing */
.service-row.needs .service-row-state { color: var(--warn-text); }

/* Signed out: the logo above the sign-in card. */
.signed-out-head { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 40px 16px 0; }

.logo-mark { width: 40px; height: 40px; flex: none; }
.logo-mark .ink { fill: var(--mark-ink); }
.logo-mark .ring { fill: none; stroke: var(--mark-ring); stroke-width: 1.5; }
.logo-mark .label { fill: var(--coral); }
.logo-mark .play { fill: #FFFFFF; stroke: #FFFFFF; stroke-width: 1.5; stroke-linejoin: round; }
.wordmark { margin: 0; font-size: 21px; letter-spacing: -0.035em; white-space: nowrap; }
.wordmark-strong { font-weight: 800; color: var(--text); }
.wordmark-light { font-weight: 500; color: var(--coral); }

.avatar {
  width: 32px; height: 32px; border-radius: 50%;
  display: inline-grid; place-items: center;
  background: var(--tonal); color: var(--tonal-text);
  font: 700 14px/1 var(--heading-font);
}
/* A picture over the initial; it only shows once it has loaded (core.js showAvatar). */
.avatar { position: relative; overflow: hidden; flex: none; }
.avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .2s; }
.avatar.has-picture img { opacity: 1; }
.avatar-large { width: 72px; height: 72px; font-size: 28px; }
.avatar-small { width: 28px; height: 28px; font-size: 12px; }

/* ---------- Layout ---------- */

:root { --sticky-top: 0px; } /* what is pinned at the top of the page: nothing, the top bar on a phone */
main { max-width: 1080px; margin: 0 auto; padding: 8px 32px 80px; }
.page-head { margin: 32px 0 20px; }
.page-head .muted { font-size: 16px; max-width: 70ch; }
.card, .home-service {
  background: var(--card);
  border-radius: var(--radius); /* no blur: only floating things (player, bars, dialogs) have it */
  box-shadow: var(--shadow);
  padding: 24px;
  margin-top: 16px;
}
.card > .muted:first-of-type { margin-bottom: 4px; }
/* Cards only for units (a tool's setup and result, the Home and Tools cards, dialogs, empty
   states): on Stats, Library, Gigs, Settings and Admin the parts sit on the page, a line between them. */
:is(#tool-stats, #tool-library, #tool-djsets, #tool-settings, #tool-admin, #tool-about) :is(.card:not(.empty), .home-service) {
  background: none; box-shadow: none; border-radius: 0; padding: 0; margin-top: 32px; /* sections apart by space, not by more lines */
}

/* Signed-out screens */
.auth { display: grid; place-items: center; min-height: calc(100vh - 160px); }
.auth-card { width: 100%; max-width: 420px; padding: 32px; }
.auth-card h2 { font-size: 28px; }
.auth-card .muted { margin-bottom: 18px; }

/* Empty states */
.empty { text-align: center; padding: 48px 24px; }
.empty-title { margin: 0 0 6px; font: 700 20px/1.3 var(--heading-font); }
.empty .muted { margin-bottom: 20px; }
.empty-state { text-align: center; padding: 40px 16px; } /* an empty state inside a page (the missing album) */
.empty-state .muted { margin-bottom: 20px; }
#dup-count:empty { display: none; }
.card-lead { margin: 0 0 12px; } /* a card's first line, where a title would repeat the page's */

/* ---------- Forms ---------- */

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.stack { display: grid; gap: 14px; }
.stack.narrow, .row.narrow { max-width: 420px; }
/* A field (its label above it) with its button beside it: the button in line with the field. */
.row.field-row { align-items: flex-end; }
/* Fields about one thing, under its name (New sync's "The copy"). */
.copy-group { display: grid; gap: 14px; margin: 0; padding: 0; border: 0; min-width: 0; }
.copy-group legend { padding: 0; margin-bottom: 10px; font-size: 15px; font-weight: 700; }
.copy-where { display: grid; grid-template-columns: 1fr 1.6fr; gap: 14px; } /* Service and Playlist on one line */
@media (max-width: 560px) { .copy-where { grid-template-columns: 1fr; } }
.stack label, .fields label, .field label { display: grid; gap: 6px; font-size: 14px; font-weight: 550; color: var(--muted); }
.stack button { justify-self: start; }
.stack button.wide { justify-self: stretch; }
.stack .hint { margin: -6px 0 0; }
.fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin: 16px 0; }

input, select, textarea {
  flex: 1;
  min-width: 0;
  height: 44px;
  padding: 0 12px;
  border: 1px solid var(--field-border);
  border-radius: var(--radius-control);
  background: var(--field);
  color: var(--text);
  font: inherit;
}
select option, select optgroup { background: var(--solid); color: var(--text); }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; border-color: transparent; }
input[type="checkbox"], input[type="radio"] { flex: none; width: 20px; height: 20px; padding: 0; accent-color: var(--accent); }
input[type="checkbox"] { width: 24px; height: 24px; } /* WCAG 2.5.8: a 24px target, also alone in a table cell */
.toggle-row { cursor: pointer; } /* a click on the row ticks its box (core.js rowToggles) */
input.short, select.short { flex: 0 0 150px; }

/* Number fields: the app's own up/down buttons (core.js stepper) instead of the browser's */
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.stepper { position: relative; display: inline-flex; flex: 0 0 auto; min-width: 0; }
.stepper input { flex: 1 1 auto; width: 100%; padding-right: 26px; }
.steps { position: absolute; top: 4px; bottom: 4px; right: 4px; width: 18px; display: grid; grid-template-rows: 1fr 1fr; gap: 1px; }
.stepper button {
  height: auto; width: 18px; padding: 0; border-radius: 4px; background: transparent; color: var(--muted);
  opacity: .55; transition: opacity .15s, background-color .15s, color .15s;
}
.stepper button::before {
  content: ''; width: 10px; height: 10px; background: currentColor;
  -webkit-mask: url(icons/chevron-up.svg) center / contain no-repeat; mask: url(icons/chevron-up.svg) center / contain no-repeat;
}
.stepper button.down::before { -webkit-mask-image: url(icons/chevron-down.svg); mask-image: url(icons/chevron-down.svg); }
.stepper:hover button, .stepper:focus-within button { opacity: 1; }
.stepper button:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent-ink); filter: none; }
.stepper button:active:not(:disabled) { transform: none; background: var(--accent-soft); }
@media (pointer: coarse) {
  .stepper button { display: none; }
  .stepper input { padding-right: 8px; }
}
input.tiny { flex: 0 0 76px; }
.stepper:has(input.tiny) { flex: 0 0 84px; }
.stepper input.tiny { flex: 1 1 auto; }
.row.or .muted { font-size: 13px; white-space: nowrap; }
.avatar-gravatar { display: flex; align-items: center; gap: 8px; margin: 12px 0 0; cursor: pointer; }
label.inline { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.actions { margin-top: 18px; }
.generate { margin-top: 4px; }

/* ---------- Buttons ---------- */

button, .button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 44px;
  padding: 0 18px;
  border: 0;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-text);
  font: inherit;
  font-weight: 650;
  text-decoration: none;
  cursor: pointer;
  transition: filter .15s, background-color .15s, transform .05s;
}
button:hover:not(:disabled), .button:hover { filter: brightness(1.06); }
button:active:not(:disabled) { transform: translateY(1px); }
button.secondary, .button.secondary { background: var(--raised); color: var(--text); box-shadow: inset 0 0 0 1px var(--field-border); font-weight: 550; }
button.secondary:hover:not(:disabled), .button.secondary:hover { background: var(--tile); filter: none; }
button.ghost { background: transparent; color: var(--muted); font-weight: 550; }
button.ghost:hover:not(:disabled) { color: var(--text); background: var(--raised); filter: none; }
button.small, .button.small { height: 36px; padding: 0 14px; font-size: 14px; } /* also a link or a menu's ⋯ (a summary) that looks like one */
button.icon { width: 44px; padding: 0; }
button.large { height: 50px; padding: 0 28px; font-size: 16px; font-weight: 700; }
/* Deleting or removing: in the error colours, quieter than the main action beside it. */
button.danger { background: var(--err-bg); color: var(--err-text); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--err-text) 40%, transparent); }
button:disabled { opacity: .5; cursor: progress; }

/* ---------- Messages ---------- */

.flash, .warning { padding: 12px 14px; border-radius: var(--radius-sm); margin: 16px 0 0; }
.flash { background: var(--err-bg); color: var(--err-text); }
/* The page's message: pinned at the top of the window while you scroll (core.js flash), on a solid
   background so it reads over the page. */
#flash:not([hidden]) { display: flex; align-items: center; gap: 12px; }
#flash > span { flex: 1; }
.flash-close { flex: none; color: inherit; }
#flash { position: sticky; top: calc(var(--sticky-top) + 8px); z-index: 15; background: color-mix(in srgb, var(--err-text) 14%, var(--solid)); box-shadow: var(--shadow); }
/* A message about what a button did, right under its row (core.js say). */
.inline-error { margin: 10px 0 0; padding: 10px 14px; border-radius: var(--radius-sm); background: var(--err-bg); color: var(--err-text); font-size: 14px; }
.warning { background: var(--warn-bg); color: var(--warn-text); }
/* A service that needs one more permission (core.js permissionNote): the sentence, its button at the right. */
.permission-note {
  display: flex; align-items: center; gap: 8px 14px; min-height: 56px; margin: 12px 0 16px; padding: 8px 2px;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); color: var(--warn-text); font-size: 14px;
}
.permission-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--warn-text); }
.permission-text { flex: 1 1 auto; min-width: 0; }
.permission-note .button { flex: none; }
@media (max-width: 760px) { /* the button at the right of the text's last line, as Home's rows */
  .permission-note { display: grid; grid-template-columns: 8px minmax(0, 1fr) auto; align-items: end; }
  .permission-dot { align-self: start; margin-top: 7px; }
}

/* Analysis progress */
.progress, .home-progress { display: grid; gap: 8px; }
.progress-text, .home-progress-text { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; }
.progress progress, .home-progress progress {
  width: 100%; height: 10px; border: 0; border-radius: 999px; overflow: hidden;
  background: var(--accent-soft); appearance: none; -webkit-appearance: none;
}
.progress progress::-webkit-progress-bar, .home-progress progress::-webkit-progress-bar { background: var(--accent-soft); border-radius: 999px; }
.progress progress::-webkit-progress-value, .home-progress progress::-webkit-progress-value { background: var(--accent-ink); border-radius: 999px; transition: width .3s; }
.progress progress::-moz-progress-bar, .home-progress progress::-moz-progress-bar { background: var(--accent-ink); border-radius: 999px; }

/* ---------- Streaming services ---------- */

/* The services' own logo marks (brand/services/README.md: colours and sizes follow their brand rules).
   The SVG is a mask, so the colour comes from the background colour below. */
.service-icon { width: 28px; height: 28px; flex: none; margin: 0 4px; }
.service-icon.spotify {
  background: light-dark(#000000, #FFFFFF);
  -webkit-mask: url(brand/services/spotify.svg) center / contain no-repeat;
  mask: url(brand/services/spotify.svg) center / contain no-repeat;
}
.service-icon.tidal {
  background: light-dark(#000000, #FFFFFF);
  -webkit-mask: url(brand/services/tidal.svg) center / contain no-repeat;
  mask: url(brand/services/tidal.svg) center / contain no-repeat;
}
/* Spotify green only on white or black; the dark rows are neither, so both logos are white there. */
.connect-buttons { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.connect-buttons .service-icon { width: 20px; height: 20px; margin: 0; background: currentColor; }

/* ---------- Tables ---------- */

.table-wrap { overflow-x: auto; margin: 0 -4px; padding: 0 4px; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th {
  text-align: left; padding: 8px 10px;
  font-size: 13px; font-weight: 600;
  color: var(--muted); border-bottom: 1px solid var(--border); white-space: nowrap;
}
td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
td:first-child, td:nth-child(2) { white-space: normal; }
tbody tr:hover td { background: color-mix(in srgb, var(--raised) 60%, transparent); }
.num { text-align: right; }
td input { width: 76px; height: 36px; flex: none; text-align: right; padding: 0 8px; }
table.ranges td input { width: 96px; }
table.ranges td:nth-child(3) { color: var(--muted); }
tr.disabled td { opacity: .55; }
.actions-cell { text-align: right; white-space: nowrap; } /* a table cell: the row's lines stay in line */
/* A row's actions in a menu: the button, then a short list under it, right-aligned. */
.row-menu { position: relative; display: inline-block; text-align: left; }
.row-menu > summary { list-style: none; cursor: pointer; }
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu > summary::after {
  content: ""; width: 12px; height: 12px; background: currentColor;
  -webkit-mask: url(icons/chevron-down.svg) center / contain no-repeat; mask: url(icons/chevron-down.svg) center / contain no-repeat;
}
.row-menu > summary.more::after { content: none; } /* "⋯" says it already */
.row-menu > summary:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
.row-menu-list {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 6; min-width: 180px;
  display: grid; gap: 2px; padding: 6px; border-radius: var(--radius-sm);
  background: var(--solid); box-shadow: var(--shadow);
}
.row-menu-list button { justify-content: flex-start; width: 100%; white-space: nowrap; text-align: left; }
.song-with-art a { font-weight: 600; text-decoration: none; } /* a song's title as in every list */
.song-with-art a:hover { text-decoration: underline; }
/* On a phone: what a column said goes under the name (Admin › People). */
.inline-detail { display: none; }
@media (max-width: 760px) { .inline-detail { display: block; } }
@media (max-width: 760px) { /* Accounts on a phone: email, role and status under the name */
  [data-admin-section="people"] .opt, [data-admin-section="people"] thead { display: none; }
  [data-admin-section="people"] .inline-detail { overflow-wrap: anywhere; }
  /* A field that submits gets its button under it, full width (the invite, the tempo search). */
  #invite-create-form, #tempo-search { flex-direction: column; align-items: stretch; }
  #invite-create-form input:not([type="checkbox"]), #tempo-search input, #tempo-search select { flex: none; width: 100%; }
  #invite-create-form label.inline { white-space: normal; }
}

/* ---------- BPM split ---------- */

/* Setup: one line, no card */
.setup { display: grid; gap: 12px; }
.setup-line { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 16px; }
.setup-line.top { align-items: flex-start; } /* a tall field beside: the labels in one line */
.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; flex: 1 1 180px; min-width: 0; } /* the column shrinks with the field, so nothing sticks out */
.field.grow { flex: 2 1 280px; }
.with-button { display: flex; gap: 8px; }
.with-button > button { flex: none; }
.link-field { max-width: 560px; }
.link-field input { flex: none; width: 100%; }
#template-summary { margin: 0; }

/* Template editor, open only when asked for */
.editor { margin: 16px 0 24px; padding: 20px 24px; border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow); }
.editor-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
.editor-head h3 { margin: 0; }
.editor .fields { margin-bottom: 8px; }
#template-state.dirty { color: var(--warn-text); }

/* The result: the one raised surface */
.result { margin-top: 24px; padding: 24px; border-radius: var(--radius); /* the cards' padding: the step numbers line up */ background: var(--card);  box-shadow: var(--shadow); }
.result .result-title { margin: 0 0 4px; font-size: 20px; font-weight: 800; } /* a section: above .result h3, its parts */
.result-title:focus { outline: none; }
.lead { margin: 0 0 26px; color: var(--muted); font-size: 16px; max-width: 72ch; }
.result h3 { font-size: 16px; }

/* Tempo map: a bar per range, as high as its number of tracks */
.tempo-map {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(28px, 1fr)); gap: 6px;
  align-items: end; height: 250px; border-bottom: 2px solid var(--text);
}
.tempo-range {
  height: 100%; padding: 0; border: 0; border-radius: 0; background: transparent; color: var(--text);
  display: grid; grid-template-rows: 1fr auto; grid-template-columns: minmax(0, 24px); justify-content: center; /* thin bars, as the other charts */ align-items: end; justify-items: stretch;
  position: relative; font-weight: 400;
}
.tempo-range:hover:not(:disabled) { filter: none; }
.tempo-range:active:not(:disabled) { transform: none; }
.tempo-range .count {
  align-self: end; margin-bottom: 6px; text-align: center;
  font: 700 18px/1 var(--heading-font); font-variant-numeric: tabular-nums;
}
.tempo-range .level {
  display: block; height: calc(var(--size, 0) * 180px); min-height: 2px;
  border-radius: 4px 4px 0 0; background: var(--bar); transition: height .3s, background-color .2s;
}
.tempo-range .axis {
  position: absolute; top: 100%; left: 0; right: 0; padding-top: 8px; text-align: center;
  font: 600 13px/1.2 var(--heading-font); font-variant-numeric: tabular-nums; color: var(--muted);
}
.tempo-range .level.zero { height: 2px; border-radius: 1px; background: var(--border); }
.tempo-range:hover .level { opacity: .85; }
.tempo-range[aria-pressed="true"] .level { background: var(--accent); }
.tempo-range[aria-pressed="true"] .count, .tempo-range[aria-pressed="true"] .axis { color: var(--accent-ink); }
.tempo-map + * { margin-top: 44px; }

/* Tracks to check by ear */
.checks { padding: 20px 22px; border-radius: var(--radius-sm); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.checks-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; margin-bottom: 12px; }
.checks-head h3 { margin: 0; font-size: 16px; }
.check-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.check { display: grid; gap: 12px; padding: 14px 0; border-top: 1px solid var(--border); } /* rows in the tile, not raised boxes: two surfaces at most */
.check:first-child { border-top: 0; padding-top: 4px; }
.check-main { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; }
.check-text { flex: 1 1 240px; min-width: 0; }
.check-text p { margin: 0; }
.check-title { font-weight: 650; }
.check-title a { text-decoration: none; }
.check-title a:hover { text-decoration: underline; }
.check-text .artist { font-size: 14px; }
.artist { font-weight: 400; color: var(--muted); }
.check-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.check-actions .stepper { flex: 0 0 96px; } /* "e.g. 92" fits */
.check-actions input { height: 36px; }
.listen { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding-left: 60px; }
.preview-progress { flex: 1 1 200px; display: flex; align-items: center; gap: 10px; min-width: 0; }
/* The play position: a slider with the played part in the accent colour (--played, 0 to 1) */
input.seek {
  flex: 1; min-width: 0; height: 24px; padding: 0; border: 0; background: transparent;
  -webkit-appearance: none; appearance: none; cursor: pointer;
}
input.seek::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px;
  background: linear-gradient(var(--accent-ink), var(--accent-ink)) 0 0 / calc(var(--played, 0) * 100%) 100% no-repeat, var(--border);
}
input.seek::-moz-range-track {
  height: 4px; border-radius: 2px;
  background: linear-gradient(var(--accent-ink), var(--accent-ink)) 0 0 / calc(var(--played, 0) * 100%) 100% no-repeat, var(--border);
}
input.seek::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 14px; height: 14px; margin-top: -5px;
  border: 0; border-radius: 50%; background: var(--accent-ink);
}
input.seek::-moz-range-thumb { width: 14px; height: 14px; border: 0; border-radius: 50%; background: var(--accent-ink); }
input.seek:hover::-webkit-slider-thumb { transform: scale(1.2); }
input.seek:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }
input.seek:disabled { cursor: default; }
input.seek:disabled::-webkit-slider-thumb { visibility: hidden; }
input.seek:disabled::-moz-range-thumb { visibility: hidden; }
.time { flex: none; font-variant-numeric: tabular-nums; }
button.play { flex: 0 0 44px; width: 44px; padding: 0; border-radius: 50%; background: var(--accent); color: var(--accent-text); }
.play-icon {
  width: 16px; height: 16px; background: currentColor;
  -webkit-mask: url(icons/play.svg) center / contain no-repeat; mask: url(icons/play.svg) center / contain no-repeat;
}
.play.playing .play-icon { -webkit-mask-image: url(icons/pause.svg); mask-image: url(icons/pause.svg); }
button.tap { height: 30px; padding: 0 12px; font-size: 13px; box-shadow: inset 0 0 0 1px var(--text); font-weight: 650; }
button.tap.tapped { animation: tap .12s ease-out; }
@keyframes tap { from { background: var(--accent-soft); } }
.tap-result { flex: 1 1 220px; font-size: 14px; }

/* Tracks in the chosen range */
.range h3 { margin-bottom: 2px; }
.range-tracks { list-style: none; margin: 12px 0 0; padding: 0; border-top: 1px solid var(--border); }
.range-tracks li { display: flex; align-items: center; gap: 16px; padding: var(--row-pad) 2px; border-bottom: 1px solid var(--border); }
.range-tracks .track { flex: 1; min-width: 0; display: grid; }
.range-tracks .track a { font-weight: 600; text-decoration: none; }
.range-tracks .track a:hover { text-decoration: underline; }
.range-tracks .artist { font-size: 14px; }
.range-tracks .note { flex: none; font-size: 13px; color: var(--muted); text-align: right; }
.row-play-gap { flex: 0 0 32px; } /* a song without ▶ (not found): the rows stay in line */
.range-tracks .artist { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---------- Home ---------- */
.home-card-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 16px; margin-bottom: 14px; }
.home-card-head h3 { margin: 0; display: flex; align-items: center; gap: 8px; }
.home-card-head h3 .service-icon { width: 24px; height: 24px; margin: 0; }
.home-card-head p { display: flex; align-items: baseline; gap: 10px; }
button.link-button { height: auto; padding: 0; background: none; color: var(--accent-ink); font-weight: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
button.link-button:hover:not(:disabled) { filter: none; text-decoration-thickness: 2px; }
.home-progress { margin-bottom: 16px; }
.home-service .error, .home-listening .error { color: var(--err-text); }

/* Figures: a label above a value, a note under it */
.home-figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 16px; margin: 0 0 8px; }
.home-figure { margin: 0; display: grid; align-content: start; gap: 2px; }
.home-figure dt { font-size: 14px; color: var(--muted); }
.home-figure dd { margin: 0; }
.home-figure .value { font: 700 30px/1.15 var(--heading-font); }
.home-figure .note { font-size: 13px; color: var(--muted); }

/* The tempo spread: columns from one baseline, 2px apart, at most 24px wide */
.home-tempo { margin: 8px 0 0; }
.home-tempo-bars { display: flex; align-items: stretch; gap: 2px; height: 150px; border-bottom: 1px solid var(--border); }
.home-tempo-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; }
.home-tempo-col { cursor: pointer; }
/* The bars quiet (grey), the tallest in the accent; the one pointed at or tapped too, with its count. */
.home-tempo-bar { width: 100%; max-width: 24px; height: calc(var(--size, 0) * (100% - 18px)); border-radius: 4px 4px 0 0; background: var(--bar); }
.home-tempo-col.top .home-tempo-bar, .home-tempo-col:hover .home-tempo-bar, .home-tempo-col.picked .home-tempo-bar { background: var(--accent); }
.home-tempo-count { font-size: 12px; line-height: 18px; font-weight: 650; color: var(--muted); visibility: hidden; }
.home-tempo-count.shown, .home-tempo-col:hover .home-tempo-count, .home-tempo-col.picked .home-tempo-count { visibility: visible; }
.home-tempo-col:hover .home-tempo-count, .home-tempo-col.picked .home-tempo-count { color: var(--text); }
.home-tempo-axis { display: flex; gap: 2px; margin-top: 4px; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.home-tempo-axis span { flex: 1 1 0; min-width: 0; text-align: center; }
.home-tempo figcaption { margin-top: 6px; }

/* The feel: a meter per feature, filled to its average */
.home-meters { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px 24px; }
.home-meters + .muted { margin-top: 10px; }
.home-meter { display: grid; grid-template-columns: 1fr auto; gap: 6px; font-size: 14px; }
.home-meter-value { font-weight: 650; }
.home-meter-track { grid-column: 1 / -1; height: 8px; border-radius: 999px; background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--field-border); overflow: hidden; } /* the edge (3:1 against the page) shows where 100% is */
.home-meter-fill { display: block; height: 100%; width: calc(var(--value, 0) * 100%); border-radius: 999px; background: var(--accent); }

.home-columns[hidden] { display: none; }
.home-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 0 32px; }
/* A top list beside a list of songs: a third for the top list, two thirds for the songs (with their chips). */
@media (min-width: 1000px) { .home-columns-songs { grid-template-columns: 1fr 2fr; } }
.home-ranked { margin: 0; padding-left: 22px; display: grid; gap: 6px; }
.home-ranked li { padding-left: 4px; }
.home-ranked li > * + * { margin-left: 8px; }
.home-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.home-list li { display: grid; gap: 2px; }
.home-songs { margin-top: 0; }

/* A choice of a few options as one control (the listening period) */
.segmented[hidden] { display: none; }
.segmented { display: inline-flex; padding: 3px; border-radius: 999px; background: var(--raised); }
.segmented label { position: relative; }
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.segmented span { display: block; padding: 6px 14px; border-radius: 999px; font-size: 14px; font-weight: 550; cursor: pointer; color: var(--muted); }
.segmented input:checked + span { background: var(--accent); color: var(--accent-text); }
.segmented input:focus-visible + span { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* A ▶ on a song in a list (see js/player.js) */
button.row-play { flex: 0 0 32px; width: 32px; height: 32px; }
button.row-play .play-icon { width: 12px; height: 12px; }
td > button.row-play { display: grid; place-items: center; }

/* The player: a glass bar along the bottom of the page */
.player {
  position: sticky; bottom: 12px; z-index: 20; max-width: 1080px; margin: auto auto 12px; width: calc(100% - 64px);
  display: grid; grid-template-columns: auto minmax(0, 1.2fr) minmax(0, 1fr) auto; grid-template-areas: "controls track progress close" "controls track source close" "controls facts facts close";
  align-items: center; gap: 2px 20px; padding: 10px 14px; border-radius: 22px;
  background: color-mix(in srgb, var(--solid) 90%, transparent); backdrop-filter: var(--glass); -webkit-backdrop-filter: var(--glass); box-shadow: var(--shadow);
}
.player[hidden] { display: none; }
.player-controls { grid-area: controls; display: flex; align-items: center; gap: 4px; }
.player-track { grid-area: track; margin: 0; display: grid; min-width: 0; }
.player-track a { font-weight: 650; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* In a list too: the title shortens, its length and tempo stay on its line (as in the player). */
/* Where the title and its length and tempo don't fit on a line (a phone, a lead before the song),
   those move under it instead of shortening the title to a letter. */
.range-tracks .song-title { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; min-width: 0; }
.range-tracks .song-title > a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.range-tracks .song-title .chip { flex: none; }
.player-track .song-title { display: flex; flex-wrap: nowrap; align-items: center; gap: 8px; min-width: 0; } /* the title shortens, the tempo stays */
.player-track .song-title a { min-width: 0; }
.player-track .song-title .chip { flex: none; display: inline-flex; align-items: center; padding: 3px 7px; line-height: 1; } /* the text in the middle */
.player-track a:hover { text-decoration: underline; }
.player-track .artist { font-size: 14px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-progress { grid-area: progress; display: flex; align-items: center; gap: 10px; min-width: 0; }
.player-source { grid-area: source; margin: 0; }
.player-facts { grid-area: facts; list-style: none; margin: 6px 0 2px; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.player-facts[hidden] { display: none; }
.player-facts .chip { padding: 3px 7px; font-size: 12px; line-height: 1; color: var(--text); font-variant-numeric: tabular-nums; } /* the same chip as beside the title */
/* Beside a song's title in a list: its length and tempo (core.js titled). */
.song-title .chip { white-space: nowrap; padding: 0 7px; font-size: 12px; font-weight: 400; color: var(--text); font-variant-numeric: tabular-nums; }
.song-title .chip[hidden] { display: none; }
#player-close { grid-area: close; }

.result > * + * { margin-top: 30px; }
.result > .result-title + .lead { margin-top: 0; }

/* All tracks, folded away: the range list's style, with the tempo editable */
/* One look for a part that opens: its name as a part heading (16px) in the text colour, with its ▸. */
.all-tracks summary, .change-block summary, .home-history-add summary, .how summary { width: fit-content; cursor: pointer; font: 700 16px/1.3 var(--heading-font); padding: 6px 0; }
.all-tracks[open] summary { margin-bottom: 8px; }
.all-list li { flex-wrap: wrap; gap: 6px 16px; }
.all-list .stepper { flex: 0 0 76px; }
input.bpm-input {
  height: 36px; padding: 0 26px 0 6px; text-align: right;
  font: 700 18px/1 var(--heading-font); font-variant-numeric: tabular-nums;
}
.all-list .where { flex: 0 1 220px; display: grid; text-align: right; font-size: 14px; }
.all-list .playlist { font-weight: 600; }
.all-list .note { font-size: 13px; color: var(--muted); }
.row-tools { flex: none; display: flex; gap: 6px; justify-content: flex-end; }
.all-list li.unsorted .playlist { color: var(--muted); font-weight: 400; }
.chip { display: inline-block; border-radius: 999px; padding: 2px 9px; font-size: 12px; box-shadow: inset 0 0 0 1px var(--border); color: var(--muted); }
.chip.auto { box-shadow: inset 0 0 0 1px var(--accent-ink); color: var(--accent-ink); }
/* Amber: needs a look ("check above", "Not sure"); the accent only for what is in progress. */
.chip.varies, .chip.suggest { box-shadow: inset 0 0 0 1px var(--warn-text); color: var(--warn-text); }

.create { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 20px; padding-top: 22px; border-top: 1px solid var(--border); } /* buttons in line with the fields, not their labels */
.create p { margin: 0; flex: 1 1 280px; align-self: center; }
.results { padding-left: 18px; margin: 14px 0 0; }
.results .new { color: var(--ok); font-weight: 600; }
.results .error { color: var(--err-text); }

/* ---------- Sync ---------- */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.pairs-section { margin-top: 16px; }
/* A page's own action above its list (New sync). */
.page-actions { display: flex; justify-content: flex-start; margin: 0 0 8px; }
/* The review of a sync: under its row, across the row's width. */
.pair > .sync-review { flex: 1 1 100%; margin: 8px 0 4px; }
/* The row whose review is open: an accent edge down the row and its review; its own button gives
   way to the review's. */
.pair.open { box-shadow: inset 3px 0 var(--accent); padding-left: 16px; }
.pair.open .pair-check { display: none; }
.dialog.wide { width: min(520px, calc(100vw - 32px)); } /* the Sync setup: five fields */
.dialog.wide .field { flex: none; }
.pairs-section h3 { margin-bottom: 8px; }
.pairs { list-style: none; margin: 12px 0 -10px; padding: 0; }
.pair { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; padding: var(--row-pad) 2px; border-top: 1px solid var(--border); }
.pair-main { flex: 1 1 320px; min-width: 0; }
.pair-main p { margin: 0; }
.pair-route { font-size: 16px; }
.pair-actions { display: flex; gap: 6px; flex: none; }
/* A row's actions sit at its right end, also when they wrap under the text on a phone (as
   Home's rows): Sync's pairs, Settings' services, Admin's problems. */
.pair-actions, .service-row-actions, .tasks-list li > button { margin-left: auto; }
.changes { display: grid; gap: 8px; }
.change-block .range-tracks li > input[type="checkbox"] { flex: none; }
.options { border: 0; margin: 8px 0 0; padding: 0; display: grid; gap: 2px; }
.option { display: flex; align-items: center; gap: 10px; min-height: 40px; padding: 4px 8px; border-radius: 8px; cursor: pointer; }
.option:hover { background: var(--accent-soft); }
.option input { flex: none; }
.option a { margin-left: 4px; }
#sync-checks .check { display: block; }

/* Settings: a set of choices */
.choice { border: 0; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 8px 18px; }
.choice legend { padding: 0; margin-bottom: 8px; font-size: 14px; }
.choice label { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; cursor: pointer; }

/* ---------- Tempo wave ---------- */

.wave-playlists { border: 0; margin: 0; padding: 0; }
.wave-playlists legend { padding: 0; margin-bottom: 6px; font-size: 14px; font-weight: 550; color: var(--muted); }
.wave-playlists .checklist { margin: 0; max-height: 220px; }
.wave-playlists .muted { margin-top: 6px; }
.wave-shares { border: 0; margin: 4px 0 8px; padding: 0; }
.wave-shares[hidden] { display: none; }
.wave-shares legend { padding: 0; margin-bottom: 2px; font-size: 14px; font-weight: 550; color: var(--muted); }
.wave-share-list { list-style: none; margin: 8px 0; padding: 0; display: grid; gap: 8px; max-width: 520px; }
.wave-share-list li { display: grid; grid-template-columns: minmax(0, 240px) 96px auto; align-items: center; gap: 10px; }
.wave-share-list label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#wave-share-total.off { color: var(--err-text); }
.wave-settings { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); margin: 4px 0; }
.wave-settings .stepper { width: 100%; }
/* A dot per song at its tempo, joined by a line, on a few labelled tempo lines (wave.js drawWave). */
.wave-actions { display: flex; margin: 8px 0 0; }
.wave-chart { height: 224px; }
.wave-chart svg { display: block; overflow: visible; }
.wave-grid { stroke: var(--border); stroke-width: 1; }
.wave-tick { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.wave-line { fill: none; stroke: var(--accent-ink); stroke-width: 2; stroke-linejoin: round; }
.wave-dot { fill: var(--accent); stroke: var(--solid); stroke-width: 2; }
.wave-dot:hover { r: 6; }
.wave-label { fill: var(--muted); font-size: 11px; font-weight: 650; font-variant-numeric: tabular-nums; }
.wave-list .bpm { flex-basis: 40px; }
.wave-name-field { flex: 1 1 260px; max-width: 420px; }

/* ---------- Import list ---------- */

textarea { height: auto; min-height: 180px; padding: 10px 12px; resize: vertical; font-variant-numeric: tabular-nums; line-height: 1.5; }
input[type="file"] { height: auto; padding: 5px 6px; line-height: 32px; } /* as tall as its button, the button inside it */
/* The file picker's button looks like the other secondary buttons; the chosen file's name beside it. */
input[type="file"]::file-selector-button {
  margin-right: 12px; padding: 0 14px; height: 32px; vertical-align: middle; border: 0; border-radius: 999px; cursor: pointer;
  font: inherit; font-weight: 550; background: var(--raised); color: var(--text); box-shadow: inset 0 0 0 1px var(--field-border);
}
input[type="file"]::file-selector-button:hover { background: var(--tile); }
.import-file-field { max-width: 420px; }
.import-repeats { display: flex; align-items: center; gap: 10px; margin: -10px 0 4px; }
.import-repeats[hidden] { display: none; }
.import-list li > input[type="checkbox"] { flex: none; }
.import-line { font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.import-none { font-weight: 600; color: var(--muted); }
.import-choice { flex: none; height: 34px; margin-top: 6px; width: auto; max-width: min(100%, 520px); font-size: 14px; }
.import-list .note { display: flex; align-items: center; gap: 8px; }
.import-list .track { flex: 1 1 140px; } /* a song not found keeps its words on a line */
.import-gig { margin: 22px 0 0; padding: 18px 0 0; border: 0; border-top: 1px solid var(--border); }
.import-gig[hidden] { display: none; }
.import-gig legend { float: left; width: 100%; margin-bottom: 8px; padding: 0; font-weight: 700; } /* under the line, as the other headings */
.import-gig legend + * { clear: both; }
.import-gig .create { border-top: 0; padding-top: 8px; }
.import-gig-date { flex: 0 1 180px; }
.import-gig-start { flex: 0 1 130px; }
.import-service-field { flex: 0 1 160px; }

/* ---------- Home: the summary ---------- */

/* Home: Start something (the tool cards), Needs you, Lately: on the page, no outer card. */
.home-summary { display: grid; gap: 32px; }
.home-section > h3 { margin: 0 0 12px; font-size: 20px; }
.home-section .tool-cards { grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr)); }
.tool-card-note { font-size: 13px; color: var(--text); font-weight: 550; }
.tool-card-note:empty { display: none; }
.home-rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.home-rows > li { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; min-height: 56px; padding: 8px 2px; border-bottom: 1px solid var(--border); }
.home-row-kind { flex: 0 0 96px; }
.home-row-text { flex: 1 1 260px; min-width: 0; }
.home-rows .button { flex: none; height: 36px; padding: 0 14px; font-size: 14px; }
.home-row-link { flex: none; font-weight: 600; white-space: nowrap; }
.home-row-kind-inline, .phone-only { display: none; }
@media (max-width: 760px) {
  /* A row in two columns: the text (starting with what it is about), the action at the right of its last line. */
  .home-rows > li { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 12px; }
  .home-rows .home-row-kind { display: none; }
  .home-row-kind-inline { display: inline; }
  /* A gig: its name and when on the left, Songs and ⋯ at the right (Make playlist is in ⋯). */
  .home-gig { flex-wrap: nowrap; align-items: center; }
  .home-gig-label { flex: 1; min-width: 0; display: grid; }
  .home-gig-actions { flex: none; flex-wrap: nowrap; }
  .not-phone { display: none; }
  .phone-only { display: inline-flex; }
}
.home-summary[hidden] { display: none; }
.home-summary > .card { margin: 0; }

/* ---------- Stats: listening history ---------- */

/* Listening: plays per month as a strip of bars (oldest left), the chosen month in the accent;
   each bar a button. Many months: the strip scrolls sideways. */
.month-figure { margin: 4px 0 8px; }
/* Listening's one row of choices, above everything it scopes. */
.listening-range { margin: 0 0 8px; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.listening-range span { white-space: nowrap; }
.month-strip {
  display: flex; align-items: stretch; gap: 2px; height: 134px; padding: 2px 2px 0; overflow-x: auto; scrollbar-width: thin;
}
/* A bar per month above its year (shown under January and the first month): both inside the strip, which clips what sticks out. */
.month-col {
  flex: 1 0 14px; max-width: 24px; height: 100%; padding: 0; border-radius: 4px 4px 0 0;
  display: flex; flex-direction: column; justify-content: flex-end; background: transparent; color: var(--muted); overflow: visible;
}
.month-col:hover:not(:disabled) { background: var(--raised); filter: none; }
.month-col:active:not(:disabled) { transform: none; }
.month-col:focus-visible { outline-offset: 0; }
.month-bar { display: block; width: 100%; height: calc(var(--size, 0) * (100% - 38px)); min-height: 2px; border-radius: 4px 4px 0 0; background: var(--bar); box-shadow: 0 1px 0 var(--border); }
.month-col[aria-pressed="true"] .month-bar { background: var(--accent); }
.month-count { height: 18px; font-size: 12px; font-weight: 650; line-height: 18px; color: var(--text); text-align: center; white-space: nowrap; font-variant-numeric: tabular-nums; }
.month-tick { height: 20px; padding-top: 4px; font-size: 11px; line-height: 16px; font-variant-numeric: tabular-nums; white-space: nowrap; text-align: left; }
.month-figure figcaption { margin-top: 6px; }
.month-name { margin: 14px 0 10px; font-size: 20px; }

/* Patterns: time of day, seasons and trends, one under the other. */
.pattern + .pattern { margin-top: 44px; } /* sections apart by space; lines belong to lists */
.pattern > h4 { margin: 0 0 12px; font-size: 20px; }
.pattern-make { margin-top: 12px; }
.pattern-list-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; margin-top: 28px; }
.pattern > .pattern-list-head:nth-child(2) { margin-top: 0; } /* first in its part: right under the title */
.pattern-list-head h4 { margin: 0; }
.pattern-list-head .home-history-sub { margin: 0; }

.home-history-sub { max-width: 100%; overflow-x: auto; scrollbar-width: none; } /* one row, swiped on a phone */
.home-history-sub::-webkit-scrollbar { display: none; }
.home-history-sub span { white-space: nowrap; }
.home-history-sub { margin: 4px 0 8px; }
.home-history-panel h4 { margin: 18px 0 8px; }
.home-history-month { max-width: 320px; margin-bottom: 16px; }
.home-history-make { margin-top: 18px; }
.home-history-make[hidden] { display: none; }
.home-history-add { margin: 0; } /* a card of its own */
.home-history-add > * + * { margin-top: 12px; }
.home-history-add .field { max-width: 420px; }
.home-gigs { list-style: none; margin: 0 0 8px; padding: 0; border-top: 1px solid var(--border); }
.home-gigs > li { border-bottom: 1px solid var(--border); padding: var(--row-pad) 2px; }
.home-gig { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
.home-gig-name { font-weight: 650; }
.home-gig-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-left: auto; }
/* Songs opens and closes the list: a chevron says which, as on the menus (not a colour). */
.home-gig-actions button[aria-expanded]::after {
  content: ""; width: 12px; height: 12px; background: currentColor;
  -webkit-mask: url(icons/chevron-down.svg) center / contain no-repeat; mask: url(icons/chevron-down.svg) center / contain no-repeat;
}
.home-gig-actions button[aria-expanded="true"]::after { -webkit-mask-image: url(icons/chevron-up.svg); mask-image: url(icons/chevron-up.svg); }
.home-gig-label { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.home-gig-edit { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 12px; flex: 1 1 100%; padding: 4px 0; }
.home-gig-field { display: grid; gap: 6px; font-size: 14px; font-weight: 550; color: var(--muted); } /* like the other form labels */
.home-gig-field:first-child { flex: 1 1 200px; }
.home-gig-field input { min-width: 0; }
.home-gig-edit-buttons { display: flex; gap: 6px; }
/* One line between the sets: none under the last set or under a set's last song (the list or form below draws its own). */
#djsets-list { border-top: 0; margin: 0; } /* the card's edge is the line above the first set */
/* The card's own padding above the first set and below the last, as in the other cards. */
#djsets-list > li:first-child { padding-top: 0; }
#djsets-list > li:last-child { border-bottom: 0; padding-bottom: 0; }
#djsets-make { margin-top: 12px; }
#djsets-list .range-tracks li:last-child { border-bottom: 0; }
.row-number { flex: 0 0 2.5ch; color: var(--muted); font-size: 14px; font-variant-numeric: tabular-nums; text-align: right; }
.home-gig-songs[hidden] { display: none; }

/* ---------- Deduplicate ---------- */

.selection-tools { margin-top: 14px; align-items: center; }
.how { margin: 8px 0; }
.how p { margin: 8px 0 0; }
.select-all { padding-left: 12px; display: inline-flex; align-items: center; gap: 8px; min-height: 36px; }
.checklist {
  list-style: none; margin: 12px 0; padding: 6px 12px;
  max-height: 320px; overflow-y: auto;
  border-radius: var(--radius-sm); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge);
}
.checklist li { padding: 5px 0; }
.checklist label { cursor: pointer; display: inline-flex; align-items: center; gap: 8px; }
.scope { border: 0; padding: 0; margin: 14px 0 0; display: flex; gap: 18px; flex-wrap: wrap; }
.scope legend { padding: 0; margin-bottom: 6px; font-size: 13px; }
.scope label { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.groups { display: grid; gap: 14px; margin-top: 16px; }
.group { border-radius: var(--radius-sm); padding: 14px; background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.group h3 { font-size: 16px; margin-bottom: 8px; }
.group .warning { margin: 0 0 8px; }
.group .range-tracks { margin-top: 0; }
.group .range-tracks li:last-child { border-bottom: 0; padding-bottom: 0; }
.group li.remove .song-title > a, .group li.remove .artist { text-decoration: line-through; opacity: .6; } /* ticked: this copy goes; its chips and ▶ stay as they are */
/* Status colours mean status: the kept copy is grey; copies to remove need a look (amber), nothing failed. */
.chip.exact, .chip.recording { box-shadow: inset 0 0 0 1px var(--warn-text); color: var(--warn-text); }
.chip.similar { box-shadow: inset 0 0 0 1px var(--warn-text); color: var(--warn-text); }

/* ---------- Admin ---------- */

.user-cell { display: inline-flex; align-items: center; gap: 10px; }
.tempo-results { list-style: none; margin: 10px 0 0; padding: 0; }
.tempo-results:empty { display: none; }
.tempo-row { border-top: 1px solid var(--border); }
.tempo-row:last-child { border-bottom: 1px solid var(--border); }
.tempo-row-main {
  display: grid; grid-template-columns: 32px minmax(0, 1fr) 88px 96px 82px auto; align-items: center; gap: 4px 14px; padding: var(--row-pad) 2px;
}
.tempo-song-name, .tempo-value { display: grid; min-width: 0; }
.tempo-song-name strong, .tempo-song-name .muted { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tempo-value strong { font: 700 18px/1.2 var(--heading-font); font-variant-numeric: tabular-nums; }
.tempo-detail { display: grid; gap: 6px; margin: 0 0 12px; padding: 12px 14px; border-radius: var(--radius-sm); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.tempo-detail p { margin: 0; }
@media (min-width: 761px) { #tempo-search select { flex: 0 1 220px; } } /* on a phone the search stacks (full width) */
.pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 16px; }
.account-head { display: flex; align-items: flex-start; gap: 18px; margin-bottom: 8px; }
.account-text { display: grid; gap: 10px; min-width: 0; }
.account-text .hint { margin: 0; }

.new-link { margin-top: 14px; padding: 14px; border-radius: var(--radius-sm); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.new-link p { margin: 0 0 10px; }
.plain-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; }
.plain-list li { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.plain-list .chip { margin-left: 6px; }
/* An open invite: who it is for, and by whom until when under it; Revoke at the right. */
.plain-list .invite-row { flex-wrap: nowrap; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--border); }
.invite-text { flex: 1; min-width: 0; display: grid; }
.invite-row button { flex: none; }

/* ---------- Small screens ---------- */

/* ---- Two-step sign-in ---- */
.auth-card > button.wide { width: 100%; margin-top: 10px; }
#app-off button, #app-on button, #recovery button { margin-top: 8px; }
.key-list { margin: 10px 0 12px; }
.key-list:empty { display: none; }
.key-item { gap: 4px 12px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.key-name { font-weight: 600; }
.key-item .muted { flex: 1 1 auto; }
.app-setup { display: grid; gap: 10px; justify-items: start; }
.app-setup p { margin: 0; }
.qr { width: 184px; background: var(--paper); border-radius: var(--radius-sm); line-height: 0; }
.qr svg { display: block; width: 100%; height: auto; border-radius: var(--radius-sm); }
.secret { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 14px; color: var(--text); overflow-wrap: anywhere; }
.recovery-box { margin-top: 16px; padding: 16px; border-radius: var(--radius-sm); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.recovery-box p { margin: 0; }
.recovery-list { list-style: none; padding: 0; margin: 14px 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 6px 18px; font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 15px; }
/* Making a playlist: the same small dialog everywhere (core.js makePlaylistDialog). */
/* Every dialog (make a playlist, confirm, password, the Sync setup: .wide). */
.dialog { border: none; border-radius: var(--radius-float); padding: 24px; width: min(440px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow: auto; background: var(--solid); color: var(--text); box-shadow: var(--shadow); }
.dialog::backdrop { background: var(--backdrop); }
/* When a dialog scrolls (a phone), its buttons stay in sight at its bottom edge. */
.dialog form > .row:last-child { position: sticky; bottom: -24px; z-index: 1; margin: -12px 0 -24px; padding: 12px 0 24px; background: var(--solid); }
.dialog h3 { margin: 0; }
.confirm-text { margin: 0; white-space: pre-line; } /* the question, its paragraphs kept */

/* A tool's steps: a number and a name above each part (Choose, Review, Make); the setup folds to
   one line once a result shows (core.js foldSetup). */
h3.step-label { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; font: 700 14px/1.2 var(--heading-font); color: var(--muted); }
.step-num {
  flex: none; width: 26px; height: 26px; border-radius: 50%; display: inline-grid; place-items: center;
  background: var(--raised); color: var(--text); font: 700 13px/1 var(--heading-font);
}
.result .step-label .step-num, .step-current .step-num { background: var(--accent); color: var(--accent-text); }
.step-folded {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 16px; padding: 14px 24px; /* as the cards: the numbers line up */
  border-radius: var(--radius-sm); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge);
}
.step-folded[hidden] { display: none; }
.step-name { font: 700 14px/1.2 var(--heading-font); color: var(--muted); }
.step-summary { flex: 1 1 240px; min-width: 0; }
.step-folded button { flex: none; }
.create > .step-label { flex: 1 1 100%; margin: 0; } /* a row of its own above the fields */
.import-gig legend .step-label { margin: 0; }
.import-also { display: flex; align-items: center; gap: 10px; margin: 14px 0 0; cursor: pointer; }
.import-gig .create.import-playlist { padding-top: 12px; }
.import-gig .row.actions { margin-top: 16px; }


@media (max-width: 760px) {
  .menu { display: none !important; }
  .topbar {
    display: flex; align-items: center; gap: 8px; position: sticky; top: 0; z-index: 10;
    height: 60px; padding: 0 8px 0 16px; background: var(--header-bg);
    backdrop-filter: var(--glass); -webkit-backdrop-filter: var(--glass); border-bottom: 1px solid var(--border);
  }
  .topbar[hidden] { display: none; }
  .topbar-account { margin-left: auto; border-radius: 50%; }
  .topbar-account .avatar { width: 34px; height: 34px; font-size: 14px; }
  .account-menu.from-topbar { left: auto; right: 12px; top: 64px; bottom: auto; }
  .account-admin:not([hidden]) { display: flex !important; }
  /* The page's name is in the top bar; its heading stays for screen readers. */
  .page-head h2 {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .page-head .page-tabs { margin-top: 0; }
  /* The places along the bottom, clear of the phone's home bar. */
  .tabbar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
    padding: 0 4px env(safe-area-inset-bottom); background: var(--header-bg);
    backdrop-filter: var(--glass); -webkit-backdrop-filter: var(--glass); border-top: 1px solid var(--border);
  }
  .tabbar[hidden] { display: none; }
  .tool-cards, .home-section .tool-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } /* two side by side, on Home and Tools */
  /* Your work going on: a strip just above the tab bar; the message above that. */
  .activity-phone:not([hidden]) {
    display: grid; grid-template-columns: 1fr auto; position: fixed; left: 8px; right: 8px; bottom: calc(var(--tabbar) + 6px); z-index: 24;
    margin: 0; background: var(--solid); box-shadow: var(--shadow);
  }
  .activity-phone .activity-bar { grid-column: 1 / -1; grid-row: 2; }
  .toast { left: 16px; right: 16px; bottom: calc(var(--tabbar) + 12px); max-width: none; }
  body:has(#player:not([hidden])) .toast { bottom: calc(var(--tabbar) + 150px); }
  .tabbar a {
    flex: 1 1 0; min-width: 0; height: 58px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    color: var(--muted); font-size: 11px; font-weight: 650; text-decoration: none;
  }
  .tabbar a.active { color: var(--accent-ink); }
  .tabbar .menu-icon { width: 22px; height: 22px; }
  :root { --sticky-top: 60px; --tabbar: calc(58px + env(safe-area-inset-bottom)); }
  main { padding: 4px 16px calc(64px + var(--tabbar)); }
  .page-head { margin: 16px 0 16px; }
  h2 { font-size: 24px; }
  .card { padding: 18px; }
  .auth-card { padding: 24px; }
  .result { padding: 20px 18px; margin: 16px 0 0; } /* as the cards on a phone */
  .step-folded { padding: 12px 18px; }
  .listen { padding-left: 0; }
  .check-text { flex-basis: 160px; }
  .check-actions { flex: 1 1 100%; flex-wrap: wrap; }
  .check-actions .stepper { flex: 1 1 100%; } /* its own line: the two buttons stay a pair */
  .tempo-map { height: 200px; gap: 3px; }
  .tempo-range .level { height: calc(var(--size, 0) * 140px); }
  .tempo-range .count { font-size: 15px; }
  .tempo-range .axis { font-size: 11px; }
  .range-tracks .note { max-width: 40%; font-size: 12px; } /* the list's own note stays, smaller */
  /* A song's state on a line of its own, at the right: the song's name gets the row's width. */
  .import-list li { flex-wrap: wrap; row-gap: 4px; }
  .import-list .note { display: flex; flex-direction: column; align-items: flex-end; }
  .import-list .note:not(:empty) { flex-basis: 100%; max-width: none; }
  .import-list .note .chip { white-space: nowrap; }
  .import-list .note .chip { font-size: 12px; }
  /* Deduplicate: the song gets the row's width; whether it is kept goes under it, at the right
     (where it is in the playlist is left out, as in a narrow table). */
  .group { padding: 12px 10px; } /* its rows nearly as wide as the other lists' */
  .dup-list li { flex-wrap: wrap; row-gap: 4px; }
  .dup-list .track { flex: 1 1 calc(100% - 160px); }
  .dup-list .note { display: none; }
  .dup-list li > .chip { margin-left: auto; }
  .tempo-row-main { grid-template-columns: 32px minmax(0, 1fr) 104px auto; } /* ▶, song, tempo, Edit on one line; the tempo column the same in every row */
  .tempo-fix { grid-column: 2 / -1; grid-row: 2; }
  .tempo-energy, .tempo-fix:empty { display: none; }
  /* The song fills the first row (after ▶, the tempo and the artwork); where it goes and its
     buttons share the second, under the tempo. */
  .all-list .track { flex: 1 1 calc(100% - 216px); }
  .all-list .where { flex: 1 1 0; order: 3; display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; padding-left: 52px; text-align: left; }
  .all-list .note { display: block; text-align: left; }
  .all-list .where .chip { white-space: nowrap; }
  .row-tools { min-width: 0; order: 4; }
  .player {
    /* Floating above the page with room all around, clear of the phone's home bar */
    bottom: calc(10px + var(--tabbar)); width: calc(100% - 32px); margin-bottom: calc(10px + var(--tabbar));
    gap: 6px 10px; padding: 12px 14px; border-radius: 24px;
    grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "track controls close" "progress progress progress" "source source source" "facts facts facts";
  }
  /* The song facts on one line you swipe sideways, fading out at the edges (a mask: only its
     transparency counts, not its colour) */
  .player-facts {
    flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
    margin-inline: -14px; padding-inline: 14px 28px;
    -webkit-mask-image: linear-gradient(to right, transparent, black 14px, black calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, transparent, black 14px, black calc(100% - 28px), transparent);
  }
  .player-facts::-webkit-scrollbar { display: none; }
  .player-facts li { flex: none; white-space: nowrap; }
  .home-tempo-axis .phone-skip { visibility: hidden; } /* barChart's thin: every other label, never the tallest bar's */
}

/* ---------- Library ---------- */

/* The search, filter and count, then the column names, stay put while the list scrolls: pinned
   under the top bar (on a phone), across the card's full width, on the card's colour. */
.library-head {
  position: sticky; top: var(--sticky-top); z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  margin: 0; padding: 12px 0; background: var(--bg); /* pinned over the list: opaque, as the page */
}
.library-head input[type="search"] { flex: 1 1 160px; min-width: 0; }
.library-head select { flex: 0 0 auto; width: auto; }
.library-head select[hidden] { display: none; }
.library-head p { margin: 0; flex: 1 1 100%; }
#library-note:not([hidden]) + .library-head { margin-top: 0; }
.library-wrap { margin: 0; }
.library-table th {
  position: sticky; top: calc(var(--sticky-top) + var(--library-head, 0px)); z-index: 4;
  background: var(--bg); border-bottom: 0; box-shadow: inset 0 -1px var(--border);
}
.library-table th:first-child, .library-table td:first-child { padding-left: 4px; }
.library-table th:last-child, .library-table td:last-child { padding-right: 4px; }
.library-table th.num { text-align: right; }
/* A column the list can be sorted on: the name is a button, the arrow says which way. */
.sort-button {
  all: unset; cursor: pointer; border-radius: 4px; color: inherit; font: inherit;
}
.sort-button:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; } /* says it can be clicked */
.sort-button:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
th[aria-sort="ascending"] .sort-button, th[aria-sort="descending"] .sort-button { color: var(--text); }
th[aria-sort="ascending"] .sort-button::after { content: " ↑"; }
th[aria-sort="descending"] .sort-button::after { content: " ↓"; }
.library-table td { white-space: normal; }
.library-table td.nowrap { white-space: nowrap; }
/* With one service connected, the Service column says nothing: left out. */
.library-table.one-service .service-col, .library-table.no-created .created-col { display: none; }
/* What the columns left out on a phone say, under the name: only there. */
.library-name-text { display: grid; min-width: 0; }
.library-title-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
.library-title-line > a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.library-title-line .chip { flex: none; padding: 0 7px; font-size: 12px; font-variant-numeric: tabular-nums; } /* a song's tempo, as in the song rows */
.phone-sub { display: none; font-size: 13px; font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.library-sort-phone { display: none; }
.library-sort-phone .row-menu-list { left: auto; right: 0; }
.library-sort-phone summary::after { content: none; } /* the arrow beside the column says the way */
.library-sort-phone .row-menu-list button.active { color: var(--accent-ink); }
@media (max-width: 760px) { /* the card's padding is 18px there */
  .library-table .opt { display: none; }
  /* A two-line list: the name, and under it what the columns said; no column names, a sort button instead. */
  .library-table thead { display: none; }
  .phone-sub { display: block; }
  .library-sort-phone { display: inline-block; flex: none; order: 2; } /* on the count's line, at the right */
  .library-head p { flex: 1 1 60%; order: 1; } /* too wide for the first row: the second, beside the sort button */
  .library-head input[type="search"] { flex: 1 1 100px; }
  .library-table td:has(.library-name) { max-width: 0; width: 100%; } /* the name cell takes the room; its lines are shortened */
  /* A playlist, album or artist opens here: the whole row is its link, with a chevron to say so. */
  .library-table tr:has(a[href^="#library/"]) { position: relative; }
  .library-table .library-name a[href^="#library/"]::after { content: ""; position: absolute; inset: 0; }
  .library-name:has(a[href^="#library/"])::after {
    content: ""; flex: none; width: 16px; height: 16px; margin-left: auto; padding-left: 8px; background: var(--muted);
    -webkit-mask: url(icons/chevron-right.svg) right center / 16px no-repeat; mask: url(icons/chevron-right.svg) right center / 16px no-repeat;
  }
  .library-name-text { flex: 1 1 auto; }

}
.library-table td:first-child { width: auto; }
.library-table a { font-weight: 600; text-decoration: none; }
.library-table a:hover { text-decoration: underline; }
#library-more { margin-top: 12px; }
.library-error { color: var(--err-text); }
/* Service names in the text colour: Spotify allows its green only on black or white, and no other greens. */
.cover {
  flex: none; display: inline-block; width: 40px; height: 40px; overflow: hidden;
  border-radius: 6px; background: var(--raised); vertical-align: middle;
}
.cover.round { border-radius: 50%; }
.cover img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cover-large { width: 64px; height: 64px; border-radius: 10px; }
.library-name, .song-with-art { display: flex; align-items: center; gap: 12px; min-width: 0; }
.library-table td { padding-top: 6px; padding-bottom: 6px; }
.library-item-head { display: flex; align-items: center; gap: 16px; margin-bottom: 4px; }
.library-item-head p { margin: 0; }
.library-albums { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.library-albums li { display: flex; align-items: center; gap: 12px; }
.library-back { display: inline-flex; align-items: center; gap: 2px; margin-bottom: 8px; font-size: 14px; }
.library-back::before {
  content: ""; width: 14px; height: 14px; background: currentColor;
  -webkit-mask: url(icons/chevron-left.svg) center / contain no-repeat; mask: url(icons/chevron-left.svg) center / contain no-repeat;
}
.library-back[hidden] { display: none; }
@media (max-width: 760px) { .library-back { display: none; } } /* the top bar has the back link there */
#library-item h3 { margin: 0 0 4px; font-size: 20px; font-weight: 800; }
#library-item h4 { margin: 20px 0 8px; }

/* ---------- Admin: background tasks ---------- */

.tasks-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; }
.tasks-head p { margin: 0; flex: 1 1 320px; }
#tasks-admin h3 { margin: 22px 0 8px; } /* 20px, as every section (above) */
#status-admin > .status-summary + h3 { margin-top: 16px; }
.tasks-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.tasks-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; padding: var(--row-pad) 2px; border-bottom: 1px solid var(--border); }
.tasks-list .chip { flex: none; min-width: 72px; text-align: center; }
.task-what { display: grid; flex: 1 1 260px; min-width: 0; }
.task-title { font-weight: 600; overflow-wrap: anywhere; }
.task-error { flex-basis: 100%; color: var(--err-text); }
.chip.task-running { box-shadow: inset 0 0 0 1px var(--accent-ink); color: var(--accent-ink); }
.chip.task-done { box-shadow: inset 0 0 0 1px var(--ok); color: var(--ok); }
.chip.task-failed { box-shadow: inset 0 0 0 1px var(--err-text); color: var(--err-text); }

/* ---------- Admin: status ---------- */

.menu-count { margin-left: 8px; min-width: 20px; padding: 0 6px; border-radius: 999px; background: var(--err-bg); color: var(--err-text); font-size: 12px; font-weight: 700; line-height: 20px; text-align: center; font-variant-numeric: tabular-nums; }
.menu-count[hidden] { display: none; }
.menu-count.tasks-count { background: var(--accent-soft); color: var(--accent-ink); } /* work going on, not a problem */
#status-admin h3 { margin: 22px 0 8px; }
.status-summary { margin: 0; font-weight: 600; }
.status-summary.ok { color: var(--ok); }
.status-summary.problems { color: var(--muted); font-weight: 400; } /* the badge counts and the list shows: one muted line */
.problem-message { color: var(--err-text); overflow-wrap: anywhere; }

/* Settings › Services: the export's file field, a little apart from the text above it. */
.history-file-field { margin-top: 16px; max-width: 560px; }

/* ---------- About ---------- */

/* The services, one row each: the name, what it is for, what it is sent and its terms. */
.about-services { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.about-services > li { padding: var(--row-pad) 2px; border-bottom: 1px solid var(--border); }
.about-services h4 { margin: 0 0 4px; }
.about-services p { margin: 0 0 4px; }
.about-services p:last-child { margin-bottom: 0; }

/* A card heading's mark: the services' logos and the Gigs mark, one size. */
.home-card-head h3 .menu-icon { width: 24px; height: 24px; }
/* A service named as a heading on About: its mark before its name, as on Overview and Settings. */
.about-services h4 { display: flex; align-items: center; gap: 8px; }
.about-services h4 .service-icon { margin: 0; width: 22px; height: 22px; }
/* On a phone a row of four figures is 2 + 2 (three stay in one row). */
@media (max-width: 760px) { .home-figures:has(> :nth-child(4)) { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Admin › Tempos: the line to ask FreqBlog about the rest, under the search. */
.tempo-freqblog { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 10px 0 0; }
.tempo-freqblog button { margin-left: auto; } /* at the row's right end, as every row's action */

/* A sync's row: an automatic sync that was held, or failed, in its colour. */
.pair-warn { color: var(--warn-text); }
.pair-error { color: var(--err-text); }
/* The schedule dialog: the four choices under each other (the first is a sentence). */
.schedule-every { display: grid; gap: 0; }
/* Off: the when and the note keep their place, unseen, so the dialog doesn't jump under the pointer. */
#schedule-form.off .schedule-when, #schedule-form.off #schedule-note { visibility: hidden; }
/* What happens on removal: the three choices under each other, each with what it means. */
.removal-choices { display: grid; gap: 4px; }
.removal-choices legend { font-size: 14px; font-weight: 550; color: var(--muted); }
.removal-choices label { align-items: flex-start; min-height: 0; padding: 6px 0; font-weight: 400; color: var(--text); }
.removal-choices .choice-text > :first-child { font-weight: 600; }
.removal-choices input { margin-top: 3px; }
.removal-choices .choice-text { display: grid; gap: 2px; }
/* The review's buttons stacked on a phone: the primary first. */
@media (max-width: 760px) { #sync-apply { order: -1; } }
@media (max-width: 760px) { /* the open sync row: its ⋯ alone would take a line, so it sits on the route's */
  .pair.open { position: relative; }
  .pair.open > .pair-actions { position: absolute; right: 2px; top: var(--row-pad); }
  .pair.open .pair-route { padding-right: 56px; }
}

/* ---------- A live gig, for dancers (gig.html) ---------- */

/* Phone first: one column, the song playing now on a card, the songs before it as rows. */
.gig-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
main.gig { width: 100%; max-width: 560px; box-sizing: border-box; padding: 24px 16px 32px; flex: 1; }
.gig-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}
.gig-who { flex: 1; min-width: 0; } /* the Live badge and the help's “?” at the end */
.gig-dj { margin: 0; font-weight: 600; }
.live-badge {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--text); /* the accent ink on the soft tint is 4.2:1 on light; the dot keeps the accent */
  font-size: 13px;
  font-weight: 700;
}
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
/* The DJ's message to dancers, under the DJ's name. */
.gig-announcement {
  margin: 0 0 16px;
  padding: 12px 16px;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 650;
  font-size: 17px;
  overflow-wrap: anywhere;
}
.gig-now {
  display: grid;
  gap: 16px;
  margin-bottom: 8px;
}
.gig-now-label { margin: 0; font-size: 13px; font-weight: 600; color: var(--muted); }
.gig-now-song { display: flex; gap: 16px; align-items: center; }
.gig-now-cover { width: 96px; height: 96px; border-radius: var(--radius-sm); object-fit: cover; flex: none; }
.gig-now-text { min-width: 0; display: grid; gap: 6px; justify-items: start; }
.gig-now-text h1 { margin: 0; font-size: 28px; line-height: 1.05; overflow-wrap: anywhere; }
/* A long title smaller (gigview.titleLength), so the card stays a card. */
.gig-now-text h1[data-length="long"] { font-size: 22px; }
/* A song the DJ didn't share, playing now: said quietly, without a cover square. */
.gig-now.hidden-song h1 { color: var(--muted); font-size: 17px; font-weight: 600; font-family: inherit; }
.gig-now.hidden-song .gig-cover-none { display: none; }
.gig-now-text p { margin: 0; font-size: 16px; }
/* Side by side; on a narrow phone (360 px) stacked, never with the text broken over two lines. */
.gig-now-open { display: flex; flex-wrap: wrap; gap: 10px; }
.gig-now-open .button { flex: 1 1 150px; border-radius: 999px; white-space: nowrap; }
.gig-now-open:not(:has(.button:not([hidden]), .gig-heart)) { display: none; }
.gig-played h2, .gig-all h2 { margin: 24px 0 4px; font-size: 18px; font-weight: 700; }
.gig-songs { list-style: none; margin: 0; padding: 0; }
.gig-song {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: var(--row-pad) 0;
  border-bottom: 1px solid var(--border);
}
.gig-song-text { flex: 1; min-width: 0; }
.gig-song-text p { margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gig-song-title { font-weight: 600; }
.gig-song-artist, .gig-when { color: var(--muted); font-size: 14px; }
.gig-when { font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.gig-all .gig-when { width: 44px; flex: none; }
.gig-cover { width: 48px; height: 48px; border-radius: 8px; object-fit: cover; flex: none; }
.gig-cover-none { display: inline-block; background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.gig-open {
  width: 44px;
  height: 44px;
  flex: none;
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--field-border);
}
/* A heart on a song (while live): pressed, filled in the accent's ink colour, also while hovered (a
   ghost button's hover is the text colour, and a phone keeps the hover after a tap). */
button.gig-heart { width: 44px; height: 44px; padding: 0; flex: none; border-radius: 50%; color: var(--muted); }
button.gig-heart svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
button.gig-heart[aria-pressed="true"], button.gig-heart[aria-pressed="true"]:hover:not(:disabled) { color: var(--accent-ink); }
button.gig-heart[aria-pressed="true"] svg { fill: currentColor; }
.gig-now-heart { margin-left: auto; flex: none; }
.gig-now-heart:empty { display: none; }
.gig-now-heart .gig-heart { width: 52px; height: 52px; }
.gig-now-heart .gig-heart svg { width: 28px; height: 28px; }
.gig-open:hover { background: var(--raised); }
.gig-open:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
.gig-open svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
.gig-all-link { margin: 14px 0 0; font-weight: 600; }
.gig-missing h1 { margin: 0 0 8px; font-size: 22px; }
.gig-foot { padding: 16px; text-align: center; }
/* The way to the help for dancers: a round "?" at the end of the header. */
.gig-help-link {
  width: 36px;
  height: 36px;
  flex: none;
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--field-border);
  font-weight: 700;
  text-decoration: none;
}
.gig-help-link:hover { color: var(--text); background: var(--raised); }
/* Help for dancers (gighelp.html). */
.gig-help h1 { margin: 8px 0 8px; font-size: 28px; }
.gig-help h2 { margin: 24px 0 6px; font-size: 18px; }
.gig-help p { margin: 0 0 8px; line-height: 1.5; }
.gig-help-lead { font-size: 17px; }
.gig-help-back { font-weight: 600; }
.gig-help-heart { color: var(--accent-ink); }
.gig-help-end { margin-top: 28px; }
.gig-set { margin: 0 0 8px; }
.gig-set h1 { margin: 0 0 4px; font-size: 28px; line-height: 1.1; }

/* ---------- Live gigs in the app ---------- */

/* Go live: the date and start time side by side. */
.live-when { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
/* Settings › Services: the bridge's keys, one row each, Remove at the right. */
.bridge-keys { list-style: none; margin: 12px 0 0; padding: 0; }
.bridge-keys > li { padding: var(--row-pad) 2px; border-top: 1px solid var(--border); }
.bridge-key { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; }
.bridge-key-name { font-weight: 600; }
.bridge-key > button { margin-left: auto; }
/* The setlist's tempo through the night: a bar per song, its height (--size, set by gig.js) from
   its tempo; the fastest in the accent colour. */
.gig-tempo { margin: 16px 0 4px; }
.gig-tempo-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 56px;
  margin-top: 8px;
  border-bottom: 1px solid var(--field-border);
}
.gig-tempo-bar {
  flex: 1;
  min-width: 1px;
  height: calc(var(--size) * 100%);
  border-radius: 2px 2px 0 0;
  background: var(--bar);
}
.gig-tempo-bar.peak { background: var(--accent); }
/* A live gig's recap on Gigs, above its songs; the dancers through the night as the setlist's tempo. */
.gig-recap { margin: 4px 0 16px; padding: 14px 16px; border-radius: var(--radius); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.gig-recap h3 { margin: 0 0 6px; font-size: 16px; }
.gig-recap p { margin: 0 0 6px; }
.gig-recap-figure { width: min(100%, max(160px, calc(var(--bars) * 18px))); margin: 6px 0 12px; } /* as wide as its bars: the times under the first and last */
.gig-recap-bars { display: flex; align-items: flex-end; gap: 2px; height: 56px; border-bottom: 1px solid var(--field-border); }
.gig-recap-bar { flex: 1; min-width: 2px; height: max(2px, calc(var(--size) * 100%)); border-radius: 2px 2px 0 0; background: var(--bar); }
.gig-recap-bar.peak { background: var(--accent); }
.gig-recap-axis { display: flex; justify-content: space-between; margin-top: 4px; font-size: 12px; color: var(--muted); }
.gig-recap-loved { margin: 0 0 8px; padding-left: 0; list-style: none; display: grid; gap: 4px; }
.gig-tempo-axis { display: flex; justify-content: space-between; margin-top: 4px; font-size: 12px; color: var(--muted); }
/* Gigs › a live gig's songs: what each was found as, under its artist; a maybe with its check. */
.live-founds { display: flex; flex-wrap: wrap; gap: 2px 14px; font-size: 13px; }
.live-found { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
.live-found a { white-space: nowrap; } /* "On TIDAL" in one piece */
.live-maybe { color: var(--warn-text); }
.live-check { display: inline-flex; gap: 6px; }
.live-check button { white-space: nowrap; }
/* A maybe: a line of its own; its songs to choose from one per line, each with its That's it. */
.live-maybe { flex-basis: 100%; }
.live-option { display: flex; flex-basis: 100%; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
.live-option a { white-space: normal; }
.live-option button, .live-maybe > .live-check { flex: none; }
.live-songs li.live-hidden .song-title > span:first-child { color: var(--muted); }
/* The setlist's search, the song playing at the time searched, and the DJ's next gig. */
.gig-search { margin: 4px 0 8px; }
.gig-search input { width: 100%; box-sizing: border-box; }
.gig-song[aria-current="true"] { background: var(--accent-soft); border-radius: var(--radius-sm); margin: 0 -8px; padding-left: 8px; padding-right: 8px; }
/* Stays at the bottom of the screen while the set scrolls (as the sketch's bar). */
.gig-more-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px 16px;
  margin: 24px -16px 0;
  padding: 14px 16px;
  border-top: 1px solid var(--border);
  background: var(--header-bg);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  font-size: 14px;
  color: var(--muted);
}
.gig-more-bar a { font-weight: 600; }

/* ---------- The DJ's screen at a live gig (screen.html) ---------- */

/* A 15" laptop or an 11" iPad by the decks, read from a few metres: large type, the QR code on
   paper. Landscape: the songs on the left, the code on the right; portrait: the song on top, the
   code and the songs before it side by side. One screen, no scrolling. */
.screen-page { overflow: hidden; }
main.screen {
  max-width: none;
  height: 100vh;
  height: 100dvh;
  box-sizing: border-box;
  /* Clear of an iPad's status bar and rounded corners when opened from the home screen. */
  padding: max(clamp(20px, 3.2vmin, 48px), env(safe-area-inset-top)) max(clamp(20px, 3.2vmin, 48px), env(safe-area-inset-right))
    max(clamp(20px, 3.2vmin, 48px), env(safe-area-inset-bottom)) max(clamp(20px, 3.2vmin, 48px), env(safe-area-inset-left));
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto minmax(0, 1fr);
  grid-template-areas: "head scan" "now scan" "before scan";
  gap: clamp(16px, 3vmin, 44px) clamp(24px, 4vmin, 64px);
}
@media (orientation: portrait) {
  main.screen {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto minmax(0, 1fr);
    grid-template-areas: "head head" "now now" "scan before";
  }
}
.screen-head { grid-area: head; display: flex; align-items: center; flex-wrap: wrap; gap: 12px 28px; }
/* The DJ's message, across the top: in the accent colour, to be seen from the floor. */
.screen-announcement {
  flex-basis: 100%;
  margin: 0;
  padding: clamp(10px, 1.8vmin, 20px) clamp(16px, 2.6vmin, 30px);
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-text);
  font: 700 clamp(22px, 4.4vmin, 56px)/1.1 var(--heading-font);
  overflow-wrap: anywhere;
}
.screen-dj { width: clamp(40px, 6vmin, 64px); height: clamp(40px, 6vmin, 64px); font-size: clamp(16px, 2.6vmin, 26px); margin-left: -14px; }
.screen-who { margin-left: auto; display: grid; gap: 2px; text-align: right; }
.screen-dj-name { margin: 0; font-weight: 600; font-size: clamp(16px, 2.2vmin, 24px); }
.screen-set { margin: 0; color: var(--muted); font-size: clamp(15px, 2vmin, 22px); }
/* Full screen (or the iPad's hint) in the top right corner of the QR code's column. */
.screen-corner { grid-area: scan; align-self: start; justify-self: end; max-width: 100%; text-align: right; }
.screen-hint { margin: 0; color: var(--muted); font-size: clamp(13px, 1.8vmin, 16px); }
.wwts-logo {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.35em;
  font: 500 clamp(26px, 4.4vmin, 52px)/0.95 var(--heading-font);
  letter-spacing: -0.035em;
}
.wwts-mark { width: 2.1em; height: 2.1em; flex: none; }
/* The mark: a dark record (on dark with a fine light edge) with a coral label carrying the "?". */
.wwts-disc { fill: var(--record); }
.wwts-edge { fill: none; stroke: var(--record-edge); stroke-width: 1.4; }
.wwts-groove { fill: none; stroke: var(--record-groove); stroke-width: 1.6; }
.wwts-label { fill: var(--coral); }
.wwts-question { fill: none; stroke: var(--paper); stroke-width: 4.6; stroke-linecap: round; stroke-linejoin: round; }
.wwts-dot { fill: var(--paper); }
.wwts-wordmark { display: grid; }
.wwts-song { color: var(--coral); font-weight: 800; }
.screen-label { margin: 0 0 12px; color: var(--muted); font: 600 clamp(14px, 2vmin, 20px)/1.2 system-ui, -apple-system, "Segoe UI", sans-serif; letter-spacing: 0; }
.screen-now {
  grid-area: now;
  padding: clamp(18px, 3vmin, 36px);
  border-radius: var(--radius-float);
  background: var(--card);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  box-shadow: var(--shadow);
}
.screen-now-song { display: flex; align-items: center; gap: clamp(18px, 3vmin, 40px); }
.screen-now-cover { width: clamp(120px, 22vmin, 240px); height: clamp(120px, 22vmin, 240px); border-radius: var(--radius); object-fit: cover; flex: none; }
.screen-now-text { min-width: 0; display: grid; gap: 10px; justify-items: start; }
.screen-now-title {
  margin: 0;
  font: 800 clamp(34px, 7vmin, 92px)/1 var(--heading-font);
  letter-spacing: -0.03em;
  overflow-wrap: anywhere;
  /* At most three lines: the songs below keep their room. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}
/* A longer title is set smaller (gigview.titleLength). */
.screen-now-title[data-length="medium"] { font-size: clamp(28px, 5vmin, 66px); }
.screen-now-title[data-length="long"] { font-size: clamp(24px, 4vmin, 52px); line-height: 1.05; }
.screen-now-artist { margin: 0; font-size: clamp(20px, 3.4vmin, 40px); }
/* A song the DJ didn't share, playing now: said quietly, at the artist's size, no cover square. */
.screen-now.hidden-song .screen-now-title { color: var(--muted); font: 600 clamp(20px, 3.4vmin, 40px)/1.2 system-ui, -apple-system, "Segoe UI", sans-serif; letter-spacing: 0; }
.screen-now.hidden-song .gig-cover-none { display: none; }
.screen-waiting { grid-row: 2 / -1; grid-column: 1; align-self: center; margin: 0; color: var(--muted); font-size: clamp(20px, 3vmin, 32px); }
@media (orientation: portrait) {
  .screen-waiting { grid-area: now; }
}
.screen-before { grid-area: before; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); }
/* The five songs share the height that is left, each with a floor: when there isn't room, the
   last ones are left out whole rather than all five squashed (or one cut in half): a row that
   doesn't fit wraps to a second column, out of sight. */
.screen-recent { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; flex-wrap: wrap; overflow: hidden; max-height: calc(5 * 96px); }
.screen-song { flex: 1 0 clamp(48px, 7vmin, 72px); width: 100%; max-height: 96px; display: flex; align-items: center; gap: 16px; min-height: 0; overflow: hidden; border-bottom: 1px solid var(--border); }
.screen-song-cover { width: clamp(36px, 6vmin, 64px); height: clamp(36px, 6vmin, 64px); max-height: 80%; aspect-ratio: 1; border-radius: 10px; object-fit: cover; flex: none; }
.screen-song-text { flex: 1; min-width: 0; }
.screen-song-text p { margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.screen-song-title { font-weight: 650; font-size: clamp(17px, 2.5vmin, 26px); }
.screen-song-artist, .screen-song-when { color: var(--muted); font-size: clamp(14px, 2vmin, 20px); }
.screen-song-when { white-space: nowrap; font-variant-numeric: tabular-nums; }
.screen-scan { grid-area: scan; align-self: center; padding-top: 48px; display: grid; justify-items: center; gap: 12px; text-align: center; }
@media (orientation: portrait) {
  .screen-scan { align-self: start; }
}
.screen-scan-title { margin: 0; font-size: clamp(20px, 3.2vmin, 34px); }
.screen-qr {
  width: clamp(200px, 38vmin, 400px);
  height: auto;
  aspect-ratio: 1;
  padding: clamp(10px, 1.6vmin, 18px);
  border-radius: var(--radius);
  background: var(--paper);
  /* Dimmed with its white ground (the code's own and the tile's): bright white glares in a dark
     room. 70% white is still about 10:1 against the black squares, which phones scan easily. */
  filter: brightness(0.7);
}
.screen-address { margin: 0; font: 650 clamp(15px, 2.2vmin, 22px)/1.3 var(--heading-font); letter-spacing: -0.01em; overflow-wrap: anywhere; }
.screen-scan-note { margin: 0; color: var(--muted); font-size: clamp(13px, 1.8vmin, 18px); }
.screen-missing { grid-column: 1 / -1; grid-row: 2 / -1; align-self: center; text-align: center; font-size: clamp(20px, 3vmin, 30px); }
.screen-missing p { margin: 0 0 8px; }

/* ---------- The DJ view (djview.html) ---------- */

/* The screen's layout, signed in: the gig's name in the head, how each song was found under it,
   and the maybes to check where the QR code is (the whole right column, scrolling on its own). */
.djview-who { display: grid; gap: 2px; min-width: 0; }
.djview-name { margin: 0; font-size: clamp(22px, 3.6vmin, 40px); }
.djview-status { margin-left: auto; display: grid; gap: 2px; text-align: right; }
.djview-state { margin: 0; color: var(--muted); font-weight: 600; font-size: clamp(15px, 2vmin, 22px); }
.djview-audience { margin: 0; color: var(--muted); font-size: clamp(13px, 1.8vmin, 18px); }
/* The bridge needs a look: across the top, under the gig's name. */
.djview-warning {
  flex-basis: 100%;
  margin: 0;
  padding: 10px 16px;
  border-radius: var(--radius);
  background: var(--warn-bg);
  color: var(--warn-text);
  font-weight: 650;
  font-size: clamp(15px, 2vmin, 20px);
  overflow-wrap: anywhere;
}
.djview-hearts { color: var(--accent-ink); font-weight: 650; }
.djview-found { margin: 0; color: var(--muted); font-size: clamp(14px, 2vmin, 20px); }
.djview-maybe { color: var(--warn-text); }
.djview-check { grid-area: scan; grid-row: 1 / -1; width: clamp(280px, 32vw, 480px); min-height: 0; display: flex; flex-direction: column; gap: 10px; }
@media (orientation: portrait) {
  .djview-check { grid-row: auto; width: clamp(260px, 42vw, 420px); }
}
.djview-check-title { margin: 0; font-size: clamp(20px, 3.2vmin, 34px); }
.djview-check-note { margin: 0; color: var(--muted); font-size: clamp(13px, 1.8vmin, 17px); }
.djview-check .inline-error { margin: 0; }
.djview-checks { list-style: none; margin: 0; padding: 0; min-height: 0; overflow-y: auto; display: grid; gap: 10px; align-content: start; }
.djview-check-item { display: grid; gap: 6px; padding: 12px; border-radius: var(--radius); background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.djview-check-item p { margin: 0; overflow-wrap: anywhere; }
.djview-check-played { font-weight: 650; }
.djview-check-found { color: var(--muted); font-size: 14px; }
/* Each song found, to choose from: its cover, its name (a link to hear it), That's it. */
.djview-options { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.djview-option { display: flex; align-items: center; gap: 10px; font-size: 14px; }
.djview-option a { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.djview-option button { flex: none; }
.djview-check-cover { width: 44px; height: 44px; border-radius: 8px; object-fit: cover; flex: none; }
.djview-check-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* The DJ's message to dancers, above To check. */
.djview-message { display: grid; gap: 8px; padding-bottom: 14px; border-bottom: 1px solid var(--tile-edge); }
.djview-message-label { font-weight: 650; font-size: clamp(15px, 2vmin, 20px); }
.djview-message-note { margin: 0; color: var(--muted); font-size: 14px; overflow-wrap: anywhere; }
/* The DJ's own search, in place of Search and None of these: the text, then Cancel and Search. */
.djview-search { display: grid; gap: 8px; }
.djview-search-text { width: 100%; }
/* The dancers' page: where every song's open button goes, as two buttons side by side. */
.gig-listen {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  margin: 16px 0 0;
  padding: 0;
  border: 0;
}
.gig-listen legend { float: left; margin-right: 4px; padding: 0; color: var(--muted); font-size: 14px; }
.gig-listen label {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 14px;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--field-border);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.gig-listen input { position: absolute; opacity: 0; pointer-events: none; }
.gig-listen label:has(input:checked) { background: var(--tonal); color: var(--tonal-text); box-shadow: none; } /* chosen, not the primary's accent */
.gig-listen label:has(input:focus-visible) { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* ---------- whatwasthatsong.app's home page (wwts.html) ---------- */

.wwts-home {
  min-height: 100vh;
  display: grid;
  place-items: center;
}
main.wwts-home-main { max-width: 560px; padding: 48px 24px; display: grid; gap: 24px; }
.wwts-home-main .wwts-logo { font-size: clamp(34px, 9vw, 56px); }
.wwts-home-lead { margin: 0; font-size: 19px; line-height: 1.5; }
.wwts-home-steps { margin: 0; padding-left: 22px; display: grid; gap: 10px; color: var(--muted); }
