/* Rolls & Rocks — shared theme.
   Palette sampled from the RollsAndRocks Spotify avatar (deep violet base,
   electric coral, stage blue, warm cream). */

:root {
    --ink: #150F24;
    --ink-soft: #1E1633;
    --surface: #221A38;
    --surface-raised: #2B2145;
    --line: #392C58;
    --line-strong: #4C3B76;
    --cream: #FFF6E5;
    --muted: #A99CC6;
    --coral: #FF4D6A;
    --coral-soft: #FF8FA3;
    /* The warm end of the gradient every button carried before the action row was
       unified (issue #32). Only the donation invite still wears it. */
    --coral-light: #FF6E86;
    --orange: #FF8A5C;
    --orange-light: #FF9C63;
    --blue: #2FA8FF;
    --sky: #EFE7FF;
    --success: #64D69A;
    --warning: #FFC861;
    --danger: #FF6B6B;
    --radius: 14px;
    --radius-sm: 9px;
    --shadow: 0 22px 60px rgba(8, 5, 18, .55);
    --font-display: "Bebas Neue", "Arial Narrow", sans-serif;
    --font-body: Manrope, system-ui, -apple-system, sans-serif;
    --font-mono: "DM Mono", ui-monospace, monospace;
}

*, *::before, *::after { box-sizing: border-box; }

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background:
        radial-gradient(1100px 520px at 12% -12%, rgba(255, 77, 106, .22), transparent 62%),
        radial-gradient(900px 520px at 88% 6%, rgba(47, 168, 255, .18), transparent 64%),
        var(--ink);
    color: var(--cream);
    font: 400 1rem/1.65 var(--font-body);
}

/* Amp-grille texture, kept behind every layer. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image: repeating-linear-gradient(0deg, rgba(255, 246, 229, .03) 0 1px, transparent 1px 3px);
}

.site-nav, .page, .site-footer { position: relative; z-index: 1; }

h1, h2, h3 { font-family: var(--font-display); font-weight: 400; line-height: .95; margin: 0; text-transform: uppercase; }
h1 { font-size: clamp(2.6rem, 6vw, 4.6rem); letter-spacing: .015em; }
h1 span, h1 em { color: var(--coral); font-style: normal; }
h2 { font-size: clamp(1.6rem, 3vw, 2.3rem); letter-spacing: .02em; }
p { margin: 0; }
a { color: inherit; }
:focus-visible { outline: 3px solid var(--coral); outline-offset: 3px; }

.eyebrow { align-items: center; color: var(--coral-soft); display: flex; font: 700 .72rem var(--font-mono); gap: 10px; letter-spacing: .22em; margin: 0 0 12px; text-transform: uppercase; }
.eyebrow::before { background: var(--coral); content: ""; height: 2px; width: 26px; }
.lead { color: var(--muted); font-size: 1.02rem; margin-top: 14px; max-width: 62ch; }
.muted, .subtle-link { color: var(--muted); }
.subtle-link { font-size: .88rem; }

/* --- logo (circular raster badge with a transparent background) -------- */
.rr-logo { border-radius: 50%; display: block; height: auto; width: 46px; }
.site-nav__brand .rr-logo, .site-footer .rr-logo { filter: drop-shadow(0 6px 16px rgba(255, 77, 106, .28)); }
.hero__marquee .rr-logo { filter: drop-shadow(0 18px 40px rgba(255, 77, 106, .32)); margin: 0 auto 18px; width: clamp(130px, 24vw, 200px); }
.site-footer .rr-logo { width: 64px; }

/* --- navigation ------------------------------------------------------- */
.site-nav {
    align-items: center;
    backdrop-filter: blur(14px);
    background: rgba(21, 15, 36, .88);
    border-bottom: 1px solid var(--line);
    display: flex;
    gap: 22px;
    justify-content: space-between;
    padding: 5px clamp(18px, 5vw, 56px);
    position: sticky;
    top: 0;
    z-index: 20;
}
.site-nav__brand { align-items: center; display: inline-flex; gap: 12px; text-decoration: none; }
.site-nav__brand .rr-logo { flex-shrink: 0; height: 64px; width: 64px; }
.site-nav__links, .site-nav__account { align-items: center; display: flex; gap: 18px; }
.site-nav__links a, .site-nav__account a, .site-nav__logout {
    color: var(--muted);
    font: 700 .8rem var(--font-body);
    letter-spacing: .06em;
    text-decoration: none;
    text-transform: uppercase;
}
.site-nav__links a:hover, .site-nav__account a:hover, .site-nav__logout:hover { color: var(--coral); }
.site-nav__logout { background: none; border: 0; cursor: pointer; padding: 0; }
.site-nav__user { color: var(--muted); font: 700 .74rem var(--font-mono); letter-spacing: .04em; }

/* --- footer ----------------------------------------------------------- */
.site-footer { border-top: 1px solid var(--line); margin-top: 64px; padding: 5px clamp(18px, 5vw, 56px) 20px; }
.site-footer__inner { align-items: center; display: flex; flex-wrap: wrap; gap: 16px; margin: 0 auto; max-width: 1100px; }
.site-footer .rr-logo { flex-shrink: 0; height: 64px; width: 64px; }
/* The tagline and the three information links share one column (issue #31), so the
   legal line starts exactly under the wordmark instead of under the logo. */
.site-footer__text { display: flex; flex-direction: column; gap: 6px; }
.site-footer__tagline { color: var(--cream); font-family: var(--font-display); font-size: 1.2rem; letter-spacing: .06em; text-transform: uppercase; }
.site-footer__meta { color: var(--muted); font: 400 .72rem var(--font-mono); letter-spacing: .04em; margin-left: auto; }
/* The three information pages — Terms of Use, Privacy Policy and the cookie
   inventory — are linked in one line (issue #26, trimmed in issue #31) and stay
   reachable without signing in. The bottom padding of the footer keeps this last
   line away from the edge of the page. */
.site-footer__legal { color: var(--muted); font: 400 .72rem var(--font-mono); letter-spacing: .04em; }
.site-footer__legal a { color: var(--cream); text-decoration: underline; text-underline-offset: 3px; }
.site-footer__legal a:hover { color: var(--coral); }

/* --- page shell ------------------------------------------------------- */
.page { flex: 1; margin: 0 auto; max-width: 1100px; padding: 54px clamp(18px, 5vw, 56px) 72px; width: 100%; }
.page-heading { margin-bottom: 34px; max-width: 760px; }
.site-messages { margin: 0 0 26px; }
.site-message {
    background: var(--surface-raised);
    border: 1px solid var(--line);
    border-left: 4px solid var(--blue);
    border-radius: var(--radius-sm);
    color: var(--cream);
    margin: 0 0 10px;
    padding: 13px 16px;
}
.site-message--error { border-left-color: var(--danger); color: var(--coral-soft); }
.site-message--success { border-left-color: var(--success); }
/* Donation invite: the same row inside an export message, under the track table
   of an exported playlist and under the players of the Embed view. The rows only
   place the invite; the button keeps the shared geometry, so the Donate pill is
   the size of the buttons in the action row above the table (issue #32). */
.site-message__cta, .donate-cta { align-items: center; display: flex; flex-wrap: wrap; gap: 14px; }
.site-message__cta { margin-top: 12px; }
.donate-cta { margin-top: 20px; }

/* --- hero (home) ------------------------------------------------------ */
.hero { display: grid; gap: 46px; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); align-items: center; }
.hero__logo { display: flex; justify-content: center; }
.hero__logo .rr-logo {
    filter: drop-shadow(0 24px 50px rgba(255, 77, 106, .38));
    height: auto;
    width: clamp(96px, 16vw, 150px);
}
.hero__marquee { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 28px; position: relative; overflow: hidden; }
.hero__marquee::before { background: var(--coral); content: ""; height: 4px; inset: 0 0 auto; position: absolute; }
.hero__marquee::after { background: var(--blue); content: ""; height: 4px; inset: auto 0 0; position: absolute; }
.hero__marquee .rr-logo { border-radius: 0; height: auto; margin: 0 auto 18px; max-width: 100%; width: 200px; }
.hero__numbers { display: grid; gap: 14px; grid-template-columns: repeat(3, 1fr); margin-top: 22px; }
.hero__number { border-left: 2px solid var(--line-strong); padding-left: 12px; }
.hero__number strong { color: var(--coral); display: block; font-family: var(--font-display); font-size: 1.9rem; letter-spacing: .04em; }
.hero__number span { color: var(--muted); font: 400 .68rem var(--font-mono); letter-spacing: .08em; text-transform: uppercase; }
.hero__bill { color: var(--muted); font: 400 .78rem var(--font-mono); letter-spacing: .06em; line-height: 1.9; text-transform: uppercase; }
/* The bulk entry point sits under the panel next to the upload form (issue #56). */
.hero__aside { display: flex; flex-direction: column; gap: 20px; }
.hero__batch { display: flex; flex-direction: column; gap: 10px; text-align: center; }
/* The button is as wide as the panel above it, so the two read as one stack. */
.hero__batch .button { display: block; }
.hero__batch .muted { font-size: .84rem; }

/* --- panels & forms --------------------------------------------------- */
/* Batch pages: the explanation stands above the table, the panel keeps the form and
   the latest runs apart (issue #56). */
.batch-intro { margin-bottom: 26px; }
.batch-intro h2 { font-size: 1.4rem; }
.batch-intro p + p { margin-top: 10px; }
.batch-list { margin-top: 32px; }
.batch-list h2 { margin-bottom: 16px; }
.batch-processing { margin-bottom: 28px; scroll-margin-top: 24px; }
.batch-processing .catalog-controls { justify-content: flex-end; }
.batch-page-size { margin-inline-start: auto; }
.batch-processing .batch-processing__scroll { max-height: min(60vh, 38rem); overflow: auto; }
.batch-processing__scroll:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
.batch-processing__scroll .track-table { min-width: 560px; }
.batch-processing__scroll th { position: sticky; top: 0; z-index: 1; }
.batch-processing__scroll th:first-child { width: 50%; }
.batch-processing__scroll th:nth-child(2) { width: 20%; }
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: clamp(22px, 4vw, 32px); }
.panel--narrow { margin: 0 auto; max-width: 520px; }
.panel--narrow .page-heading { margin-bottom: 24px; }
form p { display: grid; gap: 8px; margin: 0 0 18px; }
label { color: var(--cream); font-size: .82rem; font-weight: 800; letter-spacing: .03em; }
input:not([type="hidden"]), select, textarea {
    background: var(--ink-soft);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    color: var(--cream);
    font: inherit;
    padding: 12px 13px;
    width: 100%;
}
input::placeholder, textarea::placeholder { color: #7C6DA3; }
input[type="file"]::file-selector-button { background: var(--surface-raised); border: 1px solid var(--line-strong); border-radius: 6px; color: var(--cream); cursor: pointer; font: 700 .74rem var(--font-mono); margin-right: 12px; padding: 8px 11px; }

/* --- the Terms of Use acceptance line (registration, issue #26) -------- */
/* The box is drawn by the TermsAcceptanceCheckbox widget together with its
   caption inside one <span>: the box first, aligned with the left edge of the
   row, then the wording, and the note about the Privacy Policy on its own grid
   row below. The sizing has to come after the generic text-input rule above and
   be more specific than it, because that rule's width: 100% and padding would
   otherwise stretch the box and push it onto a line of its own; the explicit flex
   basis keeps the 18 px box even if a later rule touched the width again. */
.terms-line { align-items: center; display: flex; flex-wrap: wrap; gap: 8px 10px; }
.terms-line > label { flex: 0 1 auto; margin: 0; }
form input[type="checkbox"], input[type="checkbox"] { accent-color: var(--coral); flex: 0 0 18px; height: 18px; margin: 0; padding: 0; width: 18px; }
.form-error, .errorlist { color: var(--coral-soft); font-size: .82rem; list-style: none; margin: 0; padding: 0; }
.alert { background: rgba(255, 107, 107, .12); border: 1px solid rgba(255, 107, 107, .4); border-left: 4px solid var(--danger); border-radius: var(--radius-sm); color: var(--coral-soft); margin: 20px 0; padding: 13px 16px; }
.success { background: rgba(100, 214, 154, .12); border-left: 4px solid var(--success); border-radius: var(--radius-sm); margin: 20px 0 0; overflow-wrap: anywhere; padding: 13px 16px; }
/* A playlist a running batch owns (issue #56): readable, but it offers no action. */
.notice { background: rgba(255, 200, 97, .10); border-left: 4px solid var(--warning); border-radius: var(--radius-sm); margin: 18px 0 0; overflow-wrap: anywhere; padding: 13px 16px; }

/* --- actions ---------------------------------------------------------- */
.actions { align-items: center; display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
.actions--before-list { margin-bottom: 24px; }
.actions__end { display: flex; gap: 12px; margin-left: auto; }

/* --- empty states carry a faint logo watermark ------------------------ */
.empty-state {
    background-image: url("logo.35c5e501fa7f.png");
    background-position: center 26px;
    background-repeat: no-repeat;
    background-size: 96px auto;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius);
    color: var(--muted);
    padding: 104px 28px 38px;
    text-align: center;
}
.empty-state::before { content: none; }

/* --- tables ----------------------------------------------------------- */
.track-table-wrap { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }
.track-table { border-collapse: collapse; table-layout: fixed; width: 100%; }
.track-table th {
    background: var(--surface-raised);
    color: var(--muted);
    font: 700 .66rem var(--font-mono);
    letter-spacing: .14em;
    padding: 12px 14px;
    text-align: left;
    text-transform: uppercase;
}
.track-table td { border-top: 1px solid var(--line); font-size: .88rem; overflow-wrap: anywhere; padding: 10px 14px; vertical-align: middle; }
.track-table tbody tr:hover { background: rgba(255, 77, 106, .06); }
.track-table .cover-cell { padding: 6px 10px; width: 48px; } /* Play column: keep it narrow and let Title and Album absorb the reclaimed width evenly (1.5 x W/n - 28px). */ .track-table th.play-cell { padding: 12px 10px; width: 56px; } .track-table--tracks th.title-cell, .track-table--tracks th.album-cell { width: calc(37.5% - 28px); } .track-table--export th.title-cell, .track-table--export th.album-cell { width: calc(30% - 28px); }
.popular-ranking + .popular-ranking { margin-top: 40px; }
.popular-ranking .track-table-wrap { margin-top: 20px; }
.popular-ranking th { overflow-wrap: anywhere; }
.popular-ranking th:first-child { width: 64px; }
.popular-ranking th:last-child { width: 120px; }
/* Catalog tables: Hash and Source keep the one-seventh share they always had, the four trailing
   columns hug a single line of their widest content (header included, plus the 28px cell padding)
   and the playlist name absorbs every reclaimed pixel. */
.track-table--catalog th:nth-child(n+4), .track-table--catalog td:nth-child(n+4) { white-space: nowrap; }
.track-table--catalog th:nth-child(2), .track-table--catalog th:nth-child(3) { width: calc(100% / 7); }
.track-table--catalog th:nth-child(4) { width: 72px; } /* Type */
.track-table--catalog th:nth-child(5) { width: 104px; } /* Size */
.track-table--catalog th:nth-child(6) { width: 108px; } /* Tracks */
.track-table--catalog th:nth-child(7) { width: 110px; } /* Created */
/* Catalog and task tables: the name reads like the rest of the row (the same font as Source). */
.track-table--catalog .playlist-card__name, .track-table--tasks .playlist-card__name { font: inherit; letter-spacing: normal; text-transform: none; }
/* Task tables: Hash keeps its one-fifth share, the three trailing columns hug a single line of
   their widest content (the timestamp and the status pill included) and the task name absorbs
   every reclaimed pixel. */
.track-table--tasks th:nth-child(n+3), .track-table--tasks td:nth-child(n+3) { white-space: nowrap; }
.track-table--tasks th:nth-child(2) { width: calc(100% / 5); }
.track-table--tasks th:nth-child(3) { width: 84px; }  /* Source */
.track-table--tasks th:nth-child(4) { width: 150px; } /* Created */
.track-table--tasks th:nth-child(5) { width: 136px; } /* Status */
.track-table--tasks th:nth-child(6), .track-table--batch th:nth-child(6) { width: 96px; }
.track-table .api-calls { font-variant-numeric: tabular-nums; }
/* Why a task failed belongs to the row: the reason is printed under the task name,
   which is the only column with room to wrap it (issue #31). */
.track-table--tasks .task-error { color: var(--muted); font: 400 .72rem var(--font-mono); margin-top: 4px; }
/* Batch tables (issue #56): the batch name absorbs every freed pixel and the trailing
   columns hug a single line of their widest content, exactly as in the catalogs. The
   public table and the panel have five columns, the detail page three. */
.track-table--batch th:nth-child(n+2), .track-table--batch td:nth-child(n+2) { white-space: nowrap; }
.track-table--batch th:nth-child(2) { width: 120px; }  /* Status */
.track-table--batch th:nth-child(3) { width: 110px; }  /* Playlists */
.track-table--batch th:nth-child(4) { width: 132px; }  /* Processing time / Files */
.track-table--batch th:nth-child(5) { width: 120px; }  /* Created / Processing time */
.track-table--batch .playlist-card__name { font: inherit; letter-spacing: normal; text-transform: none; }
.cover-wrap { display: block; height: 32px; position: relative; width: 32px; }
img.cover { border-radius: 4px; display: block; height: 32px; object-fit: cover; width: 32px; }
.play-overlay {
    align-items: center;
    background: rgba(21, 15, 36, .82);
    border: 1px solid var(--coral);
    border-radius: 50%;
    color: var(--cream);
    cursor: pointer;
    display: flex;
    font: 700 .5rem var(--font-mono);
    height: 18px;
    justify-content: center;
    left: 50%;
    opacity: 0;
    padding: 0;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 18px;
}
.cover-wrap:hover .play-overlay, .cover-wrap:focus-within .play-overlay, .play-overlay.is-playing { opacity: 1; }

/* --- status pills ----------------------------------------------------- */
.status-pill { border-radius: 999px; border: 1px solid transparent; font: 700 .64rem var(--font-mono); letter-spacing: .1em; padding: 7px 11px; text-transform: uppercase; white-space: nowrap; }
.status-pill--queued { background: rgba(255, 200, 97, .14); border-color: rgba(255, 200, 97, .45); color: var(--warning); }
.status-pill--processing, .status-pill--downloading { background: rgba(47, 168, 255, .16); border-color: rgba(47, 168, 255, .45); color: var(--blue); }
.status-pill--succeeded { background: rgba(100, 214, 154, .14); border-color: rgba(100, 214, 154, .45); color: var(--success); }
.status-pill--failed { background: rgba(255, 77, 106, .16); border-color: rgba(255, 77, 106, .45); color: var(--coral-soft); }
.status-pill--partially_failed { background: rgba(255, 200, 97, .14); border-color: rgba(255, 200, 97, .45); color: var(--warning); }

/* --- definition lists & progress -------------------------------------- */
.facts { display: grid; margin: 0; }
.facts div { border-bottom: 1px solid var(--line); display: grid; gap: 12px; grid-template-columns: 150px 1fr; padding: 14px 0; }
.facts div:last-child { border-bottom: 0; }
.facts dt { color: var(--muted); font: 700 .68rem var(--font-mono); letter-spacing: .1em; text-transform: uppercase; }
.facts dd { margin: 0; overflow-wrap: anywhere; }
progress { accent-color: var(--coral); background: var(--ink-soft); border: 0; border-radius: 999px; height: 9px; overflow: hidden; width: 100%; }

/* --- playlist cards & lists ------------------------------------------- */
.status-list, .playlist-list { display: grid; gap: 12px; list-style: none; margin: 0; padding: 0; }
.playlist-card, .task-card {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    display: flex;
    gap: 20px;
    justify-content: space-between;
    padding: 18px 20px;
    transition: border-color .2s ease, transform .2s ease;
}
.playlist-card:hover, .task-card:hover { border-color: var(--line-strong); transform: translateY(-2px); }
.playlist-card__name, .task-card__file { color: var(--cream); font-family: var(--font-display); font-size: 1.2rem; letter-spacing: .04em; overflow-wrap: anywhere; text-decoration: none; text-transform: uppercase; }
.playlist-card__name:hover, .task-card__file:hover { color: var(--coral); }
.playlist-card__meta, .task-card__meta { color: var(--muted); font: 400 .7rem var(--font-mono); letter-spacing: .04em; margin-top: 6px; }

/* --- playlist detail header ------------------------------------------- */
.playlist-head { align-items: flex-end; display: flex; flex-wrap: wrap; gap: 24px; justify-content: space-between; margin-bottom: 30px; }
.playlist-meta, .export-name-form__meta { color: var(--muted); font: 400 .76rem var(--font-mono); } .playlist-meta { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-top: 15px; }
.playlist-meta strong, .export-name-form__meta strong { color: var(--cream); }
.playlist-meta__status { flex-basis: 100%; }

/* --- catalog controls & pagination ------------------------------------ */
.catalog-controls { align-items: flex-end; display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 22px; }
.catalog-controls label { display: grid; gap: 6px; }
.catalog-controls__search { flex: 1 1 240px; }
.catalog-controls input[type="search"] { width: 100%; }
.catalog-controls select { min-width: 84px; }
.pagination { display: flex; justify-content: center; margin-top: 22px; }
.pagination__pages { align-items: center; display: flex; gap: 12px; }
.pagination__status { color: var(--muted); font: 700 .72rem var(--font-mono); letter-spacing: .1em; text-transform: uppercase; }

/* --- export title form ------------------------------------------------ */
.export-name-form { align-items: center; display: flex; flex-wrap: wrap; gap: 12px; }
.export-name-form label { white-space: nowrap; }
.export-name-form input:not([type="hidden"]) { width: 230px; }
.export-name-form .errorlist { flex-basis: 100%; margin: 0; order: 2; }
.export-name-form + .empty-state { margin-top: 24px; }

/* --- cookie information page ------------------------------------------ */
/* --- long-form documents: cookies, Terms of Use, Privacy Policy -------- */
/* The cookie page and both legal documents share one set of section styles, so
   the long texts cannot drift apart visually. */
.cookie-section, .document-section { margin-top: 40px; max-width: 940px; }
.cookie-section h2, .document-section h2 { font-size: clamp(1.3rem, 2.6vw, 1.8rem); }
.cookie-section p, .document-section p { color: var(--muted); margin-top: 14px; }
.cookie-section .track-table-wrap { margin-top: 20px; }
/* Prose keeps a comfortable measure even though tables and lists may run wider. */
.document-section p, .document-section li { max-width: 78ch; }
.document-section li + li { margin-top: 8px; }
.cookie-list, .document-list { color: var(--muted); display: grid; gap: 8px; margin: 16px 0 0; padding-left: 22px; }
.cookie-list li::marker, .document-list li::marker { color: var(--coral); }
.document-section code { color: var(--cream); font: 500 .82rem var(--font-mono); }

/* Everything the project owner still has to supply before a public launch is
   marked in the text instead of being replaced with a plausible guess. */
.legal-placeholder { color: var(--coral-soft); font-style: italic; }
.facts--legal { border: 1px solid var(--line); border-radius: var(--radius-sm); margin-top: 18px; padding: 2px 18px 6px; }
.facts--legal dd { color: var(--muted); }
/* The inventory holds prose, so it keeps an automatic layout instead of the
   fixed column shares used by the track, catalog and task tables. */
.track-table--cookies { table-layout: auto; }
.track-table--cookies td { vertical-align: top; }
.track-table--cookies th:first-child, .track-table--cookies td:first-child { white-space: nowrap; }
.track-table--cookies code { color: var(--cream); font: 500 .82rem var(--font-mono); }

.sr-only { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }

/* --- buttons (loaded after buttons.css so the theme can refine it) ---- */
/* One look for every button and link-button (issue #32): a dark pill with a
   strong border, so a row never mixes a filled accent button with a dark one.
   The border is what keeps the shape readable on the dark page, and the coral
   moved from the resting fill to the hover and focus accent. The `--quiet`
   marker stays in the templates for secondary actions but no longer selects a
   different look. Three exceptions are deliberate: the PayPal button keeps its
   brand colours, the overlay play control on a cover keeps its own circle, and
   the donation invite keeps the warm gradient (below). */
button, .button {
    background: var(--ink);
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    color: var(--cream);
    cursor: pointer;
    display: inline-block;
    font: 800 .78rem var(--font-body);
    letter-spacing: .1em;
    padding: 13px 22px;
    text-decoration: none;
    text-transform: uppercase;
    transition: background .2s ease, border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
button:hover, button:focus-visible, .button:hover, .button:focus-visible {
    background: var(--coral);
    border-color: var(--coral);
    box-shadow: 0 12px 30px rgba(255, 77, 106, .35);
    color: #fff;
    transform: translateY(-2px);
}
button:disabled, button:disabled:hover, .button[aria-disabled="true"], .button[aria-disabled="true"]:hover {
    background: var(--surface-raised);
    color: var(--muted);
    cursor: not-allowed;
    opacity: .6;
    transform: none;
}

/* The donation invite is the one call to action on a page, so it wears the warm
   gradient every button carried before the action row was unified (issue #32):
   the coral-to-orange pill stands out among the dark ones again. Only the invite
   carries the modifier, so no other control changes, and the geometry stays the
   shared one (the rules above still set the size). */
.button--donate {
    background: linear-gradient(135deg, var(--coral), var(--orange));
    border-color: var(--orange);
    color: #fff;
}
.button--donate:hover, .button--donate:focus-visible {
    background: linear-gradient(135deg, var(--coral-light), var(--orange-light));
    border-color: var(--orange-light);
    box-shadow: 0 12px 30px rgba(255, 138, 92, .35);
    color: #fff;
}

/* --- donation option: "One time donation" frame with the PayPal button -- */
.donation-option {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    border-top: 4px solid var(--coral);
    box-shadow: var(--shadow);
    margin: 0 auto;
    max-width: 520px;
    padding: clamp(24px, 5vw, 38px);
    text-align: center;
}
.donation-option__title { font-size: clamp(1.5rem, 4vw, 2rem); letter-spacing: .05em; margin: 0 0 14px; }
.donation-option__text { color: var(--muted); font-size: .92rem; margin: 0 auto 24px; max-width: 42ch; }
.donation-option__form { margin: 0; }
/* Amount chooser of the PayPal.Me mode (issue #41): the same pill as every other
   control, with the radio inside it, so the donor picks with one tap and the
   choice stays legible without JavaScript. */
.donation-amounts { border: 0; display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin: 0 0 22px; padding: 0; }
.donation-amount {
    align-items: center;
    background: var(--surface-raised);
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    cursor: pointer;
    display: inline-flex;
    font: 800 .78rem var(--font-body);
    gap: 9px;
    letter-spacing: .06em;
    padding: 11px 18px;
}
.donation-amount:hover { border-color: var(--coral); }
.donation-amount input { accent-color: var(--coral); margin: 0; }
/* PayPal-branded button, modelled on krita.org/en/donations/: a white pill with
   the two-tone PayPal wordmark. Brand colours are fixed, not theme colours. */
.button--paypal {
    align-items: center;
    background: #fff;
    border: 1px solid var(--line-strong);
    color: #003087;
    display: inline-flex;
    font: 800 .84rem var(--font-body);
    gap: 5px;
    justify-content: center;
    letter-spacing: .05em;
    padding: 15px 28px;
    text-transform: none;
}
.button--paypal:hover, .button--paypal:focus-visible {
    background: #fff;
    border-color: #ffc439;
    box-shadow: 0 12px 30px rgba(255, 196, 57, .3);
    color: #003087;
    transform: translateY(-2px);
}
.button--paypal .pp-wordmark { font-style: italic; font-weight: 800; letter-spacing: .01em; }
.button--paypal .pp-wordmark span { color: #009cde; }
@media (prefers-reduced-motion: reduce) {
    .button--paypal:hover, .button--paypal:focus-visible { transform: none; }
}
/* Amount buttons of the business Payment Links mode (issue #48): one button per
   Payment Link, two per row, with the any-amount entry stretching across both
   columns. The columns are equal so the pairs line up, and each button fills its
   cell, which keeps the rows level whatever the label length. */
.donation-links {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin: 0 auto 22px;
    max-width: 460px;
}
.donation-links__wide { grid-column: 1 / -1; }
/* Narrower type than a lone button, because two pill labels share one row even on
   a phone; the shared pill geometry and the PayPal colours stay untouched. */
.donation-links .button--paypal {
    font-size: clamp(.68rem, 2.6vw, .78rem);
    padding: 14px 8px;
    width: 100%;
}

/* --- responsive ------------------------------------------------------- */
@media (max-width: 860px) {
    .hero { grid-template-columns: 1fr; }
    .site-nav { flex-wrap: wrap; gap: 12px 18px; }
    .site-nav__links, .site-nav__account { flex-wrap: wrap; }
    .site-footer__meta { margin-left: 0; }
}
@media (max-width: 640px) {
    /* The pinned catalog columns need ~540px of table width; below that they would not fit. */
    .track-table--catalog th:nth-child(n+2) { width: auto; }
    .track-table--catalog th:nth-child(n+4), .track-table--catalog td:nth-child(n+4) { white-space: normal; }
    /* The batch columns are pinned the same way (issue #56). */
    .track-table--batch th:nth-child(n+2) { width: auto; }
    .track-table--batch th:nth-child(n+2), .track-table--batch td:nth-child(n+2) { white-space: normal; }
}
@media (max-width: 560px) {
    .facts div { gap: 4px; grid-template-columns: 1fr; }
    .actions__end { margin-left: 0; }
    .catalog-controls { align-items: stretch; }
    /* The pinned task columns need ~470px of table width; below that they would not fit. */
    .track-table--tasks th:nth-child(n+2) { width: auto; }
    .track-table--tasks th:nth-child(n+3), .track-table--tasks td:nth-child(n+3) { white-space: normal; }
}

/* --- embedded Spotify players (issue #36) ------------------------------ */
/* A row is the 80 px box a player fills, and a player draws its own card inside
   that box. Spotify knows cards 80, 152, 232 and 352 px high, so an 80 px frame is
   filled by its compact card edge to edge (issue #35), with no empty remainder. The
   placeholder for an unmatched track mirrors that card — not
   the box — so the two line up on the same edge: the whole frame, 8 px of padding all round,
   12 px corners, a 64 px cover, a 16 px gap before the words and the compact type
   (14 px bold title, 11 px artist). The card is flat, because a player draws no
   shadow around it, only around the artwork. Tune the numbers here. */
.embed-player { border: 0; border-radius: 12px; display: block; max-width: 100%; width: 100%; }
.embed-list { display: grid; gap: 12px; }
.embed-row { align-items: start; display: grid; height: 80px; }
.embed-row__card { --embed-card-gap: 16px; --embed-card-height: 80px; --embed-card-inset: 8px; align-items: center; background: var(--embed-tint, #101010); border-radius: 12px; display: flex; gap: var(--embed-card-gap); height: var(--embed-card-height); padding: var(--embed-card-inset); }
.embed-row__cover { display: block; flex-shrink: 0; height: 64px; position: relative; width: 64px; }
.embed-row__cover img.cover { border-radius: 8px; height: 64px; object-fit: cover; width: 64px; }
.embed-row__meta { display: grid; min-width: 0; }
.embed-row__meta strong { font-size: .875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.embed-row__meta span { font-size: .6875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The status pill sits where a playing row keeps its controls. */
.embed-row__status { margin-left: auto; }
/* --- reduced motion --------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
    button:hover, .button:hover, .playlist-card:hover, .task-card:hover { transform: none; }
}




/* Identity login uses ordinary POST buttons with the site's focus styles. */
.social-login {
  display: grid;
  gap: 0.75rem;
  margin-block: 1.5rem;
}
.social-login form { margin: 0; }
.social-login__note { margin: 0 0 12px; font-size: 0.9rem; }
.social-login button { width: 100%; }

/* The entry screen keeps the site's palette and display type, with quiet
   provider choices and one prominent email action. */
.login-choice { max-width: 480px; padding: clamp(24px, 5vw, 40px); }
.login-choice .page-heading { text-align: center; margin-bottom: 30px; }
.login-choice .eyebrow { justify-content: center; font-size: .63rem; letter-spacing: .13em; }
.login-choice .eyebrow::before { display: none; }
.login-choice h1 { font-size: clamp(2.2rem, 7vw, 3rem); line-height: 1.05; }
.login-choice .lead { font-size: .9rem; line-height: 1.7; }
.login-choice .social-login { margin: 0; gap: 12px; }
/* The provider buttons are the site's own buttons with a brand mark on the left;
   the mark is sized to the button's type so it sits on the text's optical line
   (issue #50). */
.button--provider { align-items: center; display: inline-flex; gap: 9px; justify-content: center; }
.provider-logo { flex: 0 0 16px; height: 16px; width: 16px; }
.login-choice__divider { display: flex; align-items: center; gap: 18px; color: var(--muted); font: 500 .7rem var(--font-mono); margin: 26px 0; }
.login-choice__divider::before, .login-choice__divider::after { content: ""; height: 1px; background: var(--line-strong); flex: 1; }
.login-choice__form input[type="text"] { border-radius: 999px; min-height: 46px; padding-inline: 20px; }
.login-choice__form label { margin-left: 12px; }
.login-choice__form p { margin-bottom: 18px; }
/* The one prominent action of the identity screens: the site's button in the accent
   colour, filling the column like the provider buttons above it (issue #50). */
.button--primary { background: var(--coral); border-color: var(--coral); color: var(--ink); display: block; text-align: center; width: 100%; }
.button--primary:hover, .button--primary:focus-visible { background: var(--coral-soft); border-color: var(--coral-soft); color: var(--ink); }
.login-choice button:focus-visible { outline: 3px solid var(--cream); outline-offset: 3px; }
.login-choice__alternative { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 20px; text-align: center; margin-top: 22px; font-size: .8rem; }
.login-choice__alternative a { text-underline-offset: 4px; }
