/*
 * The custom CSS framework of the portal. Mobile first: the plain rules are the
 * phone layout, media queries only add on top of that for wider screens.
 *
 * Order: tokens, reset, base elements, layout, components.
 */

/* --- tokens ------------------------------------------------------------- */

/* Theme-independent tokens. The colours live in the theme blocks below. */
:root {
    /* Type scale. Every font-size in this file comes from here, so the scale
       can be retuned in one place when real branding arrives. */
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-ui: 0.9375rem;
    --text-base: 1rem;
    --text-md: 1.125rem;
    --text-lg: 1.25rem;
    --text-xl: 1.75rem;
    --text-2xl: 2.25rem;
    --text-3xl: 3.25rem;

    /* system-ui covers thai on every current phone and desktop; the named thai
       families are the fallback for older systems whose UI font has no thai
       glyphs, which would otherwise show boxes. All of them are installed
       fonts, nothing is downloaded. */
    --font-body: system-ui, -apple-system, "Segoe UI", Roboto,
        "Noto Sans Thai", "Sarabun", "Leelawadee UI", "Thonburi", sans-serif;

    /* Spacing steps, roughly doubling. Used for every margin/padding so that
       vertical rhythm stays consistent without one-off values. */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 1rem;
    --space-4: 1.5rem;
    --space-5: 2.5rem;
    --space-6: 4rem;

    --radius: 0.5rem;
    /* Fully round: pills and chips */
    --radius-full: 999px;
    --content-width: 68rem;
}

/* Breakpoints (media queries cannot read custom properties, so they are only
   documented here): 40rem phone→tablet, 48rem tablet→desktop, 64rem wide. */

/*
 * The two themes, selected by the data-theme attribute base.html puts on
 * <html>. Which one a page gets follows the *area* of the site, never the
 * user: the fan-facing portal (landing, auth, explore, profiles) is "studio",
 * dark and content-first; the manager area is "pro", light and plain. A future
 * rebrand is a value swap inside these two blocks and nothing else.
 *
 * Studio doubles as the :root default so a page without the attribute is
 * still styled. Every text-bearing pair below was contrast-checked to WCAG AA
 * (>= 4.5:1) — re-check when changing a value.
 */

:root,
[data-theme="studio"] {
    /* Native widgets (form controls, scrollbars, <audio>) follow the theme */
    color-scheme: dark;

    --color-bg: #131118;
    --color-bg-muted: #1a1722;
    /* Raised layers: cards, the header, toasts, the player dialog */
    --color-surface: #1c1924;
    --color-ink: #f2f0f7;
    --color-ink-muted: #a49cba;
    /* Stage magenta. Bright on dark, so filled elements (buttons, badges,
       active chips) carry dark text — see --color-accent-ink. */
    --color-accent: #ff5c8a;
    --color-accent-ink: #17141f;
    --color-accent-hover: #ff7da3;
    --color-border: #2e2a3d;
    /* Hover state of quiet borders (secondary buttons, chips, providers) */
    --color-border-strong: #4d4763;
    /* Status colours: message accents, form errors. Danger leans orange so it
       never reads as the magenta accent. */
    --color-success: #4ade80;
    --color-warning: #fbbf24;
    --color-danger: #ff8a5c;
    /* Behind the player dialog */
    --color-backdrop: rgba(0, 0, 0, 0.7);

    /* Elevation: 1 for resting cards, 2 for floating layers (toasts, dialog) */
    --shadow-1: 0 1px 3px rgba(0, 0, 0, 0.5);
    --shadow-2: 0 8px 24px rgba(0, 0, 0, 0.6);
}

[data-theme="pro"] {
    color-scheme: light;

    --color-bg: #ffffff;
    --color-bg-muted: #f4f2f7;
    --color-surface: #ffffff;
    --color-ink: #17141f;
    --color-ink-muted: #5b5470;
    /* The same magenta family as studio, deepened for a white page */
    --color-accent: #c2185b;
    --color-accent-ink: #ffffff;
    --color-accent-hover: #a3134c;
    --color-border: #ded9e8;
    --color-border-strong: #5b5470;
    --color-success: #2e7d32;
    --color-warning: #b26a00;
    --color-danger: #c62828;
    --color-backdrop: rgba(0, 0, 0, 0.55);

    --shadow-1: 0 1px 3px rgba(23, 20, 31, 0.12);
    --shadow-2: 0 8px 24px rgba(23, 20, 31, 0.18);
}

/* --- reset -------------------------------------------------------------- */

*,
*::before,
*::after {
    box-sizing: border-box;
}

body,
h1,
h2,
h3,
p,
ul,
figure {
    margin: 0;
}

ul {
    padding: 0;
    list-style: none;
}

img {
    display: block;
    max-width: 100%;
}

/* --- base --------------------------------------------------------------- */

html {
    /* Anchor links land below the sticky header instead of behind it */
    scroll-padding-top: 5rem;
}

body {
    background: var(--color-bg);
    color: var(--color-ink);
    font-family: var(--font-body);
    font-size: var(--text-base);
    line-height: 1.6;
    -webkit-text-size-adjust: 100%;
}

h1,
h2,
h3 {
    line-height: 1.2;
    text-wrap: balance;
}

h1 {
    font-size: var(--text-2xl);
}

h2 {
    font-size: var(--text-xl);
}

h3 {
    font-size: var(--text-md);
}

/* Thai stacks tone marks above the line and vowel signs below it, so a
   line-height that is comfortable for latin text makes them touch the line
   above. It also has no capitals and no spaces between words, which makes the
   same font-size read denser. The selector matches on the lang attribute
   (<html lang="th">, and any element that overrides it — the language switcher
   marks its own links), never on a css class, so it follows the language of the
   text and not the language of the page. Only line-height is touched: a
   font-size in em would compound on every nesting level. */
:lang(th) {
    line-height: 1.8;
}

h1:lang(th),
h2:lang(th),
h3:lang(th) {
    line-height: 1.45;
}

a {
    color: var(--color-accent);
}

:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

@media (min-width: 48rem) {
    h1 {
        font-size: var(--text-3xl);
    }

    h2 {
        font-size: var(--text-2xl);
    }
}

/* --- layout ------------------------------------------------------------- */

/* Horizontal frame for every page section */
.container {
    width: 100%;
    max-width: var(--content-width);
    margin: 0 auto;
    padding: 0 var(--space-3);
}

/* Vertical rhythm between the big blocks of a page */
.section {
    padding: var(--space-5) 0;
}

@media (min-width: 48rem) {
    .container {
        padding: 0 var(--space-4);
    }

    .section {
        padding: var(--space-6) 0;
    }
}

.section--muted {
    background: var(--color-bg-muted);
}

/* Row of call-to-action buttons below a block of text */
.actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-4);
}

/* Card grid that collapses to a single column on phones */
.grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: 1fr;
}

@media (min-width: 40rem) {
    .grid {
        gap: var(--space-4);
        grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    }
}

/* A grid as the body of a section, below the section intro */
.section .grid {
    margin-top: var(--space-4);
}

/* Keeps the footer at the bottom on short pages */
.page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.page__body {
    flex: 1;
}

/* --- header ------------------------------------------------------------- */

.site-header {
    position: sticky;
    top: 0;
    z-index: 10;
    /* Surface, not bg: a slight lift off the page it floats over */
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}

.site-header__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: 4rem;
}

.site-header__logo {
    color: inherit;
    font-size: var(--text-md);
    font-weight: 700;
    letter-spacing: -0.01em;
    text-decoration: none;
}

.site-nav {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.site-nav__link {
    color: var(--color-ink-muted);
    padding: var(--space-2);
    text-decoration: none;
}

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

/* Language switcher. Only the other language(s) are shown (see base.html), so
   this is one short link that has to stay legible without stealing room from
   the login buttons next to it. */
.site-nav__lang {
    color: var(--color-ink-muted);
    font-size: var(--text-ui);
    padding: var(--space-2) 0;
    text-decoration: none;
    white-space: nowrap;
}

.site-nav__lang:hover {
    color: var(--color-ink);
    text-decoration: underline;
}

/* The signed-in address is a nice-to-have, the phone header is too narrow */
.site-nav__user {
    display: none;
    color: var(--color-ink-muted);
    font-size: var(--text-ui);
}

@media (min-width: 40rem) {
    .site-nav__user {
        display: inline;
    }
}

/* --- subnav ------------------------------------------------------------- */

/* The bar below the header that base.html's subnav block renders into —
   filled by the per-artist management pages, empty (and absent) elsewhere. */

.subnav {
    background: var(--color-bg-muted);
    border-bottom: 1px solid var(--color-border);
}

.subnav__inner {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding-top: var(--space-2);
    padding-bottom: var(--space-2);
}

.subnav__title {
    font-weight: 600;
}

.subnav__link {
    color: var(--color-ink-muted);
    font-size: var(--text-ui);
    text-decoration: none;
}

.subnav__link:hover {
    color: var(--color-ink);
    text-decoration: underline;
}

/* --- manage sections ----------------------------------------------------- */

/* The frame of one artist's management pages: the section navigation
   (Profile / Content / Team / Fans) and the section body. One markup, two
   shapes — mobile first, a horizontal scrollable tab row above the content
   (the same gesture as the discipline chips and the profile's content
   tabs), and from 48rem a vertical sidebar beside it. */

.section-nav {
    display: flex;
    gap: var(--space-1);
    overflow-x: auto;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: var(--space-4);
}

.section-nav__link {
    display: block;
    padding: var(--space-2) var(--space-3);
    color: var(--color-ink-muted);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    /* The active marker's rail, so the row does not jump on selection */
    border-bottom: 2px solid transparent;
}

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

.section-nav__link--active {
    color: var(--color-accent);
    border-bottom-color: var(--color-accent);
}

@media (min-width: 48rem) {
    .manage-layout {
        display: grid;
        grid-template-columns: 11rem 1fr;
        gap: var(--space-5);
        align-items: start;
    }

    .section-nav {
        flex-direction: column;
        gap: 2px;
        overflow: visible;
        border-bottom: 0;
        margin-bottom: 0;
        /* Stays in reach on long lists; 5rem clears the sticky header, the
           same offset html's scroll-padding-top uses */
        position: sticky;
        top: 5rem;
    }

    .section-nav__link {
        border-bottom: 0;
        border-left: 2px solid transparent;
        border-radius: 0 var(--radius) var(--radius) 0;
    }

    .section-nav__link--active {
        color: var(--color-ink);
        background: var(--color-bg-muted);
        border-left-color: var(--color-accent);
    }

    .section-nav__link:hover {
        background: var(--color-bg-muted);
    }
}

/* Inside the section body the form column starts at the left edge instead
   of centering between sidebar and page edge */
.manage-layout .form {
    margin: 0;
}

/* The content listing of the Content section: one row per piece, the
   16:9 poster idea of the public cards shrunk to a hand-sized thumbnail */
.content-list__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--color-border);
}

.content-list__poster {
    flex: none;
    display: block;
    width: 4.5rem;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--color-bg-muted);
}

.content-list__text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.content-list__title {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.content-list__meta {
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
}

/* At the row's right edge, whatever length the title is — a ragged pill
   column reads as noise */
.content-list__premium {
    margin-left: auto;
}

/* The count beside the Fans heading — a fact in the muted voice */
.fans-count {
    color: var(--color-ink-muted);
    font-size: var(--text-md);
    font-weight: 400;
}

/* --- team --------------------------------------------------------------- */

/* The people behind an artist on its management page, one row each — and the
   pending invitations reuse the same rows, with the cancel form as a third
   cell. */

.team {
    margin-top: var(--space-2);
    margin-bottom: var(--space-5);
}

.team__row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--color-border);
}

.team__email {
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* Since-when on a fans row */
.team__since {
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
}

/* Paid-until (or expired-since) on a superfan's fans row */
.team__until {
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
}

/* Same quiet pill as .artist-card__level: a fact, not a badge */
.team__level,
.content-list__premium {
    padding: 0 var(--space-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    color: var(--color-ink-muted);
    font-size: var(--text-xs);
    font-weight: 600;
}

/* --- footer ------------------------------------------------------------- */

.site-footer {
    border-top: 1px solid var(--color-border);
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
    padding: var(--space-4) 0;
}

/* --- media credits ------------------------------------------------------ */

/* A few hundred short rows; columns keep the page from becoming a scroll
   marathon, and the column width lets the browser decide how many fit. */
.credits-list {
    columns: 16rem;
    column-gap: var(--space-4);
    margin-bottom: var(--space-5);
    font-size: var(--text-sm);
}

/* --- buttons ------------------------------------------------------------ */

.button {
    display: inline-block;
    border: 1px solid transparent;
    border-radius: var(--radius);
    font: inherit;
    font-weight: 600;
    /* Stays above the 44px touch target on phones */
    padding: 0.7rem var(--space-4);
    text-align: center;
    text-decoration: none;
    cursor: pointer;
}

.button--primary {
    background: var(--color-accent);
    color: var(--color-accent-ink);
}

.button--primary:hover {
    background: var(--color-accent-hover);
}

.button--secondary {
    background: transparent;
    border-color: var(--color-border);
    color: var(--color-ink);
}

.button--secondary:hover {
    border-color: var(--color-border-strong);
}

/* Header-sized button, sits next to the navigation links */
.button--small {
    font-size: var(--text-ui);
    padding: 0.4rem var(--space-3);
}

/* --- hero --------------------------------------------------------------- */

.hero {
    padding: var(--space-5) 0 var(--space-6);
}

.hero__lead {
    color: var(--color-ink-muted);
    font-size: var(--text-md);
    margin-top: var(--space-3);
    max-width: 38rem;
}

@media (min-width: 48rem) {
    .hero__lead {
        font-size: var(--text-lg);
    }
}

/* --- cards -------------------------------------------------------------- */

.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-4);
}

.card__text {
    color: var(--color-ink-muted);
    margin-top: var(--space-2);
}

/* --- artists ------------------------------------------------------------ */

/* The explore grid. Two columns on a phone rather than the one column .grid
   collapses to: these cards are a picture with a name under it, so they read
   fine narrow, and a browse page that shows two rows at a time instead of one
   is the difference between scanning and scrolling. */
.artist-grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(2, 1fr);
    margin-top: var(--space-4);
}

@media (min-width: 40rem) {
    .artist-grid {
        gap: var(--space-4);
        /* auto-fill, not auto-fit: a single artist should stay card-sized
           instead of stretching across the whole row */
        grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
    }
}

/* The whole card is the link, so the name and the text below the picture are
   part of the click target — the picture alone is a small target on a phone. */
.artist-card {
    display: block;
    color: inherit;
    text-decoration: none;
}

/* aspect-ratio holds the space before the picture has loaded, so the grid does
   not jump as the thumbnails arrive, and it does that whatever shape the
   original was. The background is what fills that space in the meantime. */
.artist-card__image {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--color-bg-muted);
}

.artist-card__name {
    display: block;
    margin-top: var(--space-2);
    font-weight: 600;
}

.artist-card:hover .artist-card__name {
    color: var(--color-accent);
}

.artist-card__text {
    display: block;
    margin-top: var(--space-1);
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
}

/* The access level on a my-artists card. A quiet pill, not the accent badge
   premium content wears: this states a fact about the account, it does not
   sell anything. */
.artist-card__level {
    display: inline-block;
    margin-top: var(--space-1);
    padding: 0 var(--space-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    color: var(--color-ink-muted);
    font-size: var(--text-xs);
    font-weight: 600;
}

/* The loader at the end of the grid is not a card, so it spans the whole row
   instead of one cell — a small box in the corner would read as a bug. htmx
   replaces it with the next page of cards when it scrolls into view; without
   javascript the link inside is the plain "next page" (see
   main/templates/main/_artist_cards.html). */
.artist-grid__loader {
    grid-column: 1 / -1;
    margin-top: var(--space-3);
    text-align: center;
}

/* The discipline filter of the explore grid. Pills rather than the underlined
   tabs of a profile (.content-tabs): switching a filter is a different
   gesture from switching a tab, and it should look like one. overflow-x for
   small phones — the whole taxonomy plus "All" outgrows a 320px screen, and
   scrolling the row beats wrapping it. */
.filter-chips {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-4);
    overflow-x: auto;
    /* The active pill's border is part of its height; keep the row from
       clipping the focus ring of the last chip */
    padding-bottom: var(--space-1);
}

.filter-chips__chip {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    color: var(--color-ink-muted);
    font-size: var(--text-ui);
    font-weight: 600;
    padding: var(--space-1) var(--space-3);
    text-decoration: none;
    white-space: nowrap;
}

.filter-chips__chip:hover {
    border-color: var(--color-border-strong);
    color: var(--color-ink);
}

.filter-chips__chip--active,
.filter-chips__chip--active:hover {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-accent-ink);
}

/* The explore page head: the title with the search beside it. On a phone the
   form wraps under the title and takes the full width. */
.explore-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.search {
    display: flex;
    gap: var(--space-2);
    flex: 1 1 100%;
}

@media (min-width: 40rem) {
    .search {
        flex: 0 1 24rem;
    }
}

.search__input {
    flex: 1;
    /* Text inputs refuse to shrink below their intrinsic width without this,
       which would push the button out of a narrow flex row */
    min-width: 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    color: inherit;
    font: inherit;
    padding: 0.4rem var(--space-3);
}

.search__input:focus {
    border-color: var(--color-accent);
}

.search__button {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    color: var(--color-ink);
    cursor: pointer;
    font: inherit;
    font-size: var(--text-ui);
    font-weight: 600;
    padding: 0.4rem var(--space-3);
}

.search__button:hover {
    border-color: var(--color-border-strong);
}

/* The "latest content" slideshow on the explore page: one poster at a time,
   filling the container, swiped through by finger. The mechanism is a plain
   scroll container with mandatory snapping — the browser does the sliding,
   swiping works with javascript off, and slideshow.js keeps the dots in step
   and turns the show on a timer until the user takes over. No scrollbar and
   no arrows: the dots are the position indicator. */
.slideshow {
    margin-top: var(--space-5);
}

.slideshow__track {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* A swipe past the last slide must not become a browser history swipe */
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    border-radius: var(--radius);
}

.slideshow__track::-webkit-scrollbar {
    display: none;
}

.slideshow__slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
}

.slideshow__card {
    position: relative;
    display: block;
    color: inherit;
    text-decoration: none;
}

/* Same 16:9 poster idea as .content-card__poster, with a lid on it: at full
   container width the untamed ratio would grow monumental on a desktop, so
   past ~42rem wide the picture crops to a band instead. The --empty modifier
   works as everywhere: no styles of its own, the base class is the box. */
.slideshow__poster {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 24rem;
    object-fit: cover;
    background: var(--color-bg-muted);
}

/* Title and artist live on the picture itself, over a scrim. A scrim on a
   photograph is dark in either theme, so its colors are deliberately not
   tokens — as is the text sitting on it. */
.slideshow__caption {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    padding: var(--space-5) var(--space-3) var(--space-3);
    background: linear-gradient(transparent, rgba(0, 0, 0, 0.75));
    color: #ffffff;
}

.slideshow__name,
.slideshow__artist {
    display: block;
    /* One line each; a slide is a teaser, the full title lives on the
       profile it links to */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.slideshow__name {
    font-size: var(--text-md);
    font-weight: 600;
}

.slideshow__artist {
    margin-top: var(--space-1);
    color: rgba(255, 255, 255, 0.8);
    font-size: var(--text-sm);
}

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

.slideshow__dots {
    display: flex;
    justify-content: center;
    gap: var(--space-1);
    margin-top: var(--space-2);
}

/* The visible dot is drawn by ::before; the button around it is bigger than
   what it draws, because it is a finger target */
.slideshow__dot {
    display: grid;
    place-items: center;
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
}

.slideshow__dot::before {
    content: "";
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--radius-full);
    background: var(--color-border-strong);
}

.slideshow__dot--active::before {
    background: var(--color-accent);
}

/* The "12 artists" line above a searched or filtered grid */
.artist-grid__meta {
    margin-top: var(--space-4);
    color: var(--color-ink-muted);
}

/* The picture above the text on a phone, beside it once there is room */
.artist-detail {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: 1fr;
    align-items: start;
}

@media (min-width: 48rem) {
    .artist-detail {
        grid-template-columns: 20rem 1fr;
    }
}

.artist-detail__image {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--color-bg-muted);
}

.artist-detail__text {
    color: var(--color-ink-muted);
    margin-top: var(--space-3);
}

/* The discipline under the artist's name — the same label the explore cards
   and filter chips use */
.artist-detail__discipline {
    color: var(--color-ink-muted);
    font-weight: 600;
    margin-top: var(--space-1);
}

/* The fan toggle under the description */
.artist-detail__fan {
    margin-top: var(--space-4);
}

/* The superfan status line ("You are a superfan until …") */
.artist-detail__superfan {
    color: var(--color-ink-muted);
    font-weight: 600;
    margin-top: var(--space-4);
}

/* The subscribe/renew button under it */
.artist-detail__subscribe {
    margin-top: var(--space-3);
}

/* The "are you an artist?" line under the my-artists list — a door, not a
   call to action, so it stays body-quiet */
.my-artists__create {
    color: var(--color-ink-muted);
    margin-top: var(--space-5);
}

/* --- content (the kind tabs on a profile) --------------------------------- */

/* The tabs are plain links (?kind=...) rendered server-side, so they work
   without javascript. overflow-x for small phones: three labels plus counts
   can outgrow a 320px screen, and scrolling the bar beats wrapping it. */
.content-tabs {
    display: flex;
    gap: var(--space-1);
    border-bottom: 1px solid var(--color-border);
    margin-top: var(--space-5);
    overflow-x: auto;
}

.content-tabs__tab {
    color: var(--color-ink-muted);
    font-weight: 600;
    padding: var(--space-2) var(--space-3);
    text-decoration: none;
    white-space: nowrap;
    /* The active tab underlines onto the bar's own border */
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

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

.content-tabs__tab--active {
    color: var(--color-accent);
    border-bottom-color: var(--color-accent);
}

.content-tabs__count {
    color: var(--color-ink-muted);
    font-weight: 400;
}

/* The cards sit in the generic .grid (one column on a phone, auto-fit above
   40rem). The whole card is the link when the user may open the content —
   same reasoning as .artist-card. */
.content-card {
    display: block;
    position: relative;
    color: inherit;
    text-decoration: none;
}

/* 16:9: posters are the teaser image of a video/audio track, that is the
   shape they come in. aspect-ratio also holds the space before the picture
   arrives, so the grid does not jump. */
.content-card__poster {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--color-bg-muted);
}

/* A piece without a poster gets the same box via the --empty modifier in the
   markup, which carries no styles of its own: .content-card__poster above
   already gives it shape and background, and there is no broken-image icon */

.content-card__title {
    display: block;
    margin-top: var(--space-2);
    font-weight: 600;
}

/* Only a card that actually links somewhere gets the hover colour — an <a>
   without href (a locked premium card) never matches this */
a.content-card:hover .content-card__title {
    color: var(--color-accent);
}

/* The premium marker, overlaid on the poster's corner */
.content-card__badge {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    background: var(--color-accent);
    color: var(--color-accent-ink);
    border-radius: var(--radius);
    font-size: var(--text-xs);
    font-weight: 600;
    padding: var(--space-1) var(--space-2);
}

/* The overlay player (main/js/content-player.js). A native <dialog>: open it
   with showModal() and the browser centers it, traps focus and wires Esc —
   none of that is ours to reimplement. A surface of the theme it opens in,
   over a darkened backdrop. */
.content-player {
    width: min(56rem, 100vw - 2 * var(--space-3));
    border: none;
    border-radius: var(--radius);
    padding: var(--space-3);
    background: var(--color-surface);
    box-shadow: var(--shadow-2);
    color: var(--color-ink);
}

.content-player::backdrop {
    background: var(--color-backdrop);
}

.content-player__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.content-player__title {
    font-size: var(--text-md);
}

.content-player__close {
    border: none;
    background: none;
    color: var(--color-ink-muted);
    font-size: 1.5rem;
    line-height: 1;
    padding: var(--space-1) var(--space-2);
    cursor: pointer;
}

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

/* 75dvh, not vh: on a phone the browser chrome comes out of vh and the media
   would sit under the controls. object-fit keeps a portrait image or an odd
   video inside the box without cropping. */
.content-player__stage img,
.content-player__stage video {
    display: block;
    width: 100%;
    max-height: 75dvh;
    object-fit: contain;
    background: var(--color-bg-muted);
    border-radius: var(--radius);
}

.content-player__stage audio {
    display: block;
    width: 100%;
    margin-top: var(--space-2);
}


/* --- auth pages and plain forms ------------------------------------------ */

/* Allauth renders plain semantic markup (form > p > label + input, button,
   ul > li > a for the providers), so the pages are styled by element inside
   this one wrapper instead of by overriding every allauth template. Our own
   form pages (.form — profile creation, the invite form) emit the same
   as_p markup, so they share every rule; only the provider list below stays
   allauth's alone. */

.auth,
.form {
    max-width: 32rem;
    margin: 0 auto;
}

/* The "you are accepting an invitation" note above the login/signup forms —
   informational, so bordered like the level pills, not accent-filled */
.auth__invitation {
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-accent);
    border-radius: var(--radius);
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-4);
}

.auth h1,
.form h1 {
    font-size: var(--text-xl);
}

.auth form,
.form form {
    margin-top: var(--space-4);
}

.auth form p,
.form form p {
    margin-bottom: var(--space-3);
}

.auth label,
.form label {
    display: block;
    font-weight: 600;
    margin-bottom: var(--space-1);
}

.auth input,
.form input,
.form textarea,
.form select {
    width: 100%;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    color: inherit;
    font: inherit;
    padding: 0.6rem var(--space-2);
}

.auth input:focus,
.form input:focus,
.form textarea:focus,
.form select:focus {
    border-color: var(--color-accent);
}

/* The description box should invite more than one line */
.form textarea {
    min-height: 8rem;
    resize: vertical;
}

/* The file input is a button-opening control, not a text box */
.form input[type="file"] {
    padding: var(--space-2);
}

/* The current picture on the profile edit page, shown as a picture rather
   than as ClearableFileInput's "Currently:" link (see ArtistProfileForm) */
.form__picture {
    display: block;
    width: 10rem;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--color-bg-muted);
    margin-top: var(--space-4);
}

/* Checkboxes ("remember me") keep their label on the same line */
.auth input[type="checkbox"],
.auth input[type="radio"],
.form input[type="checkbox"],
.form input[type="radio"] {
    width: auto;
    margin-right: var(--space-2);
}

.auth input[type="checkbox"] + label,
.auth input[type="radio"] + label,
.form input[type="checkbox"] + label,
.form input[type="radio"] + label {
    display: inline;
    font-weight: 400;
}

.auth button,
.form button {
    display: inline-block;
    background: var(--color-accent);
    border: 1px solid transparent;
    border-radius: var(--radius);
    color: var(--color-accent-ink);
    font: inherit;
    font-weight: 600;
    padding: 0.7rem var(--space-4);
    cursor: pointer;
}

.auth button:hover,
.form button:hover {
    background: var(--color-accent-hover);
}

.auth .helptext,
.auth form span,
.form .helptext,
.form form span {
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
}

.auth .errorlist,
.form .errorlist {
    color: var(--color-danger);
    font-size: var(--text-sm);
    margin-bottom: var(--space-1);
}

/* Social provider list ("Sign in with ...") */
.auth .providers {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

.auth .providers a {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    color: var(--color-ink);
    display: block;
    font-weight: 600;
    padding: 0.7rem var(--space-4);
    text-align: center;
    text-decoration: none;
}

.auth .providers a:hover {
    border-color: var(--color-border-strong);
}

/* --- payment ------------------------------------------------------------- */

/* One superfan payment: heading, instructions, the QR, the state line. A
   narrow single column — the page has exactly one thing to do. */

.payment {
    max-width: 32rem;
}

.payment__status {
    margin-top: var(--space-3);
}

/* The QR sits on literal white, whatever the theme: a scanner wants dark
   modules on a light ground, and on the studio theme's dark background an
   unpadded QR is unreadable to a camera. Theme-independent by physics, so
   deliberately not a token. */
.payment__qr {
    display: inline-block;
    background: #ffffff;
    padding: var(--space-3);
    border-radius: var(--radius);
    margin: var(--space-3) 0;
}

.payment__qr img {
    display: block;
    width: 14rem;
    max-width: 100%;
}

.payment__hint {
    color: var(--color-ink-muted);
    font-size: var(--text-sm);
    margin-bottom: var(--space-4);
}

/* --- messages ----------------------------------------------------------- */

/* The overlay base.html renders django messages into: fixed above the page
   instead of a block inside it, so any page can show one without making room
   in its layout. On a phone it spans the width, from tablet width up it is a
   column in the top right corner. */

.messages {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    position: fixed;
    top: var(--space-2);
    left: var(--space-2);
    right: var(--space-2);
    /* Above the sticky header (10) and the focused skip link (20) */
    z-index: 30;
}

@media (min-width: 40rem) {
    .messages {
        left: auto;
        width: 24rem;
    }
}

.message {
    align-items: baseline;
    animation: message-slide-in 0.25s ease-out;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-left-width: 4px;
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
    display: flex;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    transition: opacity 0.4s, transform 0.4s;
}

/* Set by the dismiss script in base.html just before it removes the element */
.message--leaving {
    opacity: 0;
    transform: translateY(-0.5rem);
}

@keyframes message-slide-in {
    from {
        opacity: 0;
        transform: translateY(-0.5rem);
    }
}

@media (prefers-reduced-motion: reduce) {
    .message {
        animation: none;
        transition: none;
    }
}

.message__text {
    flex: 1;
}

.message__dismiss {
    background: none;
    border: 0;
    color: var(--color-ink-muted);
    cursor: pointer;
    font-size: 1.25rem;
    line-height: 1;
    padding: 0 var(--space-1);
}

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

.message--success {
    border-left-color: var(--color-success);
}

.message--warning {
    border-left-color: var(--color-warning);
}

.message--error {
    border-left-color: var(--color-danger);
}

/* --- misc --------------------------------------------------------------- */

.lead {
    color: var(--color-ink-muted);
    margin-top: var(--space-3);
    max-width: 42rem;
}

/* Visible only once it receives keyboard focus */
.skip-link {
    position: absolute;
    left: var(--space-3);
    top: -4rem;
    background: var(--color-accent);
    color: var(--color-accent-ink);
    border-radius: var(--radius);
    padding: var(--space-2) var(--space-3);
    z-index: 20;
}

.skip-link:focus {
    top: var(--space-2);
}
