/* MusicPerfect - hand-written CSS, no framework, built on the TOKENS below (appi/docs/UI_GUIDE.md section 7): no raw colors, font sizes or ad-hoc spacing outside this block.
   An app overrides ONLY the accent (--p-* in the palette rules); the neutrals, type scale, radii, spacing and motion are shared.
   One layout for every form factor: a bottom tab bar under 860px, a side rail from 860px; modals are full-screen sheets under 640px.
   UI_VERSION (ui.c816d75abb.js) says which template version this file came from; the guide's changelog says what changed. */
:root {
  /* the accent: a light and a dark value, chosen by the palette (data-palette); MusicPerfect's default is 'violet' (light #5b21b6 on white 9:1, dark #c4b5fd on the dark page 10:1) */
  --p-brand: #5b21b6; --p-brand-fg: #ffffff; --p-soft: #ede9fe; --p-brand-d: #c4b5fd; --p-brand-fg-d: #1e1038; --p-soft-d: #2a1d4f;
  /* neutrals, light */
  --brand: var(--p-brand); --brand-fg: var(--p-brand-fg); --brand-soft: var(--p-soft);
  --bg: #f5f7f7; --panel: #ffffff; --fg: #14191b; --muted: #596366; --line: #dce2e4;
  --good: #1a7f45; --bad: #c0392b; --warn: #8a5500; --warn-bg: #fff1cc;
  --shadow: 0 1px 2px rgb(20 25 27 / .06), 0 4px 14px rgb(20 25 27 / .05); --shadow-pop: 0 12px 40px rgb(0 0 0 / .3);
  --scrim: rgb(0 0 0 / .45); --media-bg: #000000; --media-fg: #ffffff;
  /* type: three sizes */
  --font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --text-sm: .85rem; --text-md: 1rem; --text-lg: 1.4rem;
  /* spacing (4px scale), radii (12 / 16, and 6 for tiny parts), tap target */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px; --space-6: 32px;
  --radius: 12px; --radius-lg: 16px; --radius-xs: 6px; --radius-pill: 999px; --gap: var(--space-4); --tap: 44px;
  /* motion: three durations and three easings (animate transform and opacity only) */
  --dur-1: 120ms; --dur-2: 200ms; --dur-3: 320ms; --ease: cubic-bezier(.2, .7, .2, 1); --ease-spring: cubic-bezier(.34, 1.4, .64, 1); --ease-exit: cubic-bezier(.4, 0, 1, 1);
  --dur-loop: 1400ms; --dur-breathe: 2400ms; --boot-slow-after: 10s;          /* the startup screen's loops, and when it explains a slow start */
  /* RHYTHM (UI guide, "Rhythm and type"; the sign-in card is the baseline): bands in a surface 16, titled sections 24, items in a group 8, a title and its subtitle 4; cards pad 24, inset blocks 16 */
  --stack: var(--space-4); --stack-section: var(--space-5); --stack-tight: var(--space-2); --stack-snug: var(--space-1); --pad-card: var(--space-5); --pad-inset: var(--space-4);
  --control: 52px; --avatar: 32px; --measure: 860px;
  --shade-1: rgb(0 0 0 / .38); --shade-2: rgb(0 0 0 / .55); --shade-3: rgb(0 0 0 / .62); --shade-4: rgb(0 0 0 / .75); --veil: rgb(255 255 255 / .45);
  --nav-h: 64px; --top-h: 52px;
  --scroll-thumb: color-mix(in srgb, var(--muted) 45%, transparent); --scroll-thumb-hover: color-mix(in srgb, var(--muted) 75%, transparent);
  --z-nav: 5; --z-top: 6; --z-call: 30; --z-modal: 40; --z-confirm: 50; --z-toast: 60;
  color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-mode=light]) {
    --brand: var(--p-brand-d); --brand-fg: var(--p-brand-fg-d); --brand-soft: var(--p-soft-d);
    --bg: #0f1213; --panel: #171b1d; --fg: #e9eef0; --muted: #94a0a4; --line: #272e31;
    --good: #5ee59a; --bad: #ff8a7a; --warn: #f0c24f; --warn-bg: #3a3112; --shadow: none;
  }
}
:root[data-mode=light] { color-scheme: light; }
:root[data-mode=dark] {
  color-scheme: dark;
  --brand: var(--p-brand-d); --brand-fg: var(--p-brand-fg-d); --brand-soft: var(--p-soft-d);
  --bg: #0f1213; --panel: #171b1d; --fg: #e9eef0; --muted: #94a0a4; --line: #272e31;
  --good: #5ee59a; --bad: #ff8a7a; --warn: #f0c24f; --warn-bg: #3a3112; --shadow: none;
}

/* ---- UI STYLES (user, 2026-11-01: three looks to switch between while the style is decided; theme.js STYLES, data-style on <html>). Soft is the tokens above; the others change shape tokens only ---- */
:root[data-style=crisp] { --radius: 6px; --radius-lg: 8px; --pad-card: var(--space-4); --shadow: none; }
:root[data-style=bold] { --radius: 16px; --radius-lg: 24px; --pad-card: var(--space-6); }
:root[data-style=bold] .card { border-color: transparent; background: color-mix(in srgb, var(--brand-soft) 45%, var(--panel)); }
:root[data-style=bold] .btn, :root[data-style=bold] .chip-choice { border-radius: var(--radius-pill); }
:root[data-style=bold] h2 { font-weight: 800; }
:root[data-style=bold] h1 { font-weight: 800; }

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; background: var(--bg); color: var(--fg); font: var(--text-md)/1.5 var(--font); }
h1, h2 { line-height: 1.15; margin: 0; letter-spacing: -.01em; }
h1 { font-size: var(--text-lg); font-weight: 700; } h2 { font-size: var(--text-md); font-weight: 700; }      /* TITLE = lg 700 (the app and the page); a section heading = md 700 */
p { margin: 0; } small, .muted { color: var(--muted); }
/* META = sm, muted: the line under a title, hints, helper text, captions, footers (one role, one size, wherever it appears) */
.lead, .hint, p.muted, small, .field-hint, .gate-foot, .empty p, .row-sub { font-size: var(--text-sm); }
.lead { color: var(--muted); margin-top: var(--stack-snug); }
button, input, select, textarea { font: inherit; color: inherit; }
:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; scroll-behavior: auto !important; } }

/* THE STARTUP SCREEN (index.html's .boot, UI guide 10 "Starting"; user, 2026-11-06: "a loading animation or spinner, something cool, in the colors of the app with the app name"):
   what shows from the first paint until the app mounts (the app replaces it). Plain HTML and CSS - it runs before any script (the CSP allows no inline script) - in the app's own
   tokens, so it takes the app's colors, light or dark. The app's icon breathes inside a ring that turns in the brand color; under it the app's name and "Starting…"; a slow start
   (the first visit downloads the app) says why after a while. Reduced motion: still, with the explanation shown at once. */
.boot { position: fixed; inset: 0; display: grid; place-content: center; justify-items: center; gap: var(--space-2); padding: var(--space-5); background: var(--bg); color: var(--fg); text-align: center; }
.boot-mark { position: relative; display: grid; place-items: center; width: 112px; height: 112px; margin-bottom: var(--space-3); }
.boot-mark::before { content: ''; position: absolute; inset: 16px; border-radius: 50%; background: var(--brand-soft); animation: boot-breathe var(--dur-breathe) var(--ease) infinite; }
.boot-ring { position: absolute; inset: 0; border-radius: 50%; background: conic-gradient(from 0deg, transparent 0 30%, color-mix(in srgb, var(--brand) 25%, transparent) 55%, var(--brand) 100%);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 6px), black calc(100% - 5px)); mask: radial-gradient(farthest-side, transparent calc(100% - 6px), black calc(100% - 5px));
  animation: boot-spin var(--dur-loop) linear infinite; }
.boot-icon { position: relative; width: 64px; height: 64px; border-radius: var(--radius-lg); box-shadow: var(--shadow); animation: boot-breathe var(--dur-breathe) var(--ease) infinite; }
.boot-name { margin: 0; font-size: var(--text-lg); font-weight: 700; letter-spacing: .02em; animation: boot-in var(--dur-3) var(--ease) both; }
.boot-note { margin: 0; color: var(--muted); font-size: var(--text-sm); animation: boot-in var(--dur-3) var(--ease) var(--dur-2) both; }
.boot-slow { max-width: 34ch; margin: var(--space-3) 0 0; color: var(--muted); font-size: var(--text-sm); opacity: 0; animation: boot-in var(--dur-3) var(--ease) var(--boot-slow-after) both; }
@keyframes boot-spin { to { transform: rotate(1turn); } }
@keyframes boot-breathe { 0%, 100% { transform: scale(.94); opacity: .8; } 50% { transform: scale(1); opacity: 1; } }
@keyframes boot-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .boot-slow { opacity: 1; } }
/* the first visit's DOWNLOAD PROGRESS (user, 2026-11-07): boot.js (written by build_pwa.mjs) adds a bar and "x of y MB" under "Starting…" while the worker saves the app; none when it is saved already */
.boot-progress { appearance: none; width: min(240px, 70vw); height: 6px; margin-top: var(--space-2); border: 0; border-radius: var(--radius-pill); background: var(--brand-soft); color: var(--brand); overflow: hidden; animation: boot-in var(--dur-3) var(--ease) both; }
.boot-progress::-webkit-progress-bar { background: var(--brand-soft); border-radius: var(--radius-pill); }
.boot-progress::-webkit-progress-value { background: var(--brand); border-radius: var(--radius-pill); transition: width var(--dur-2) var(--ease); }
.boot-progress::-moz-progress-bar { background: var(--brand); border-radius: var(--radius-pill); }
.boot-amount { margin: 0; color: var(--muted); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }

/* ---- controls ---- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); min-height: var(--tap); padding: 0 var(--space-4); border-radius: var(--radius); border: 1px solid var(--line); background: var(--panel); cursor: pointer; }
.btn.primary { background: var(--brand); border-color: var(--brand); color: var(--brand-fg); font-weight: 600; }
.btn.ghost { background: transparent; border-color: transparent; }
.btn.danger { color: var(--bad); } .btn.wide { width: 100%; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.icon-btn { padding: 0 var(--space-3); } .icon { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.link { background: none; border: 0; color: var(--brand); cursor: pointer; padding: var(--space-2) 0; min-height: var(--tap); }
input, select, textarea { font-size: var(--text-md); width: 100%; min-height: var(--tap); padding: var(--space-2) var(--space-3); border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); }
select { appearance: none; -webkit-appearance: none; padding-right: calc(var(--space-4) + var(--space-3) * 2); background-repeat: no-repeat; background-position: right var(--space-3) center; background-size: var(--space-4); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a96a3' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); }      /* one chevron with room on its right, for every select (the browser's own sits against the edge) */
textarea { resize: vertical; }
.field { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-sm); color: var(--muted); min-width: 0; } .field.inline { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--space-3); } .field.inline input { max-width: 160px; }
.row { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; margin-top: var(--space-3); }
.chip { display: inline-block; padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill); background: var(--brand-soft); color: var(--brand); font-size: var(--text-sm); }

/* ---- shell: bottom bar on phones, side rail on wide screens ---- */
.app { min-height: 100dvh; display: flex; flex-direction: column; }
.nav { position: fixed; inset: auto 0 0 0; z-index: 5; display: flex; background: var(--panel); border-top: 1px solid var(--line); padding-bottom: env(safe-area-inset-bottom); }
.nav .brand, .nav .nav-foot { display: none; }
.tab { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-1); min-height: var(--nav-h); background: none; border: 0; color: var(--muted); font-size: var(--text-sm); cursor: pointer; }
.tab.current { color: var(--brand); font-weight: 600; }
.tab.unavailable, .btn.unavailable { opacity: .45; cursor: default; }          /* a destination this session cannot use (a visitor): shown, never removed */
.content { flex: 1; width: 100%; max-width: 1080px; margin: 0 auto; padding: var(--gap) var(--gap) calc(var(--nav-h) + var(--gap) + env(safe-area-inset-bottom)); }
.update { position: sticky; top: 0; z-index: 6; display: flex; gap: var(--space-3); align-items: center; justify-content: center; padding: var(--space-2); background: var(--brand); color: var(--brand-fg); flex-wrap: wrap; }
.update .btn { min-height: 34px; background: var(--brand-fg); color: var(--brand); }
@media (min-width: 860px) {
  .app { flex-direction: row; flex-wrap: wrap; }
  .update { flex: 1 0 100%; position: static; }
  .nav { position: sticky; top: 0; inset: auto; height: 100dvh; flex: none; flex-direction: column; gap: var(--space-1); padding: var(--space-3); border-top: 0; border-right: 1px solid var(--line); }
  .nav .brand { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); white-space: nowrap; } .nav .nav-foot { display: flex; align-items: center; gap: var(--space-2); margin-top: auto; padding: var(--space-2); }
  .tab { flex: none; flex-direction: row; justify-content: flex-start; gap: var(--space-3); min-height: var(--tap); padding: 0 var(--space-3); border-radius: var(--radius); font-size: var(--text-sm); }
  .tab.current { background: var(--brand-soft); }
  .content { flex: 1; min-width: 0; padding: var(--space-5) var(--space-6) var(--space-6); }
}
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); } .dot.on { background: var(--good); } .dot.off { background: var(--bad); }
.logo { display: grid; place-items: center; width: 56px; height: 56px; border-radius: var(--radius-lg); background: var(--brand); color: var(--brand-fg); font-weight: 800; font-size: var(--text-lg); } .logo.small { width: 34px; height: 34px; border-radius: var(--radius); font-size: var(--text-sm); }

/* ---- onboarding ---- */
.gate { min-height: 100dvh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-4); padding: var(--gap); }
.gate-card { width: min(420px, 100%); background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: var(--pad-card); display: flex; flex-direction: column; gap: var(--stack); box-shadow: var(--shadow); }
.gate-card .lead { margin: 0; } .hint { font-size: var(--text-sm); color: var(--muted); }
.banner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); } .banner .btn.icon-only { border-color: transparent; background: transparent; } .banner, .banner-inline { padding: var(--space-3) var(--space-4); border-radius: var(--radius); background: var(--brand-soft); margin-bottom: var(--gap); } .banner.bad, .banner-inline.bad { background: var(--warn-bg); color: var(--bad); }

/* ---- views ---- */
.view { display: flex; flex-direction: column; gap: var(--stack-section); }
.view-head.row-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); flex-wrap: wrap; }
.view-head.row-head > .lead { flex: 1 1 12rem; min-width: 0; margin: 0; }          /* the lead line shares the row with the add buttons (the page name is in the top bar) */
.section-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-2); }
section { display: flex; flex-direction: column; gap: var(--stack-tight); }
.card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4); box-shadow: var(--shadow); display: flex; flex-direction: column; gap: var(--space-3); text-align: left; }
.empty { padding: var(--space-5); border: 1px dashed var(--line); border-radius: var(--radius); color: var(--muted); display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); } .empty.tall { min-height: 40vh; justify-content: center; align-items: center; text-align: center; }
.quick { display: flex; gap: var(--space-2); }
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-3); display: flex; flex-direction: column; } .stat strong { font-size: var(--text-lg); line-height: 1.1; } .stat span { color: var(--muted); font-size: var(--text-sm); } .stat.good strong { color: var(--good); } .stat.bad strong { color: var(--bad); }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); gap: var(--space-3); }

.bar { height: 8px; border-radius: var(--radius-pill); background: var(--line); overflow: hidden; } .bar.big { height: 12px; } .bar span { display: block; height: 100%; background: var(--brand); border-radius: inherit; }
.list { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); overflow: hidden; }
.row-item { display: flex; flex-direction: row; gap: var(--space-3); align-items: center; text-align: left; padding: var(--space-3) var(--space-4); background: none; border: 0; border-bottom: 1px solid var(--line); cursor: pointer; width: 100%; min-height: var(--tap); }
.row-item:last-child { border-bottom: 0; } .row-item.active { background: var(--brand-soft); } .row-item .muted { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-item.flat { flex-direction: row; align-items: center; justify-content: space-between; cursor: default; } .row-item.flat .row-main { flex: 1; } .row-item.flat .row-main .muted { flex: none; text-align: left; padding: 0; } .row-item.flat .muted { flex: 1; text-align: right; padding: 0 var(--space-2); }
.row-meta { display: flex; gap: var(--space-2); align-items: center; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-3); } .form-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: var(--space-3); align-items: end; }
@media (max-width: 520px) { .form-row { grid-template-columns: 1fr 1fr; } .form-row .btn { grid-column: 1 / -1; } }
.facts { margin: 0; padding-left: var(--space-4); color: var(--muted); display: flex; flex-direction: column; gap: var(--space-1); }

/* ONE button language: actions are icon-only (tooltip + accessible name), the same size and shape everywhere */
.btn.icon-only { width: var(--tap); min-width: var(--tap); padding: 0; gap: 0; flex: none; }
.btn.icon-only .icon { width: 22px; height: 22px; }
.btn.icon-only.primary { background: var(--brand); border-color: var(--brand); color: var(--brand-fg); }
.btn.icon-only.danger { color: var(--bad); }
.btn.icon-only.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--bad) 10%, transparent); }
.btn.icon-only.primary.danger { background: var(--bad); border-color: var(--bad); color: var(--media-fg); }
.btn.icon-only:disabled { opacity: .45; cursor: default; }
.btn.icon-only.wide { width: 100%; }
.btn.icon-only:focus-visible, .kind:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.vc-pick { position: relative; cursor: pointer; }
.vc-pick .file { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; }

.gate-others { display: grid; gap: var(--space-2); }
/* a collapsible section (ui.js disclosure): the chevron turns when it opens */
.disclosure-head { display: flex; align-items: center; gap: var(--space-2); width: 100%; min-height: var(--tap); background: none; border: 0; padding: var(--space-1) var(--space-1); color: var(--muted); font: inherit; text-align: left; cursor: pointer; border-radius: var(--radius); }
.disclosure-head:hover { color: var(--fg); }
.disclosure-head .icon { transition: transform var(--dur-1) var(--ease); }
.disclosure.open > .disclosure-head .icon { transform: rotate(90deg); }
.disclosure-body { display: grid; gap: var(--space-2); }
/* an info tip (ui.js infoTip): the field and its info icon on one row, what it does in a soft panel under it while open */
.info-tip-row { display: flex; align-items: center; gap: var(--space-2); }
.info-tip-row > .field { flex: 1; }
.info-tip-btn { color: var(--muted); }
.info-tip-btn.on { color: var(--brand); }
.info-tip-body { display: grid; gap: var(--space-2); margin-top: var(--space-1); padding: var(--pad-inset); border-radius: var(--radius); background: var(--brand-soft); color: var(--fg); font-size: var(--text-sm); }
.info-tip-body p { margin: 0; }
/* a row that is one button (optionRow): the text and its icon, hover and press feedback over the whole row */
.option-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); width: 100%; min-height: calc(var(--tap) + 2 * var(--space-1)); padding: var(--space-1) var(--space-1) var(--space-1) var(--space-3); border: 0; border-radius: var(--radius); background: transparent; color: var(--muted); font: inherit; text-align: left; cursor: pointer; transition: background-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.option-row:hover { background: color-mix(in srgb, var(--brand-soft) 55%, transparent); color: var(--fg); }
.option-row:active { background: var(--brand-soft); color: var(--fg); transform: scale(.98); }
.option-row-icon { display: inline-flex; align-items: center; justify-content: center; flex: none; width: var(--tap); height: var(--tap); border-radius: var(--radius-xs); color: var(--fg); }
.option-row:hover .option-row-icon, .option-row:active .option-row-icon { color: var(--brand); }
.option-row-icon .icon { width: 22px; height: 22px; }
.option-row.danger .option-row-icon { color: var(--bad); }

.section-head .btn.icon-only { min-height: 36px; width: 36px; min-width: 36px; }

/* the "are you sure" dialog */
.confirm-backdrop { position: fixed; inset: 0; z-index: var(--z-confirm); display: grid; place-items: center; padding: var(--space-4); background: var(--scrim); animation: fade var(--dur-2) var(--ease); }
.confirm { width: min(360px, 100%); display: grid; gap: var(--space-3); padding: var(--space-5); border-radius: var(--radius-lg); background: var(--panel); color: inherit; border: 1px solid var(--line); box-shadow: var(--shadow-pop); animation: pop var(--dur-3) var(--ease-spring); }
.confirm-message { margin: 0; font-weight: 600; }
.confirm-detail { margin: 0; }
.confirm-actions { display: flex; justify-content: flex-end; gap: var(--space-3); } .confirm-actions .btn { min-width: 96px; } .btn.primary.danger { background: var(--bad); border-color: var(--bad); color: var(--bg); }

/* profiles: the account bar in the rail and the settings card */
.nav-account { padding: var(--space-2) 0 0; border-top: 1px solid var(--line); }      /* no bottom padding: the profile row's own padding matches the title row's, so the gap below it equals the gap above the title */
.profile-card { display: grid; gap: var(--space-3); }
.gate-card .profiles { margin: var(--space-1) 0; }
@media (max-width: 859px) { .nav-account { display: none; } }
@media (min-width: 860px) { .nav .nav-account { margin-top: auto; } .nav .nav-account + .nav-foot { margin-top: 0; } .nav-account .account-bar { flex-wrap: wrap; } }

.theme-panel { margin-bottom: var(--gap); }
.palettes { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.palette { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--tap); padding: 0 var(--space-4) 0 var(--space-3); border-radius: var(--radius-pill); border: 1px solid var(--line); background: var(--panel); cursor: pointer; }
.palette.on { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand); } .palette .swatch { width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--muted); }

/* ---- the sign-in card: brand on one line, the password and its button side by side ---- */
.gate-card { position: relative; }
.gate-brand { display: flex; align-items: center; gap: var(--space-4); padding-right: calc(var(--tap) + var(--space-2)); }
.gate-brand .mode-toggle { position: absolute; top: 14px; right: 14px; }
.gate-row { display: flex; align-items: flex-end; gap: var(--space-3); } .gate-row .field { flex: 1; }

/* ============================================================================================================================================================================
   THE UI LAYER (ui.c816d75abb.js): shell, modal, toast, states, fields, rows, motion. Everything uses the tokens; animate transform and opacity only.
   ============================================================================================================================================================================ */
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes pop { from { opacity: 0; transform: translateY(12px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes sheet { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes flash { 0%, 40% { background-color: var(--brand-soft); } 100% { background-color: transparent; } }
@keyframes toast-in { from { opacity: 0; transform: translateY(16px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes row-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes shimmer { from { opacity: .45; } to { opacity: .9; } }
@keyframes page-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--dur-2); animation-timing-function: var(--ease); }
@media (prefers-reduced-motion: reduce) { ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; } }
.spin { animation: spin 1s linear infinite; }
.btn { transition: background-color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.btn:active:not(:disabled) { transform: scale(.95); }
.view { animation: page-in var(--dur-3) var(--ease) both; }

/* ---- the shell: a header (title, search, sync status, settings) above the page; the content leaves room for it and for the bottom bar ---- */
.content { --pad-x: var(--gap); --pad-y: var(--gap); padding: var(--pad-y) var(--pad-x) calc(var(--nav-h) + var(--gap) + env(safe-area-inset-bottom)); }
@media (min-width: 860px) { .content { --pad-x: var(--space-6); --pad-y: 28px; padding: var(--pad-y) var(--pad-x) var(--space-6); } }
.topbar { position: sticky; top: 0; z-index: var(--z-top); display: flex; align-items: center; gap: var(--space-1); min-height: var(--top-h); padding: var(--space-1) var(--pad-x); margin: calc(-1 * var(--pad-y)) calc(-1 * var(--pad-x)) var(--pad-y); background: color-mix(in srgb, var(--bg) 90%, transparent); backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); }
.topbar .spacer, .pane-head .spacer, .modal-foot .spacer { flex: 1; }
.topbar-title { font-size: var(--text-md); }
.topbar-logo { margin-right: var(--space-2); }
@media (min-width: 860px) { .topbar-logo { display: none; } }
.topbar .btn.icon-only { border-color: transparent; background: transparent; }
.status-chip.synced { color: var(--good); } .status-chip.attention { color: var(--warn); } .status-chip.maintenance { color: var(--warn); } .status-chip.offline, .status-chip.local { color: var(--muted); }
.tab { transition: color var(--dur-1) var(--ease); }
.tab .icon { transition: transform var(--dur-2) var(--ease-spring); } .tab.current .icon { transform: translateY(-1px) scale(1.08); }

/* ---- the modal: three sizes, a full-screen sheet on phones, a sticky footer ---- */
html.modal-open { overflow: hidden; }
.modal-backdrop { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: center; padding: var(--space-4); background: var(--scrim); animation: fade var(--dur-2) var(--ease); }
.modal { width: min(520px, 100%); max-height: min(92dvh, 100%); display: flex; flex-direction: column; border-radius: var(--radius-lg); background: var(--panel); color: inherit; border: 1px solid var(--line); box-shadow: var(--shadow-pop); overflow: hidden; animation: pop var(--dur-3) var(--ease-spring); }
.modal.wide { width: min(980px, 100%); }
.modal.screen { width: 100%; height: 100%; max-height: none; border-radius: 0; border: 0; }
.modal-backdrop:has(.modal.screen) { padding: 0; }
.modal-form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.modal-head { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4); border-bottom: 1px solid var(--line); min-height: 56px; }
.modal-head h2 { flex: 1; margin: 0; font-size: var(--text-md); }
.modal-head .btn.icon-only { border-color: transparent; background: transparent; }
.modal-body { display: flex; flex-direction: column; gap: var(--stack); padding: var(--space-4); overflow-y: auto; flex: 1; min-height: 0; }
.modal.screen .modal-body > * { width: min(860px, 100%); margin-inline: auto; }
.modal.screen .modal-head, .modal.screen .modal-foot { padding-inline: max(var(--space-4), calc((100% - var(--measure)) / 2)); }
.modal.screen .rich-editor-wrap { display: flex; flex-direction: column; flex: 1; } .modal.screen .rich-toolbar { order: 2; position: sticky; bottom: 0; background: var(--panel); padding: var(--space-2) 0; border-top: 1px solid var(--line); }
.modal-foot { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom)); border-top: 1px solid var(--line); background: var(--panel); }
@media (max-width: 640px) {
  .modal-backdrop { padding: 0; place-items: stretch; }
  .modal, .modal.wide, .modal.screen { width: 100%; height: 100%; max-height: none; border-radius: 0; border: 0; animation: sheet var(--dur-3) var(--ease); }
}

/* ---- the toast and the row highlight ---- */
.toast-region { position: fixed; left: 0; right: 0; bottom: calc(var(--nav-h) + var(--space-4) + env(safe-area-inset-bottom)); z-index: var(--z-toast); display: flex; justify-content: center; padding: 0 var(--space-4); pointer-events: none; }
@media (min-width: 860px) { .toast-region { bottom: var(--space-5); } }
.toast { display: flex; align-items: center; gap: var(--space-2); max-width: 480px; padding: var(--space-3) var(--space-4); border-radius: var(--radius); background: var(--fg); color: var(--bg); box-shadow: var(--shadow-pop); animation: toast-in var(--dur-3) var(--ease-spring); }
.toast .icon { color: var(--good); }
.flash { animation: flash 1.8s var(--ease); }

/* ---- the three states ---- */
.empty { align-items: center; text-align: center; padding: var(--space-5); gap: var(--space-3); } .empty .icon { width: 32px; height: 32px; opacity: .7; }
.skeleton { display: flex; flex-direction: column; gap: var(--space-2); }
.skeleton-row { height: 56px; border-radius: var(--radius); background: var(--line); animation: shimmer .9s ease-in-out infinite alternate; }
.error-notice { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-radius: var(--radius); background: var(--warn-bg); color: var(--warn); } .error-notice p { flex: 1; }

/* ---- form fields: the label above, helper text below, errors in red under the field ---- */
.field-label { font-size: var(--text-sm); color: var(--muted); }
.field-hint { font-size: var(--text-sm); } .field-error { font-size: var(--text-sm); color: var(--bad); }
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: var(--bad); }
.field.toggle { flex-direction: row; align-items: center; gap: var(--space-3); min-height: var(--tap); color: var(--fg); font-size: var(--text-md); }
input[type=checkbox][role=switch] { appearance: none; width: 46px; min-width: 46px; height: 28px; min-height: 28px; padding: 0; border-radius: var(--radius-pill); border: 1px solid var(--line); background: var(--line); position: relative; cursor: pointer; transition: background-color var(--dur-2) var(--ease); }
input[type=checkbox][role=switch]::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: var(--panel); box-shadow: var(--shadow); transition: transform var(--dur-2) var(--ease-spring); }
input[type=checkbox][role=switch]:checked { background: var(--brand); border-color: var(--brand); }
input[type=checkbox][role=switch]:checked::after { transform: translateX(18px); }
.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip-choice { min-height: var(--tap); padding: 0 var(--space-4); border-radius: var(--radius-pill); border: 1px solid var(--line); background: var(--panel); cursor: pointer; transition: background-color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease); }
.chip-choice.on { background: var(--brand-soft); border-color: var(--brand); color: var(--brand); font-weight: 600; }
/* ---- TABS (ui.js tabs/tabPanel): the sections of one page. The look follows the UI style: soft = a segmented control, crisp = an underline, bold = filled tabs ---- */
.tabs { display: flex; gap: var(--space-1); width: fit-content; max-width: 100%; overflow-x: auto; padding: var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-pill); background: var(--panel); scrollbar-width: none; }
.tabs-tab { flex: none; min-height: var(--tap); padding: 0 var(--space-4); border: 0; border-radius: var(--radius-pill); background: transparent; color: var(--muted); font-weight: 600; white-space: nowrap; cursor: pointer; transition: background-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease); }
.tabs-tab:hover { color: var(--fg); }
.tabs-tab.on { background: var(--brand-soft); color: var(--brand); }
.tabs-panel { display: flex; flex-direction: column; gap: var(--stack); }
:root[data-style=crisp] .tabs { width: 100%; gap: var(--space-5); padding: 0; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: transparent; }
:root[data-style=crisp] .tabs-tab { padding: 0 var(--space-1); border-radius: 0; }
:root[data-style=crisp] .tabs-tab.on { background: transparent; color: var(--fg); box-shadow: inset 0 -3px 0 var(--brand); }
:root[data-style=bold] .tabs { gap: var(--space-2); padding: 0; border: 0; border-radius: 0; background: transparent; }
:root[data-style=bold] .tabs-tab { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); color: var(--fg); }
:root[data-style=bold] .tabs-tab.on { border-color: var(--brand); background: var(--brand); color: var(--brand-fg); }
/* the UI style picker (Settings > Appearance): chips that wrap under the row's text on a phone */
.style-row { flex-wrap: wrap; }

/* ---- the one list row: leading part, title + one subline + chips, trailing part ---- */
.list-row { animation: row-in var(--dur-2) var(--ease) both; transition: background-color var(--dur-1) var(--ease); }
.list-row:hover { background: color-mix(in srgb, var(--brand-soft) 55%, transparent); }
.row-lead { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: var(--radius); background: var(--brand-soft); color: var(--brand); flex: none; overflow: hidden; }
.row-lead img { width: 100%; height: 100%; object-fit: cover; }
.row-main { display: flex; flex-direction: column; gap: var(--space-1); flex: 1; min-width: 0; }
.row-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-sub { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-trail { display: flex; align-items: center; color: var(--muted); flex: none; }
.row-trail .spark { width: 84px; height: 28px; }
.list-row.flash { animation: flash 1.8s var(--ease); }

/* ---- search, settings, the gate's steps ---- */
.search-bar { display: flex; gap: var(--space-2); align-items: center; } .search-bar input { flex: 1; }
.search-groups { display: flex; flex-direction: column; gap: var(--space-4); }
.search-group h2 { font-size: var(--text-sm); color: var(--muted); margin-bottom: var(--space-2); }
.settings-group { display: flex; flex-direction: column; gap: var(--space-2); }
.settings-group > h2 { font-size: var(--text-sm); color: var(--muted); padding: 0 var(--space-1); }
.settings-group.danger > h2 { color: var(--bad); }
.gate-steps { display: flex; justify-content: center; gap: var(--space-2); }
.gate-step { width: 8px; height: 8px; border-radius: 50%; background: var(--line); transition: background-color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease-spring); }
.gate-step.on { background: var(--brand); transform: scale(1.3); }
.gate-head { display: flex; align-items: center; gap: var(--space-2); }

/* ---- the shared player controls (player.js): a bar over the bottom of the stage, hidden while it plays, back on a tap ---- */

.pl-controls { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); background: linear-gradient(transparent, var(--shade-4)); color: var(--media-fg); transition: opacity var(--dur-2) var(--ease); }
.pl-controls.pl-hidden { opacity: 0; pointer-events: none; }
.pl-controls .btn.icon-only { background: transparent; border-color: transparent; color: var(--media-fg); }
.pl-range { flex: 1; min-width: 0; padding: 0; min-height: 28px; accent-color: var(--brand); background: transparent; border: 0; }
.pl-volume { display: none; align-items: center; gap: var(--space-1); }
@media (hover: hover) and (pointer: fine) { .pl-volume { display: flex; } }      /* a computer with a mouse; phones have their own volume keys */
.pl-vol { width: calc(var(--tap) * 1.5); padding: 0; min-height: 28px; accent-color: var(--brand); background: transparent; border: 0; }
@media (max-width: 640px) { .pl-vol { width: var(--tap); } }      /* a narrow window keeps room for the position bar */
.pl-controls.pl-volume-only { display: none; justify-content: flex-end; }
@media (hover: hover) and (pointer: fine) { .pl-controls.pl-volume-only { display: flex; } }      /* the cutter's player: volume only, and only where there is a mouse */
.pl-time { font-size: var(--text-sm); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---- THIN SCROLLBARS everywhere (standard properties first, WebKit for older browsers) ---- */
* { scrollbar-width: thin; scrollbar-color: var(--scroll-thumb) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: var(--radius-pill); border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* ---- WIDE SCREENS: the side menu stays put and only the main view scrolls; the menu collapses to icons ---- */
@media (min-width: 860px) {
  .app { display: grid; grid-template-columns: max-content minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr); height: 100dvh; overflow: hidden; }      /* the menu is as wide as its widest item (the app's title): nothing fixed, so any app's title fits */
  .app > .update, .app > .proxy-job { grid-column: 1 / -1; position: static; }
  .app > .nav { grid-row: 3; position: static; height: auto; width: auto; overflow-x: hidden; overflow-y: auto; }
  .app > .content { grid-row: 3; grid-column: 2; min-height: 0; overflow-y: auto; }
  .nav.collapsed { padding-inline: var(--space-2); align-items: center; }
  .nav.collapsed .app-brand-text, .nav.collapsed .tab-label, .nav.collapsed .nav-foot small { display: none; }
  .nav.collapsed .brand { justify-content: center; } .nav.collapsed .brand-toggle { padding: var(--space-3) 0; width: var(--tap); }
  .nav.collapsed .tab { justify-content: center; padding: 0; width: var(--tap); }
  .nav.collapsed .nav-account { width: 100%; }
  .nav.collapsed .profile-row { justify-content: center; padding: var(--space-3) 0; } .nav.collapsed .profile-row-name { display: none; }
}
@media (min-width: 860px) {
  .app > .content { max-width: none; margin: 0; }
  .app > .content > :not(.topbar) { max-width: 1080px; margin-inline: auto; }
}
@media (min-width: 860px) { .topbar { top: calc(-1 * var(--pad-y)); } }      /* the main view is its own scroller: its padding is above the header's stuck position */

/* the brand toggles the menu (wide screens); the profile row opens the Profile window, where switching and logging out live */
.brand-toggle { background: none; border: 0; color: inherit; font: inherit; text-align: left; cursor: default; width: 100%; padding: var(--space-3); }
@media (min-width: 860px) { .brand-toggle { cursor: pointer; border-radius: var(--radius); } .brand-toggle:hover { background: color-mix(in srgb, var(--brand-soft) 55%, transparent); } }
.profile-row { display: flex; align-items: center; justify-content: flex-start; gap: var(--space-2); width: 100%; min-height: var(--tap); padding: var(--space-3); background: none; border: 0; border-radius: var(--radius); color: inherit; font: inherit; cursor: pointer; text-align: left; }
.profile-row:hover { background: color-mix(in srgb, var(--brand-soft) 55%, transparent); }
.profile-row-name { flex: 1; min-width: 0; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* wide menu: the icon column stays where it is; the text column takes the rest of the row and centers its text */
@media (min-width: 860px) { .nav:not(.collapsed) .tab .tab-label { flex: 1; text-align: center; } }

/* the sign-in screen's profiles: the choices and the add button in one wrapping row */
.gate-card .profiles { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.gate-card .profile-list, .gate-card .profile-actions { display: contents; }
.gate-card .profile-list { list-style: none; margin: 0; padding: 0; }

/* every window has an icon before its title; the profile window's name is edited in place */
.modal-head .modal-icon { color: var(--brand); flex: none; }
.profile-head { display: flex; align-items: center; gap: 0; } .profile-head .spacer { flex: 1; }      /* the name's own padding supplies the 8 px after the avatar */
.inline-text { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--tap); padding: 0 var(--space-2); background: none; border: 0; border-radius: var(--radius); color: inherit; font: inherit; font-weight: 600; cursor: text; min-width: 0; }
.inline-text:hover { background: color-mix(in srgb, var(--brand-soft) 55%, transparent); }
.inline-text .icon { width: 16px; height: 16px; opacity: .55; flex: none; }
.inline-text-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inline-text-input { flex: 1; min-width: 0; font-weight: 600; }

.topbar-icon { color: var(--brand); flex: none; margin-right: var(--space-2); }

/* ---- the label rule: floating labels for text fields (the label sits inside and moves up on focus or when filled); plain = placeholder only ---- */
.field.float { position: relative; gap: 0; color: var(--fg); font-size: var(--text-md); }
.field.float input, .field.float textarea { padding-top: var(--space-5); padding-bottom: var(--space-1); min-height: var(--control); }
.field.float textarea { min-height: 96px; }
.field.float .field-label { position: absolute; left: 13px; top: 26px; transform: translateY(-50%); transform-origin: left top; font-size: var(--text-md); color: var(--muted); pointer-events: none; transition: transform var(--dur-2) var(--ease), top var(--dur-2) var(--ease), color var(--dur-2) var(--ease); max-width: calc(100% - 26px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field.float.area .field-label { top: 24px; }
.field.float input:focus + .field-label, .field.float input:not(:placeholder-shown) + .field-label, .field.float input:-webkit-autofill + .field-label,
.field.float textarea:focus + .field-label, .field.float textarea:not(:placeholder-shown) + .field-label { top: 7px; transform: translateY(0) scale(.78); color: var(--brand); }
.field.float input:not(:focus):not(:placeholder-shown) + .field-label, .field.float textarea:not(:focus):not(:placeholder-shown) + .field-label { color: var(--muted); }
.field.float input::placeholder, .field.float textarea::placeholder { color: transparent; }
.field.float input:focus::placeholder, .field.float textarea:focus::placeholder { color: var(--muted); opacity: .8; }
.field.float.has-error .field-label { color: var(--bad); }
.field.float .field-hint, .field.float .field-error { margin-top: var(--space-1); }
.field.plain { gap: var(--space-1); }

/* the action inside the input (see ui.js field({ action })): the label of a floating field stops before it; the quick note and chat boxes are one field */
.field.float.has-action .field-label { max-width: calc(100% - 26px - 52px); }
.gate-card .profile-form { flex: 1 1 100%; margin-top: var(--space-4); }      /* the same air between the chips and what follows as in every other mode */
.quick > .field { flex: 1; min-width: 0; }
.gate-card .field.has-action { width: 100%; }

/* the app card (ui.js appBrand): icon, title and subtitle as one unit; the subtitle sits under the title, beside the icon */
.app-brand { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.app-brand-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.app-brand-title { margin: 0; font-size: var(--text-md); font-weight: 700; white-space: nowrap; }
.app-brand-subtitle { margin-top: var(--space-1); font-size: var(--text-sm); color: var(--muted); line-height: 1.3; }
.app-brand.large .logo { width: 56px; height: 56px; flex: none; font-size: var(--text-lg); }
.app-brand.large .app-brand-title { font-size: var(--text-lg); letter-spacing: -.01em; }
.app-brand.compact .logo { width: 34px; height: 34px; flex: none; border-radius: var(--radius); font-size: var(--text-sm); }

/* the sign-in card's pacing: air around the profile chips, and a quiet line at the bottom that anchors the card */
.gate-card { gap: var(--space-4); }
.gate-card .profiles { margin-block: var(--space-2); }
.gate-foot { margin: var(--space-2) 0 0; padding-top: var(--space-4); border-top: 1px solid var(--line); text-align: center; font-size: var(--text-sm); color: var(--muted); }

/* PROFILE IDENTITY (UI guide, section 7b): the same everywhere a profile is shown - the sign-in chip, the Profile window and the menu row: a 32 px avatar, the name in body size (md) at weight 600, 8 px between them */
.avatar { width: var(--avatar); height: var(--avatar); font-size: var(--text-md); font-weight: 700; }
.profile .profile-name, .profile-row-name, .inline-text, .inline-text-input { font-size: var(--text-md); font-weight: 600; }
.profile, .profile-row { gap: var(--space-2); }

/* THE WAY BACK: a plain chevron and the parent's name (never a boxed button). In the header it replaces the section title on that page; in a card it sits at the top. */
.topbar-back, .back-link { display: inline-flex; align-items: center; gap: var(--space-1); min-height: var(--tap); padding: 0 var(--space-2) 0 0; background: none; border: 0; color: var(--brand); font: inherit; font-weight: 600; cursor: pointer; min-width: 0; }
.topbar-back span, .back-link span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar-back:hover, .back-link:hover { text-decoration: underline; }
.topbar-title-wrap { display: inline-flex; align-items: center; min-width: 0; }
.topbar.has-back .topbar-title-wrap { display: none; }
@media (max-width: 859px) { .topbar.has-back-phone .topbar-title-wrap { display: none; } }
@media (min-width: 860px) { .topbar-back.phone-only { display: none; } }
.gate-head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--stack-snug); }
.gate-step-title { font-size: var(--text-lg); }

/* ---- NARROW WINDOWS AND PHONES: the same structure as wide screens - the main view is its own scroller and the bottom bar is a row of its own, so the scroll bar ends above the bar instead of running behind it ---- */
@media (max-width: 859px) {
  .app { display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto; height: 100dvh; overflow: hidden; }
  .app > .update, .app > .proxy-job { position: static; }
  .app > .nav { position: static; grid-row: 4; }
  .app > .content { grid-row: 3; min-height: 0; overflow-y: auto; padding-bottom: var(--pad-y); }
  .topbar { top: calc(-1 * var(--pad-y)); }
}

/* sync problems (the Sync window): enough to debug - the document, the error code, the detail, the node's raw answer */
.sync-problems { display: flex; flex-direction: column; gap: var(--stack-tight); }
.problem { display: flex; flex-direction: column; gap: var(--stack-snug); padding: var(--space-3); border-radius: var(--radius); background: var(--warn-bg); min-width: 0; }
.problem-name { overflow-wrap: anywhere; } .problem-code { color: var(--warn); font-weight: 600; } .problem-detail { color: var(--fg); overflow-wrap: anywhere; }
.problem-raw summary { cursor: pointer; font-size: var(--text-sm); color: var(--muted); }
.problem-raw pre { margin: var(--space-2) 0 0; font: var(--text-sm)/1.4 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; }

.sync-notes { display: flex; flex-direction: column; gap: var(--stack-snug); overflow-wrap: anywhere; }

.problem-hint { overflow-wrap: anywhere; }

/* the node's health in the Sync window: version, the administrator's message, an update notice that matters, the download link */
.node-info { display: flex; flex-direction: column; gap: var(--stack-snug); }
.node-message { overflow-wrap: anywhere; }
.update-notice { display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-3); border-radius: var(--radius); background: var(--warn-bg); color: var(--warn); font-size: var(--text-sm); } .update-notice.critical { color: var(--bad); }
.download-link { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--tap); color: var(--brand); font-weight: 600; text-decoration: none; } .download-link:hover { text-decoration: underline; }
.download-sum { overflow-wrap: anywhere; }

.progress-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.progress-step { display: flex; align-items: center; gap: var(--space-2); color: var(--muted); }
.progress-step.done { color: var(--fg); }
.progress-step.now { color: var(--brand); font-weight: 600; }
.progress-step .icon { color: var(--brand); flex: none; }
.progress-dot { flex: none; width: var(--space-4); height: var(--space-4); display: grid; place-items: center; }
.progress-dot::before { content: ""; width: var(--space-2); height: var(--space-2); border-radius: var(--radius-pill); background: currentColor; opacity: .5; }
.progress-step.now .progress-dot::before { opacity: 1; animation: progress-pulse 1s ease-in-out infinite; }
@keyframes progress-pulse { 50% { transform: scale(1.5); } }
@media (prefers-reduced-motion: reduce) { .progress-step.now .progress-dot::before { animation: none; } }

/* ---- the palettes: ACCENT ONLY (a light and a dark value each). 'violet' is the default above; theme.js sets data-palette and data-mode (none = follow the device). ---- */
:root[data-palette=green] { --p-brand: #0a7650; --p-brand-fg: #ffffff; --p-soft: #dcf3ea; --p-brand-d: #2dd4a0; --p-brand-fg-d: #06251a; --p-soft-d: #123329; }
:root[data-palette=blue] { --p-brand: #1d4ed8; --p-brand-fg: #ffffff; --p-soft: #dbe7ff; --p-brand-d: #6ea0ff; --p-brand-fg-d: #07142e; --p-soft-d: #172a4f; }
:root[data-palette=orange] { --p-brand: #b03a08; --p-brand-fg: #ffffff; --p-soft: #ffe8d9; --p-brand-d: #ff9150; --p-brand-fg-d: #2a1000; --p-soft-d: #3d2112; }
:root[data-palette=purple] { --p-brand: #6d28d9; --p-brand-fg: #ffffff; --p-soft: #ece3fb; --p-brand-d: #b794f6; --p-brand-fg-d: #1a0b36; --p-soft-d: #2a1d48; }
:root[data-palette=graphite] { --p-brand: #27272a; --p-brand-fg: #ffffff; --p-soft: #e7e7ea; --p-brand-d: #e4e4e7; --p-brand-fg-d: #18181b; --p-soft-d: #2a2a2f; }
.pl-stage { position: relative; }      /* the stage a player's controls sit on (the controls are the last child of it) */

/* "Powered by APPI-AI": the quiet badge under the sign-in card, and its window */
.powered-by { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--tap); padding: 0 var(--space-4); border: 0; background: none; color: var(--muted); font-size: var(--text-sm); cursor: pointer; border-radius: var(--radius); }
.powered-by strong { color: var(--text); font-weight: 700; letter-spacing: .02em; }
.powered-by:hover strong { text-decoration: underline; }
.powered-by-mark { width: 20px; height: 20px; }
.get-app { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); }
.get-app-link { border: 0; background: none; cursor: pointer; display: inline-grid; place-items: center; width: var(--tap); height: var(--tap); border-radius: var(--radius); color: var(--muted); }
.get-app-link:hover { color: var(--text); }
.get-app-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.about-tagline { margin: 0; font-size: var(--text-lg); font-weight: 700; }
.ws-compat { display: flex; gap: var(--space-2); align-items: flex-start; font-size: var(--text-sm); } .ws-compat .icon { width: 18px; height: 18px; margin-top: var(--space-1); }
.ws-summary { display: flex; flex-direction: column; gap: var(--stack-snug); margin-bottom: var(--stack); } .ws-summary p { margin: 0; }
.ws-actions { display: flex; gap: var(--space-2); margin-top: var(--stack); }
.ws-row.ws-expired, .ws-row.ws-revoked { opacity: .7; } .ws-expired .chip, .ws-revoked .chip { color: var(--muted); }

/* MESSAGES (UI_VERSION 32): the address card, the inbox rows, the header's two icons */
.row-head-actions { display: flex; gap: var(--space-2); align-items: center; }
.address-card .row { margin-top: 0; flex-wrap: nowrap; }
.address { font-family: var(--mono); font-size: var(--text-md); letter-spacing: .04em; user-select: all; overflow-wrap: anywhere; }
.message-row .row-main { min-width: 0; } .message-row .message-text { white-space: normal; overflow-wrap: anywhere; color: var(--fg); }

/* YOUR DATA AS A FILE (dataviews.js, 2026-11-04): the import window says in red what a restore deletes */
.data-mode.bad { color: var(--bad); }
/* the backup reminder on Home (dataviews.js reminderCard, UI guide section 15b) */
.data-reminder { display: grid; gap: var(--stack-tight); } .data-reminder-text { display: flex; gap: var(--space-2); align-items: flex-start; } .data-reminder-actions { justify-content: flex-end; }
/* INVITATIONS (UI_VERSION 42, inviteviews.js): the code shown as a QR over its text, the person's invitations under it, the way in from the Sync window */
.invite-shown { display: flex; flex-direction: column; gap: var(--stack); }
.invite-mine { display: flex; flex-direction: column; gap: var(--stack-tight); margin-top: var(--stack-section); }
.invite-mine > h2, .invite-mine > p { margin: 0; }
.invite-row.revoked, .invite-row.over { opacity: .7; } .invite-revoked { color: var(--muted); }
.invite-enter { display: grid; gap: var(--space-2); } .invite-enter p { margin: 0; }
/* KEY ROTATION (UI_VERSION 43, keyviews.js): a meta sentence over the row that leads somewhere else (a stale recovery code, "It was not me", the Sync window's way to follow a replaced key) */
.key-stale, .key-not-me, .key-replaced { display: grid; gap: var(--space-2); } .key-stale p, .key-not-me p, .key-replaced p { margin: 0; }
.key-not-me { margin-top: var(--stack-section); }

/* ---- a list row with a second control beside it (a tap target and an icon button): friend requests, shared items ---- */
.row-split { display: flex; align-items: center; gap: var(--space-2); }
.row-split .row-open { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-3); background: none; border: 0; padding: 0; text-align: left; color: inherit; cursor: pointer; min-height: var(--tap); }
.row-actions { display: flex; gap: var(--space-2); }

/* ==== THE UI STYLES, EVERY PART OF THE APP (user, 2026-11-01: "every aspect of the app" changes with the style, so a look is chosen by using it; theme.js STYLES, data-style on <html>).
   Soft is everything above. Crisp: square, hairline, quick, thin icons, underlines. Bold: big, filled, springy, heavy icons, pills. The shape tokens are set at the top of this file. ==== */
/* -- crisp -- */
:root[data-style=crisp] { --dur-1: 80ms; --dur-2: 140ms; --dur-3: 200ms; --ease-spring: var(--ease); }
:root[data-style=crisp] .icon { stroke-width: 1.4; }
:root[data-style=crisp] h1 { letter-spacing: -.02em; }
:root[data-style=crisp] .settings-group > h2, :root[data-style=crisp] .lesson-module > h2 { text-transform: uppercase; letter-spacing: .08em; }
:root[data-style=crisp] .topbar { background: var(--bg); backdrop-filter: none; }
:root[data-style=crisp] .tab.current { box-shadow: inset 0 3px 0 var(--brand); }
:root[data-style=crisp] .tab.current .icon { transform: none; }
:root[data-style=crisp] input, :root[data-style=crisp] select, :root[data-style=crisp] textarea { background: transparent; border-radius: var(--radius-xs); }
:root[data-style=crisp] .chip-choice, :root[data-style=crisp] .chip, :root[data-style=crisp] .logo, :root[data-style=crisp] .toast, :root[data-style=crisp] .option-row { border-radius: var(--radius-xs); }
:root[data-style=crisp] .toast { border-left: var(--space-1) solid var(--brand); }
@media (min-width: 860px) {
  :root[data-style=crisp] .tab.current { background: transparent; border-radius: 0; box-shadow: inset 3px 0 0 var(--brand); }
}
/* -- bold -- */
:root[data-style=bold] { --text-lg: 1.6rem; --ease-spring: cubic-bezier(.3, 1.8, .5, 1); }
:root[data-style=bold] .icon { stroke-width: 2.4; }
:root[data-style=bold] .topbar { background: color-mix(in srgb, var(--brand-soft) 60%, var(--bg)); border-bottom-color: transparent; }
:root[data-style=bold] .tab.current .icon { box-sizing: content-box; padding: var(--space-1) var(--space-4); border-radius: var(--radius-pill); background: var(--brand); color: var(--brand-fg); }
:root[data-style=bold] .btn.primary { font-weight: 800; }
:root[data-style=bold] .btn.icon-only:not(.primary):not(.danger) { border-color: transparent; background: var(--brand-soft); color: var(--brand); }
:root[data-style=bold] input, :root[data-style=bold] select, :root[data-style=bold] textarea { border-color: transparent; background: color-mix(in srgb, var(--brand-soft) 35%, var(--panel)); }
:root[data-style=bold] .list { gap: var(--space-2); border: 0; background: transparent; overflow: visible; }
:root[data-style=bold] .list > .row-item { border: 0; border-radius: var(--radius); background: color-mix(in srgb, var(--brand-soft) 45%, var(--panel)); }
:root[data-style=bold] .chip-choice.on { background: var(--brand); border-color: var(--brand); color: var(--brand-fg); }
:root[data-style=bold] .logo { border-radius: var(--radius-pill); }
:root[data-style=bold] .toast { background: var(--brand); color: var(--brand-fg); border-radius: var(--radius-pill); } :root[data-style=bold] .toast .icon { color: var(--brand-fg); }
@media (min-width: 860px) {
  :root[data-style=bold] .tab.current { background: var(--brand); color: var(--brand-fg); border-radius: var(--radius-pill); }
  :root[data-style=bold] .tab.current .icon { padding: 0; background: none; color: inherit; }
}

/* ---- THREADS (threadviews.js) and FRIENDS (friendviews.js): the chat-like timeline, entries, friends' sections ---- */
.chat { display: flex; flex-direction: column; gap: var(--space-2); max-height: 55dvh; overflow-y: auto; padding: var(--space-1); }
.msg { max-width: 82%; align-self: flex-start; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-2) var(--space-3); }
.msg.mine { align-self: flex-end; background: var(--brand-soft); }
.msg-head { display: flex; align-items: center; gap: var(--space-2); }
.msg-head .msg-del { margin-left: auto; padding: var(--space-1); min-width: 0; min-height: 0; opacity: .6; }
.msg-text { margin: var(--space-1) 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.chat-form { margin-top: var(--space-2); }
/* ---- THREADS (threadviews.js): a timeline like a messenger, the first note on top, the composer at the bottom (sticky above the tab bar on phones) ---- */
.thread-timeline { display: flex; flex-direction: column; gap: var(--space-3); }
.thread-note { align-self: stretch; }
.thread-note.card { padding: var(--space-3); }
.thread-comment { max-width: 82%; }
.thread-answers { font-style: italic; }
.thread-entry-actions { display: flex; gap: var(--space-2); justify-content: flex-end; }
.thread-composer { position: sticky; bottom: 0; align-items: center; padding: var(--space-2) 0; background: var(--bg); }
.thread-closed { text-align: center; }
/* a thread announced in a team chat: one line that opens it (user, 2026-11-03) */
.msg-thread { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-radius: var(--radius-xs); background: var(--bg); border: 1px solid var(--line); }
.msg-thread > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* what was sent to the coaches as notes before threads: listed under the threads */
.team-legacy { margin-top: var(--space-5); }
.thread-watch.on { color: var(--brand); }
.post { gap: var(--space-3); }
.post-head { display: flex; align-items: center; gap: var(--space-2); }
.post-head .icon-only { margin-left: auto; }
.post-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.friend-section { display: flex; flex-direction: column; gap: var(--stack-tight); }
.friend-section > h2, .friend-people > .friend-section > h2 { font-size: var(--text-md); font-weight: 700; }
.friend-people { display: flex; flex-direction: column; gap: var(--stack-section); }
.friend-code { display: flex; flex-direction: column; gap: var(--stack); align-items: flex-start; }
.friend-code .link-field { align-self: stretch; }          /* the code's text fills the row, its copy button inside it (user, 2026-11-03) */
.friend-add { display: flex; flex-direction: column; gap: var(--stack); }
.friend-request .row-trail, .friend-blocked-row .row-trail { color: inherit; }

/* A MONTH CALENDAR (ui.js monthCalendar, UI guide section 20b, 2026-11-04): seven columns, Monday first; today ringed, the chosen day filled; a dot per kind of thing a day holds */
.cal { display: grid; gap: var(--space-2); }
.cal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.cal-title { text-transform: capitalize; }
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-1); }
.cal-weekday { text-align: center; color: var(--muted); font-size: var(--text-sm); }
.cal-day { min-height: var(--tap); display: grid; justify-items: center; align-content: center; gap: var(--space-1); border: 1px solid transparent; border-radius: var(--radius-xs); background: none; color: var(--fg); font: inherit; cursor: pointer; padding: var(--space-1) 0; }
.cal-day:hover { background: var(--brand-soft); }
.cal-day.today { border-color: var(--brand); }
.cal-day[aria-pressed="true"] { background: var(--brand); color: var(--brand-fg); }
.cal-dots { display: flex; gap: var(--space-1); min-height: var(--space-2); }
.cal-dot { width: var(--space-2); height: var(--space-2); border-radius: var(--radius-pill); background: var(--brand); }
.cal-day[aria-pressed="true"] .cal-dot { background: var(--brand-fg); }
.cal-dot.kind-session { background: var(--good); } .cal-dot.kind-reading { background: var(--warn); } .cal-day[aria-pressed="true"] .cal-dot.kind-session, .cal-day[aria-pressed="true"] .cal-dot.kind-reading { background: var(--brand-fg); }

/* CHARTS (chart.js, UI guide section 20; from SportPerfect, a kit file since 2026-11-04): a line with its goal, a bar chart, a sparkline - colors from the tokens */
.chart-card { padding: var(--space-2) var(--space-2) var(--space-1); } .chart { width: 100%; max-width: 560px; height: auto; display: block; margin-inline: auto; } .chart-empty { padding: var(--space-6) var(--space-3); text-align: center; color: var(--muted); }
.chart-grid { stroke: var(--line); stroke-width: 1; } .chart-label { fill: var(--muted); font-size: 13px; /* ui-lint-ignore: SVG user units */ } .chart-line { fill: none; stroke: var(--brand); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.chart-dot { fill: var(--panel); stroke: var(--brand); stroke-width: 2.5; } .chart-goal { stroke: var(--good); stroke-width: 1.5; stroke-dasharray: 6 5; } .chart-goal-label { fill: var(--good); font-size: 13px; /* ui-lint-ignore: SVG user units */ }
.spark { width: 100%; height: 36px; } .spark-line { fill: none; stroke: var(--brand); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; } .spark-dot { fill: var(--brand); } .spark-flat { stroke: var(--line); stroke-width: 2; stroke-dasharray: 3 4; }
.bars { width: 100%; max-width: 420px; height: auto; display: block; }
.bars-bar { fill: color-mix(in srgb, var(--brand) 55%, transparent); } .bars-bar.on { fill: var(--brand); } .bars-bar.none { fill: var(--line); }
.bars-value { fill: var(--fg); font-size: 11px; /* ui-lint-ignore: SVG user units */ } .bars-label { fill: var(--muted); font-size: 11px; /* ui-lint-ignore: SVG user units */ } .bars-label.on { fill: var(--fg); font-weight: 600; }
.hello-charts { display: grid; gap: var(--stack-tight); } .hello-week { margin: 0; }

/* a toast's one action (ui.js toast({ action }): Undo after a drop, UI guide section 21) - the region lets nothing else take the pointer */
.toast .toast-action { pointer-events: auto; color: inherit; background: transparent; border-color: transparent; } .toast .toast-action:hover { background: color-mix(in srgb, currentColor 14%, transparent); }

/* CALLS IN A THREAD (UI_VERSION 53, callviews.js): the call is a full-screen view over the app (windows and the confirm dialog still open above it); a tile per person, my own picture small and
   mirrored; the controls are icon-only at the bottom. The banner in the thread is a list row. */
.call-banner { margin-bottom: var(--space-3); border: 1px solid var(--brand); background: var(--brand-soft); }
.call-banner.in { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3); border-radius: var(--radius); color: var(--fg); }
.call-banner.full { border-color: var(--line); background: var(--panel); }
.call-join { color: var(--brand); font-weight: 600; }
.call-screen { position: fixed; inset: 0; z-index: var(--z-call); display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3); background: var(--media-bg); color: var(--media-fg); }
.call-head { display: flex; align-items: center; gap: var(--space-2); min-height: 44px; }
.call-head .call-count { margin-left: auto; color: color-mix(in srgb, var(--media-fg) 70%, transparent); }
.call-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.call-tiles { flex: 1; min-height: 0; display: grid; gap: var(--space-2); grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); grid-auto-rows: minmax(0, 1fr); }
.call-tile { position: relative; margin: 0; min-height: 120px; overflow: hidden; border-radius: var(--radius); background: color-mix(in srgb, var(--media-fg) 10%, var(--media-bg)); }
.call-tile video { width: 100%; height: 100%; object-fit: cover; display: block; }
.call-tile video.mirror { transform: scaleX(-1); }
.call-tile.off video { visibility: hidden; }
.call-tiles { position: relative; }
.call-tiles.with-others .call-tile.mine { position: absolute; right: var(--space-2); bottom: var(--space-2); z-index: 1; width: 160px; height: 90px; min-height: 0; box-shadow: var(--shadow-pop); }
.call-tiles.with-others .call-tile.mine .call-avatar { width: 40px; height: 40px; font-size: var(--text-md); }
.call-avatar { position: absolute; inset: 0; margin: auto; width: 72px; height: 72px; display: grid; place-items: center; border-radius: 50%; background: var(--brand); color: var(--brand-fg); font-size: var(--text-lg); font-weight: 700; }
.call-name { position: absolute; left: var(--space-2); bottom: var(--space-2); display: flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2); border-radius: var(--radius-pill); background: color-mix(in srgb, var(--media-bg) 60%, transparent); font-size: var(--text-sm); }
.call-name .icon { width: 16px; height: 16px; }
.call-state { color: color-mix(in srgb, var(--media-fg) 70%, transparent); }
.call-sound { align-self: center; display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px; padding: var(--space-2) var(--space-4); border: 0; border-radius: var(--radius-pill); background: var(--media-fg); color: var(--media-bg); font: inherit; font-weight: 600; cursor: pointer; }
.call-controls { display: flex; justify-content: center; gap: var(--space-3); padding-bottom: env(safe-area-inset-bottom); }
.call-controls .btn { width: 56px; height: 56px; border-radius: 50%; }
.call-controls .btn.on { background: var(--media-fg); color: var(--media-bg); }
@media (max-width: 640px) {
  .call-tiles { grid-template-columns: 1fr; }
  .call-tiles.n2 { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; }
  .call-tiles:is(.n3, .n4, .n5, .n6) { grid-template-columns: 1fr 1fr; }
  .call-tiles.with-others .call-tile.mine { width: 96px; height: 128px; }
}

/* the node refusing this profile (nodeproblem.js; UI guide section 11): an attention card on Home, never a dialog */
.node-problem { display: flex; flex-direction: column; gap: var(--stack-snug); background: var(--warn-bg); border-left: 3px solid var(--warn); }
.node-problem strong { color: var(--warn); }

/* ================================================================================================================================================================
   MUSICPERFECT (2026-11-08): the app's own screens. Tokens only (ui-lint "error"); SVG text sizes are user units and carry ui-lint-ignore.
   ================================================================================================================================================================ */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; } /* ui-lint-ignore: the standard visually-hidden pattern needs margin -1px */
/* Home: the week (as SportPerfect's), titled sections */
.week-card { display: grid; gap: var(--space-2); }
.week-head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.week-streak { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--brand); font-weight: 600; }
.home-section, .ex-group { display: flex; flex-direction: column; gap: var(--stack-tight); }
.ex-group > h2, .home-section > h2 { font-size: var(--text-sm); color: var(--muted); padding: 0 var(--space-1); }
:root[data-style=crisp] .ex-group > h2, :root[data-style=crisp] .home-section > h2 { text-transform: uppercase; letter-spacing: .08em; }
.ex-row .row-meta .chip { margin-top: var(--space-1); }
/* an exercise's page: its choices side by side where there is room */
.exercise-choices { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); gap: var(--stack-tight) var(--space-3); align-items: start; }
.roll-card { padding: var(--space-2); gap: var(--space-2); }
.roll-words { padding: 0 var(--space-2) var(--space-1); }
/* THE PIANO ROLL (roll.dcdd27dfd2.js) */
.roll { width: 100%; height: auto; display: block; }
.roll-lane { fill: transparent; } .roll-lane.black { fill: color-mix(in srgb, var(--line) 45%, transparent); }
.roll-beat { stroke: var(--line); stroke-width: 1; } .roll-beat.bar { stroke: var(--muted); stroke-width: 1.2; }
.roll-label { fill: var(--muted); font-size: 10px; /* ui-lint-ignore: SVG user units */ }
.roll-note { fill: var(--brand-soft); stroke: color-mix(in srgb, var(--brand) 55%, transparent); stroke-width: 1; }
.roll-note.missed { fill: transparent; stroke: var(--bad); stroke-dasharray: 3 2; }
.roll-trace { fill: none; stroke: var(--brand); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.roll-playhead { stroke: var(--fg); stroke-width: 1.5; }
.roll-mark { stroke: var(--panel); stroke-width: 1; } .roll-mark.good { fill: var(--good); } .roll-mark.near { fill: var(--warn); } .roll-mark.off { fill: var(--bad); }
.roll-mark.missed { fill: none; stroke: var(--bad); stroke-width: 2; } .roll-mark.extra { fill: none; stroke: var(--warn); stroke-width: 1.5; }
/* THE PRACTICE VIEW (practiceview.9818830733.js): a full-screen capture view, the controls in a fixed bottom bar (UI guide 12) */
.practice-screen .modal-body { padding-bottom: 0; }
.practice { display: flex; flex-direction: column; gap: var(--stack); min-height: 100%; }
.count-in { display: flex; flex-direction: column; align-items: center; gap: var(--stack-tight); text-align: center; }
.count-number { font-size: var(--text-lg); font-weight: 800; color: var(--brand); min-width: var(--tap); }
.beat-dots { display: flex; gap: var(--space-3); justify-content: center; }
.beat-dot { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--line); transition: transform var(--dur-1) var(--ease); }
.beat-dot.first { border-color: var(--muted); }
.beat-dot.on { background: var(--brand); border-color: var(--brand); transform: scale(1.25); }
.live-note { font-size: var(--text-lg); } .live-note .muted { font-size: var(--text-md); }
.mic-ask-head { display: flex; align-items: center; gap: var(--space-2); } .mic-ask-head .icon { color: var(--brand); }
.scores { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.score { display: flex; flex-direction: column; align-items: center; gap: var(--stack-snug); padding: var(--pad-inset); border-radius: var(--radius); background: var(--brand-soft); }
.score-value { font-size: var(--text-lg); font-weight: 800; color: var(--brand); }
.score-label { font-size: var(--text-sm); color: var(--muted); }
.result-notes { display: flex; flex-direction: column; gap: var(--stack-tight); }
.note-list { margin: 0; padding: 0; list-style: none; }
.note-row { display: flex; align-items: center; justify-content: flex-start; gap: var(--space-3); font-size: var(--text-sm); text-align: left; }
.note-mark { display: inline-grid; place-items: center; width: 24px; flex: none; font-weight: 700; }
.note-mark.good { color: var(--good); } .note-mark.near { color: var(--warn); } .note-mark.off, .note-mark.missed { color: var(--bad); }
.capture-bar { position: sticky; bottom: 0; margin-top: auto; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0 calc(var(--space-3) + env(safe-area-inset-bottom)); background: var(--panel); border-top: 1px solid var(--line); }
.capture-bar .capture-close { justify-self: start; } .capture-bar .capture-save { justify-self: end; }
.capture-bar .capture-main { width: 64px; height: 64px; border-radius: 50%; }
.capture-bar .capture-main .icon { width: 28px; height: 28px; }
/* THE TUNER (tunerview.c30ccd45d1.js) */
.tuner-card { align-items: center; text-align: center; gap: var(--space-2); }
.tuner-card.in-tune { border-color: var(--good); }
.tuner-dial { width: min(100%, 360px); height: auto; }
.tuner-arc { fill: none; stroke: var(--line); stroke-width: 6; stroke-linecap: round; }
.tuner-band { fill: color-mix(in srgb, var(--good) 22%, transparent); }
.tuner-tick { stroke: var(--muted); stroke-width: 2; } .tuner-tick.center { stroke: var(--good); stroke-width: 3; }
.tuner-tick-label { fill: var(--muted); font-size: 9px; /* ui-lint-ignore: SVG user units */ }
.tuner-needle { stroke: var(--warn); stroke-width: 3; stroke-linecap: round; transition: transform var(--dur-2) var(--ease); } .tuner-needle.in-tune { stroke: var(--good); }
.tuner-hub { fill: var(--fg); }
.tuner-note { font-size: var(--text-lg); font-weight: 800; min-height: var(--tap); }
.tuner-cents { font-size: var(--text-md); font-weight: 600; }
/* Progress: the table for screen readers (and anyone who likes numbers) */
.table-scroll { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.data-table th, .data-table td { padding: var(--space-2); border-bottom: 1px solid var(--line); text-align: left; white-space: nowrap; }
.data-table th { color: var(--muted); font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .beat-dot.on { transform: none; } .tuner-needle { transition: none; } }

/* EDGE TO EDGE (UI guide section 3, changelog 99; user, 2026-11-09): index.html says viewport-fit=cover, so on Android (Chrome 135 and later) the page reaches the bottom edge and the main view
   scrolls on behind the gesture bar. Whatever must stay tappable or readable keeps clear of it by the system's inset (0 everywhere else): the bottom bar already pads it on phones (its own
   background fills behind the gesture bar), and the window footer, the call controls and the toast already add it. These are the places that still ran to the edge. */
.app { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }
.gate { padding-bottom: calc(var(--gap) + env(safe-area-inset-bottom)); }
.modal-body:last-child { padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom)); }
@media (min-width: 860px) {
  .app > .content { padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom)); }
  .app > .nav { padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom)); }
  .toast-region { bottom: calc(var(--space-5) + env(safe-area-inset-bottom)); }
  .thread-composer { padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom)); }      /* sticky at the foot of the main view */
}

/* A SETTINGS ROW WITH A CONTROL AT ITS END (Settings > Data "Remind me to download it"; fixed 2026-11-09): the control keeps its own width and the text its sentence; on a phone the
   control goes under the text instead of squeezing the title to one word a line */
.row-item.flat { flex-wrap: wrap; row-gap: var(--space-2); }
.row-item.flat > .row-main { flex: 1 1 16rem; }      /* ui-lint-ignore: a flex basis, the width the text needs before the control wraps under it */
.row-item.flat > .row-main .muted { white-space: normal; overflow: visible; }
.row-item.flat > select { flex: 0 0 auto; width: auto; max-width: 100%; }

/* STAFF NOTATION (staff.js, user 2026-11-09: "musical notation and not just pitch time notation"): lines in the line color, notes in the text color; after a run each note in the color of how it
   went (the note-by-note list says it in words). The clef and accidentals are text glyphs (the system's music font). The session file's section under the result. */
.notation-choice { margin-bottom: var(--stack-tight); }
.staff { width: 100%; height: auto; display: block; }
.staff-line { stroke: var(--muted); stroke-width: 1; }
.staff-bar { stroke: var(--muted); stroke-width: 1.2; } .staff-bar.final { stroke-width: 3; }
.staff-clef, .staff-key, .staff-accidental, .staff-rest-glyph { fill: var(--fg); font-family: "Segoe UI Symbol", "Noto Music", "Bravura Text", serif; }
.staff-clef.treble { font-size: 44px; } .staff-clef.bass { font-size: 30px; }      /* ui-lint-ignore: music glyphs are drawn to the staff's size, not text */
.staff-key, .staff-accidental { font-size: 16px; } .staff-rest-glyph { font-size: 22px; }      /* ui-lint-ignore: music glyphs are drawn to the staff's size, not text */
.staff-time { fill: var(--fg); font-weight: 800; font-size: 18px; }      /* ui-lint-ignore: the time signature is part of the staff drawing */
.staff-head { fill: var(--fg); stroke: var(--fg); stroke-width: 1.2; } .staff-head.open, .staff-head.whole { fill: none; stroke-width: 1.6; }
.staff-stem { stroke: var(--fg); stroke-width: 1.3; } .staff-flag { fill: none; stroke: var(--fg); stroke-width: 1.6; } .staff-dot { fill: var(--fg); } .staff-rest { fill: var(--fg); }
.staff-head.good, .staff-stem.good, .staff-flag.good, .staff-dot.good { fill: var(--good); stroke: var(--good); }
.staff-head.near, .staff-stem.near, .staff-flag.near, .staff-dot.near { fill: var(--warn); stroke: var(--warn); }
.staff-head.off, .staff-stem.off, .staff-flag.off, .staff-dot.off { fill: var(--bad); stroke: var(--bad); }
.staff-head.missed { fill: none; stroke: var(--bad); stroke-dasharray: 2 2; }
.staff-head.open.good, .staff-head.whole.good, .staff-head.open.near, .staff-head.whole.near, .staff-head.open.off, .staff-head.whole.off { fill: none; }
.session-file { display: flex; flex-direction: column; gap: var(--stack-tight); margin-top: var(--stack); }
