/* ==========================================================================
   Concert Manager — components that are genuinely specific to this app.
   ==========================================================================

   The layout primitives, cards, tables, buttons, menus, stat tiles, badges,
   forms and flashes all come from `shared/ui`
   (ui/{tokens,system}.css); the app's hue, radii and type come from
   `theme.css`. What is left here is Concert Manager's own furniture: the home
   hero, the statistics visualisations, the setlist editors and the band modal.

   Where a rule below only adjusts density — how wide a row of tiles wants to
   be, say — it sets the shared token (`--tile-min`) rather than restating the
   rule it came from.

   Same rule as the shared package: reference tokens, never raw values. The
   only literals below are in the app-token block, which is where colour is
   defined rather than used.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. App tokens
   -------------------------------------------------------------------------- */
:root {
    /* Domain colours. These encode *what a thing is*, not how it looks, so
       they live here rather than in the shared token layer: a concert is
       always violet and a festival is always amber, in every chart, badge and
       legend on the site. Never swap them. */
    --concert: light-dark(#7c3aed, #a78bfa);
    --concert-soft: light-dark(#f1eafd, #2a2145);
    --festival: light-dark(#d97706, #f0a33f);
    --festival-soft: light-dark(#fdf3e3, #3a2e14);
    /* Light --festival as badge text on its own tint is 4.1:1; this keeps 4.5:1. */
    --festival-ink: light-dark(#c06505, #f0a33f);

    /* The third domain colour: a radar hit, a show that is not yet planned. */
    --radar: light-dark(#0891b2, #1aa3bb);
    --radar-soft: light-dark(#e1f4f8, #12303a);
    --radar-ink: light-dark(#0b6a80, #86d6e5);

    /* Stage surfaces stay dark in both modes: covers are lit rooms, not cards. */
    --stage: light-dark(#17122a, #0f0c1c);
    --stage-ink: light-dark(#ffffff, #f4f1fb);

    /* A single-series chart carries no category, so it gets a neutral rather
       than borrowing one of the two domain colours. */
    --series-neutral: light-dark(#79719c, #8079a6);
    --series-track: light-dark(#eeebf5, #2a2640);

    /* Activity tile map: one hue, four steps of the domain violet. */
    --activity-0: var(--series-track);
    --activity-1: light-dark(#d6c4f7, #3d2a63);
    --activity-2: light-dark(#a97ded, #5b3aa0);
    --activity-3: light-dark(#7c3aed, #a78bfa);
}

/* --------------------------------------------------------------------------
   2. Page frame
   -------------------------------------------------------------------------- */
h4, h5, h6 { font-family: var(--font-display); }

h3 { font-size: 0.95rem; font-weight: 650; margin: 0; }

/* --------------------------------------------------------------------------
   4. Page head
   -------------------------------------------------------------------------- */
/* A page head here can carry a two-line title cluster and a back button, so it
   aligns on the top edge rather than shared/ui's centre. */
.page-head { align-items: flex-start; }
/* The back button is `ui:PageHead`'s lead, a sibling of the titles: keep the
   titles beside it instead of letting the head's space-between centre them. */
.page-head > :is(.btn-back, .btn-back-link) + .page-titles { margin-right: auto; }

/* Round "back" affordance, left of the page title. */
.btn-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    background: var(--card);
    color: var(--muted);
    text-decoration: none;
}
.btn-back:hover { background: var(--primary-soft); color: var(--primary-ink); }

/* …and its labelled form, a plain link above the title. */
.btn-back-link {
    display: inline-flex;
    align-items: center;
    min-height: 1.5rem;
    gap: 0.3rem;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--muted);
    text-decoration: none;
    white-space: nowrap;
}
.btn-back-link:hover { color: var(--primary); }

/* --------------------------------------------------------------------------
   5. Text
   -------------------------------------------------------------------------- */
/* Secondary line under a primary one — the single most common piece of text
   in the app, and the reason `text-muted`/`small` appeared 190 times. */
.sub { color: var(--muted); font-size: 0.85rem; }
.name { font-weight: 600; color: var(--text); }
/* A rating is always shown as a filled star; the colour is the warn token
   because that is what an amber star already means everywhere else. */
.rating-star { color: var(--warn); }
.tag-row { gap: 0.3rem; margin-bottom: 0.5rem; }
a.plain { color: inherit; text-decoration: none; }
a.plain:hover { color: var(--primary); }

/* --------------------------------------------------------------------------
   6. Cards
   -------------------------------------------------------------------------- */
/* shared/ui ships `.card-foot`; this is its counterpart at the top. Same trick:
   cancel the card's own padding with negative margins to reach the edges, which
   is why that padding is a token. */
.card-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: calc(-1 * var(--card-pad-y)) calc(-1 * var(--card-pad-x)) 1rem;
    padding: 0.8rem var(--card-pad-x);
    border-bottom: 1px solid var(--border);
    background: var(--card-2);
    border-radius: var(--r-l) var(--r-l) 0 0;
    font-family: var(--font-display);
    font-weight: 650;
    font-size: 0.95rem;
}
.card-head .ui-icon { color: var(--primary); }
.card-head .actions { margin-left: auto; }

/* Cards in a grid all stretch to the tallest one, so a foot has to be pushed
   to the bottom rather than sitting directly under short content. */
.cards > .card { display: flex; flex-direction: column; }
.cards > .card > .card-foot { margin-top: auto; }

/* The thing a one-at-a-time workflow page is currently about. */
.subject-name { display: flex; align-items: center; gap: 0.5rem; font-size: 1.15rem; }
.subject-head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem; }
.subject-head h2 { margin: 0 0 0.35rem; font-size: 1.25rem; }

/* Lift on hover for the card grids on the concert and festival lists. */
.card-hover { transition: transform 0.12s ease, box-shadow 0.12s ease; }
.card-hover:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }

/* The login and register cards close with a single centred line of prose. */
.login-card .card-foot { text-align: center; }
.login-card .login-brand h1 { margin: 0; }
.login-card .login-brand .sub { margin: 0.15rem 0 0; }

/* A card whose content reaches its own edges — a table, usually. The head and
   foot stop pulling outwards because there is no padding left to escape. */
.card-flush { padding: 0; overflow: hidden; }
.card-flush .card-head { margin: 0; border-radius: var(--r-l) var(--r-l) 0 0; }
.card-flush .card-foot { margin: 0; }
.card-flush .grid { border: none; border-radius: 0; box-shadow: none; }

/* --------------------------------------------------------------------------
   7. Tables
   -------------------------------------------------------------------------- */
/* Column alignment. shared/ui's .grid left-aligns everything; counts read
   better centred and actions belong at the trailing edge. */
.grid .center { text-align: center; }
.grid .right { text-align: right; }
.grid td.actions-cell { text-align: right; white-space: nowrap; }
.grid td.actions-cell form { display: inline; }
.grid tbody tr:hover { background: var(--card-2); }
/* A row that is present but will not be acted on — an import duplicate. */
.grid tr.row-muted td { color: var(--muted); }
.grid .thumb-cell { display: flex; align-items: center; gap: 0.6rem; }

/* Sortable column header: a link that has to sit inside .grid th's muted,
   uppercase treatment rather than fight it. */
.sort-link {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: inherit;
    text-decoration: none;
}
.sort-link:hover { color: var(--text); }
.sort-link .ui-icon { opacity: 0.5; font-size: 0.75em; }
.sort-link.is-sorted .ui-icon { opacity: 1; color: var(--primary); }

/* Bands and venues: a row per entry with the user's own counts. The counts keep their
   labels, so on a phone they sit under the name instead of in columns that run off-screen. */
.entity-sort { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; margin-bottom: 0.75rem; }
.entity-sort .sort-link.is-sorted .ui-icon { color: inherit; }
.entity-list { list-style: none; margin: 0; padding: 0; }
.entity-row {
    display: grid;
    grid-template-columns: 3rem minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.5rem 0.9rem;
    padding: 0.65rem var(--card-pad-x, 1rem);
    border-bottom: 1px solid var(--border);
}
.entity-row.has-actions { grid-template-columns: 3rem minmax(0, 1fr) auto auto; }
.entity-lead { display: block; line-height: 0; }
.entity-lead--venue {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: var(--r-m);
    background: var(--radar-soft);
    color: var(--radar);
    font-size: 1.25rem;
}
.entity-main { display: grid; gap: 0.1rem; min-width: 0; }
.entity-main .name { font-weight: 600; overflow-wrap: anywhere; }
.entity-coords { justify-self: start; margin-top: 0.15rem; }
.entity-counts { display: flex; gap: 1.25rem; margin: 0; }
.entity-counts div { display: grid; justify-items: end; min-width: 3.5rem; }
.entity-counts dt { font-size: 0.72rem; color: var(--muted); }
.entity-counts dd { margin: 0; font-weight: 700; }
.entity-counts dd small { font-weight: 600; color: var(--muted); }
.entity-counts .is-total dd { color: var(--primary-ink); }
.entity-actions { display: flex; gap: 0.35rem; }
.entity-actions form { margin: 0; }
@media (max-width: 40rem) {
    .entity-row,
    .entity-row.has-actions { grid-template-columns: 3rem minmax(0, 1fr) auto; }
    .entity-counts { grid-column: 2 / -1; gap: 1rem; }
    .entity-counts div { justify-items: start; min-width: 0; grid-auto-flow: column; align-items: baseline; gap: 0.35rem; }
    .entity-row.has-actions .entity-actions { grid-column: 3; grid-row: 1; }
}

/* shared/ui's `dl` is a figure list: label left, number right. A detail panel
   is the other kind — a label column and prose that has to read left to right. */
.detail-list { grid-template-columns: minmax(6rem, auto) minmax(0, 1fr); gap: 0.5rem 1.5rem; }
.detail-list dd { text-align: left; }

/* --------------------------------------------------------------------------
   8. Lists
   -------------------------------------------------------------------------- */
.list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.list > li,
.list > a {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.7rem 0;
    border-bottom: 1px solid var(--border);
    color: inherit;
    text-decoration: none;
}
.list > li:last-child,
.list > a:last-child { border-bottom: none; }
.list > a:hover { color: var(--primary); }
.list .list-meta { margin-left: auto; text-align: right; }
/* A row that is several stacked lines rather than one — needs to beat the
   single-line flex above, hence the doubled-up selector. */
.list > li.stack-tight { display: grid; align-items: stretch; }

/* A list filling a .card-flush has no card padding to sit in, so it brings
   its own — otherwise the rows run into the card's edges. */
.list-inset > li,
.list-inset > a { padding-inline: var(--card-pad-x); }
.empty-line { padding: 1.5rem; text-align: center; margin: 0; }
/* Prose inside a .card-flush, which has no padding of its own. */
.column-hint { padding: 0 var(--card-pad-x) 0.75rem; margin: 0; }

/* A festival's acts are grouped under the day they played. */
.list > li.day-row {
    background: var(--card-2);
    margin: 0 calc(-1 * var(--card-pad-x));
    padding: 0.4rem var(--card-pad-x);
}

/* A band's setlist as a printed running order: ruled lines, numbers in the
   headline face, and the encore after a ruled break rather than a badge. */
.running-order { margin-bottom: 0.75rem; }
.running-order-count {
    margin: 0 0 0.35rem;
    font-size: var(--step--1);
    font-weight: 600;
    color: var(--muted);
}
.running-order-list {
    list-style: none;
    margin: 0;
    padding: 0;
    border-top: 2px solid var(--text);
}
.running-order-song {
    display: flex;
    align-items: baseline;
    gap: 0.85rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--border);
}
.running-order-pos {
    flex: none;
    min-width: 1.5rem;
    font-family: var(--font-display);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--muted);
}
.running-order-title { min-width: 0; overflow-wrap: anywhere; }
.tip-pick { display: inline-flex; align-items: center; gap: 0.5rem; cursor: pointer; }
.setlist-scorecard { margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--border); }
.setlist-scorecard p { margin: 0 0 0.25rem; }
/* The forecast numbers its rows "10/10", which needs a wider column than a position. */
.setlist-forecast .running-order-pos { min-width: 2.6rem; text-align: right; }
.forecast-marks {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.5rem;
    margin-left: 0.5rem;
    white-space: nowrap;
}
.running-order-break {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1rem 0 0.4rem;
    border-bottom: 2px solid var(--text);
    font-family: var(--font-display);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--primary-ink);
}

/* The sheet is the setlist printed as on stage: two columns on a wide screen,
   with the encore ruled off across both. */
.setlist-sheet .running-order-list { display: block; }
@media (min-width: 1100px) {
    .setlist-sheet .running-order-list { columns: 2; column-gap: 2.5rem; }
    .setlist-sheet .running-order-song { break-inside: avoid; }
    .setlist-sheet .running-order-break { column-span: all; }
}
.setlist-sheet .running-order-song { align-items: baseline; }
.sheet-marks {
    margin-left: auto;
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    white-space: nowrap;
    font-size: var(--step--1);
    color: var(--muted);
}
.sheet-mark { line-height: 1.4; }
.sheet-mark--first {
    padding: 0 0.35rem;
    border: 1px solid var(--concert);
    border-radius: var(--r-s);
    color: var(--primary-ink);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.goosebumps-form { display: inline-flex; margin: 0; }
.goosebumps-toggle {
    padding: 0.1rem 0.25rem;
    border: 0;
    background: none;
    color: var(--muted);
    cursor: pointer;
    line-height: 1;
}
.goosebumps-toggle:hover,
.goosebumps-toggle[aria-pressed="true"] { color: var(--neg); }
.is-goosebumps .running-order-title { font-weight: 600; }
.goosebumps-reel { list-style: none; margin: 0; padding: 0; }
.goosebumps-reel li {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--border);
}
.goosebumps-reel li:last-child { border-bottom: 0; }
.goosebumps-reel-mark { color: var(--neg); }
.goosebumps-reel-song { display: flex; flex-direction: column; min-width: 0; }
.goosebumps-reel-show { margin-left: auto; text-align: right; }
.sheet-mark--favourite { color: var(--concert); }
.sheet-mark--plays { font-variant-numeric: tabular-nums; }

/* Forecast: the share of recent shows a song was played at, as a bar. */
.forecast-bar {
    flex: none;
    width: 4.5rem;
    height: 0.5rem;
    align-self: center;
    border-radius: 999px;
    background: var(--concert-soft);
    overflow: hidden;
}
.forecast-bar > i { display: block; height: 100%; background: var(--concert); border-radius: inherit; }

/* The show page: one column of sections under the cover. */
.show-sections { display: grid; gap: var(--gap-section); align-content: start; }
.show-day { margin: 0.5rem 0 -0.5rem; font-size: var(--step-1); }
.show-act > .running-order { margin-bottom: 0; }
.show-cover-actions {
    position: absolute;
    top: 1rem;
    right: 1rem;
    display: flex;
    gap: 0.4rem;
    align-items: center;
}
.btn-glass {
    background: color-mix(in srgb, var(--stage-ink) 14%, transparent);
    border-color: color-mix(in srgb, var(--stage-ink) 35%, transparent);
    color: var(--stage-ink);
}
.btn-glass:hover { background: color-mix(in srgb, var(--stage-ink) 24%, transparent); color: var(--stage-ink); }
.show-cover-menu > summary { list-style: none; cursor: pointer; padding: 0.3rem 0.5rem; }
.menu-list .menu-danger { color: var(--neg); }
.menu-list form { margin: 0; }

/* A band or act as a chip: its name, the controls that belong to it and a way out. */
.entry-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem; }
.entry-chip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0.25rem 0.4rem 0.25rem 0.75rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-m);
    background: var(--card-2);
}
.entry-chip input.band-search {
    width: 11rem;
    max-width: 100%;
    padding: 0.2rem 0.3rem;
    border: none;
    border-bottom: 1px dashed var(--border-strong);
    border-radius: 0;
    background: transparent;
    font: inherit;
}
.entry-chip .day-input[hidden] { display: none; }
.chip-toggle { position: relative; display: inline-grid; place-items: center; width: 1.8rem; height: 1.8rem; border-radius: 50%; color: var(--muted); cursor: pointer; }
.chip-toggle input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.chip-toggle:has(input:checked) { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
.chip-toggle:has(input:focus-visible) { outline: 2px solid var(--primary); outline-offset: 1px; }
.chip-remove {
    display: inline-grid;
    place-items: center;
    width: 1.8rem;
    height: 1.8rem;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
}
.chip-remove:hover { background: color-mix(in srgb, var(--neg) 14%, transparent); color: var(--neg); }
.chip-day {
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    color: var(--text-2);
    font: inherit;
    font-size: 0.8rem;
    cursor: pointer;
}
.chip-day[aria-pressed="true"] { background: var(--primary); border-color: var(--primary); color: var(--on-primary); font-weight: 600; }
.day-chips { gap: 0.25rem; }

.venue-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.4rem 0.3rem 0.8rem;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--card-2);
    font-weight: 500;
}
.field-full[hidden], .form-grid > div[hidden] { display: none; }

/* Mutually exclusive options as chips: the radio hides, its label is the chip. */
.chip-choice { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip-choice-item { position: relative; }
.chip-choice-item input {
    position: absolute;
    inset: 0;
    opacity: 0;
    margin: 0;
    cursor: pointer;
}
.chip-choice-item label {
    display: inline-block;
    margin: 0;
    padding: 0.3rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    color: var(--text-2);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
}
.chip-choice-item input:hover + label { border-color: var(--border-strong); color: var(--text); }
.chip-choice-item input:checked + label {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
    font-weight: 600;
}
.chip-choice-item input:focus-visible + label { outline: 2px solid var(--primary); outline-offset: 2px; }

/* A drop zone over a file input; the input fills it so a click still opens the picker. */
.dropzone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
    padding: 1.25rem 1rem;
    border: 2px dashed var(--border-strong);
    border-radius: var(--r-m);
    color: var(--text-2);
    text-align: center;
    cursor: pointer;
}
.dropzone:hover,
.dropzone.is-over { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-ink); }
.dropzone:focus-within { outline: 2px solid var(--primary); outline-offset: 2px; }
.dropzone-label { font-weight: 600; }
.dropzone-hint { font-size: var(--step--1); }
.dropzone-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }

/* "After the show": what is still open, with the way to close it. */
.check { list-style: none; margin: 0; padding: 0; }
.check li { display: flex; align-items: center; gap: 0.75rem; padding: 0.55rem 0; border-bottom: 1px solid var(--border); }
.check li:last-child { border-bottom: none; }
.check .tick {
    flex: none;
    display: inline-grid;
    place-items: center;
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    border: 1.5px dashed var(--border-strong);
    font-size: 0.8rem;
}
.check .done .tick { border: none; background: color-mix(in srgb, var(--pos) 16%, transparent); color: var(--pos); }
.check .open .grow { font-weight: 600; }
.check .done .grow { color: var(--muted); }

/* --------------------------------------------------------------------------
   9. Stat tiles
   -------------------------------------------------------------------------- */
/* The tiles themselves come from shared/ui: `.tile` is the centred figure the
   import preview uses, `.tile tile-lead` the icon-beside-figure row everywhere
   else. What is left here is how wide a particular row of them wants to be,
   which is one token per row rather than a copy of the grid. */


/* One attended or upcoming show, as a line in a dashboard card. */
.show-row {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border);
}
.show-row:last-child { border-bottom: none; }
.show-date { min-width: 5rem; color: var(--muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.show-main { flex: 1; min-width: 0; overflow: hidden; }

/* The next show as a ticket: a date stub behind a perforation, the bill beside
   it. The outer element is the link and the query container, since a container
   can't query itself; the inner one carries the surface with the notches masked
   out. No shadow: it would fill the notches back in. */
.ticket-stub {
    display: block;
    container-type: inline-size;
    color: inherit;
    text-decoration: none;
    --stub-soft: var(--concert-soft);
    --stub-ink: var(--primary-ink);
}
.ticket-stub--festival {
    --stub-soft: var(--festival-soft);
    --stub-ink: var(--warn);
}
.page-stub { margin-bottom: var(--gap-section); }
.ticket-stub-ticket {
    --stub-w: 7rem;
    --notch: 0.85rem;
    --perforation: calc(var(--stub-w) - 1px);
    display: flex;
    overflow: clip;
    background: var(--card);
    border-radius: var(--r-l);
    box-shadow: inset 0 0 0 1px var(--border);
    /* The mask reads alpha only; currentColor is just "opaque". */
    mask-image:
        radial-gradient(circle at var(--perforation) 0, transparent var(--notch), currentColor var(--notch)),
        radial-gradient(circle at var(--perforation) 100%, transparent var(--notch), currentColor var(--notch));
    mask-composite: intersect;
}
.ticket-stub-date {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: var(--stub-w);
    padding: 1rem 0.5rem;
    border-right: 2px dashed var(--border-strong);
    background: var(--stub-soft);
    color: var(--stub-ink);
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.1;
    text-align: center;
    text-transform: uppercase;
}
.ticket-stub-weekday,
.ticket-stub-month { font-size: var(--step--1); letter-spacing: 0.08em; }
.ticket-stub-day {
    font-size: var(--step-5);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: var(--text);
}
.ticket-stub-day--range { font-size: var(--step-3); padding-block: 0.2em; }
.ticket-stub-year { margin-top: 0.3rem; font-size: var(--step--1); font-variant-numeric: tabular-nums; color: var(--muted); }
.ticket-stub-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.35rem;
    padding: 1.1rem 1.4rem 1.2rem 1.6rem;
}
.ticket-stub-countdown {
    padding: 0.1rem 0.55rem;
    border-radius: var(--r-s);
    background: var(--stub-soft);
    color: var(--stub-ink);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.ticket-stub-title {
    font-family: var(--font-display);
    font-size: var(--step-4);
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.01em;
    overflow-wrap: anywhere;
    text-wrap: balance;
}
a.ticket-stub:hover .ticket-stub-title { color: var(--primary); }
.ticket-stub-support {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    color: var(--text-2);
}
.ticket-stub-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 1rem;
    margin-top: 0.35rem;
    font-size: 0.85rem;
    color: var(--muted);
}
.ticket-stub-time { font-variant-numeric: tabular-nums; }
@container (max-width: 30rem) {
    .ticket-stub-ticket { --stub-w: 5rem; --notch: 0.65rem; }
    .ticket-stub-day { font-size: var(--step-4); }
    .ticket-stub-day--range { font-size: var(--step-1); }
    .ticket-stub-body { padding: 0.9rem 1rem 1rem 1.15rem; }
    .ticket-stub-title { font-size: var(--step-2); }
}

/* The "on this day" flash carries structure rather than one line of text. */
.on-this-day { align-items: flex-start; }
.on-this-day a { color: inherit; }

/* Each "on this day" line is a band thumbnail, the acts and place as running
   text, and the link to the show pinned to the right. */
.otd-list { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.35rem; }
.otd-item { display: flex; align-items: center; gap: 0.6rem; }
.otd-text { flex: 1; min-width: 0; }
.otd-open { flex: none; }
/* The flash makes every link inherit its own colour, so the band and venue
   links inside the text need the underline to read as links at all. */
.otd-text a { text-decoration: underline; text-underline-offset: 0.15em; }
.otd-open { text-decoration: none; }

/* The statistics page is a long column of tile rows, chart rows and cards;
   these give it one consistent vertical rhythm. */
.stats-tiles { --tile-min: 10rem; margin-bottom: 1.25rem; }
.card-spaced { margin-bottom: 1.25rem; }
.stats-row { margin-bottom: 1.25rem; }
.chart-hint { text-align: center; margin: 0.5rem 0 0; }
.legend-gap { margin-left: 0.6rem; }

/* Eight milestone figures, always four per row (two rows) on desktop; two per row on phone. */
.milestones {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1rem;
    text-align: center;
}
@media (max-width: 480px) {
    .milestones { grid-template-columns: repeat(2, 1fr); }
}

/* Lead figure beside the records; the tiles and the activity map follow at full width. */
.stats-lead-row { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: var(--gap-grid); align-items: start; margin-bottom: 1.25rem; }
@media (max-width: 52rem) {
    .stats-lead-row { grid-template-columns: minmax(0, 1fr); }
}
.stats-lead-row .stat-lead-value { font-size: clamp(2.6rem, 6vw, 3.6rem); }
.stats-lead-row .spark { height: 5rem; }
.record-list { list-style: none; margin: 0; padding: 0; display: grid; }
.record-list li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0 1rem; align-items: baseline; padding: 0.65rem 0; border-top: 1px solid var(--border); }
.record-list li:first-child { border-top: 0; }
.record-label { color: var(--text-2); font-size: 0.85rem; }
.record-value { font-family: var(--font-display); font-size: 1.15rem; font-variant-numeric: tabular-nums; text-align: right; }
.record-sub { grid-column: 1 / -1; font-size: 0.85rem; color: var(--muted); overflow-wrap: anywhere; }
a.record-sub { color: var(--primary); }
.stats-tiles .tile-label { hyphens: none; overflow-wrap: anywhere; }

.stats-all > summary { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; font-weight: 650; list-style: none; }
.stats-all > summary::-webkit-details-marker { display: none; }
.stats-all[open] > summary { margin-bottom: 1rem; }
.stats-figures { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 1rem 1.25rem; margin: 0; }
.stats-figures dt { color: var(--muted); font-size: 0.8rem; }
.stats-figures dd { margin: 0; font-family: var(--font-display); font-size: 1.1rem; font-weight: 650; font-variant-numeric: tabular-nums; }

/* Year strip: one year on a track, concerts as bars, festivals as wider bars. */
.year-strip { display: grid; gap: 0.5rem; margin-bottom: 1.25rem; }
.year-strip-top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 1rem; }
.year-strip-top h2 { font-size: 0.95rem; margin: 0; }
.year-strip-legend { display: flex; gap: 0.25rem 1rem; margin: 0 0 0 auto; padding: 0; list-style: none; font-size: 0.75rem; color: var(--text-2); }
.year-strip-legend li { display: inline-flex; align-items: center; gap: 0.4rem; }
.year-key { display: inline-block; flex: none; }
.year-key--concert { width: 4px; height: 14px; border-radius: 2px; background: var(--concert); }
.year-key--festival { width: 16px; height: 8px; border-radius: 3px; background: var(--festival); }
.year-strip-track { position: relative; height: 2.4rem; margin-top: 1.1rem; border-radius: var(--r-m); background: var(--card-2); border: 1px solid var(--border); }
.year-mark { position: absolute; top: 50%; transform: translate(-50%, -50%); }
.year-mark::before { content: ""; position: absolute; inset: -9px -7px; }
.year-mark--concert { width: 4px; height: 22px; border-radius: 2px; background: var(--concert); box-shadow: 0 0 0 2px var(--card-2); }
.year-mark--festival { height: 12px; border-radius: 4px; background: var(--festival); box-shadow: 0 0 0 2px var(--card-2); transform: translate(0, -50%); }
.year-mark[data-tip]:hover::after, .year-mark[data-tip]:focus-visible::after {
    content: attr(data-tip); position: absolute; left: 50%; bottom: calc(100% + 10px); transform: translateX(-50%);
    padding: 0.25rem 0.5rem; border-radius: 6px; background: var(--text); color: var(--card); font-size: 0.75rem; font-weight: 600; white-space: nowrap; z-index: 3; pointer-events: none;
}
.year-today { position: absolute; top: -9px; bottom: -9px; width: 2px; background: var(--text); }
.year-today > span { position: absolute; bottom: calc(100% + 2px); left: 50%; transform: translateX(-50%); font-size: 0.7rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text); white-space: nowrap; }
.year-months { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); font-size: 0.75rem; color: var(--muted); }
.year-months span { border-left: 1px solid var(--border); padding-left: 4px; overflow: hidden; white-space: nowrap; }

/* The import preview's figures have no icon chip, so they stay on the plain
   centred `.tile` rather than taking `.tile-lead`. */
.import-tiles { --tile-min: 9rem; margin-bottom: 1.25rem; }

/* Three figures across the top of a band's song statistics. */
.song-figures { margin-bottom: 1.25rem; }
.song-figures .tile-value { font-size: 1.3rem; }
.song-bar { height: 4px; }

/* A rule-and-label divider between the bands of a long statistics page. */
.section-heading {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    margin: 2rem 0 1rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border);
}

/* --------------------------------------------------------------------------
   10. Empty states
   -------------------------------------------------------------------------- */
/* shared/ui's `ui:EmptyState` is one paragraph; here it usually leads with a
   heading-weight line, and sometimes offers more than one way out. */
.empty-note .empty-title {
    display: block;
    margin-bottom: 0.25rem;
    font-family: var(--font-display);
    font-size: 1.05rem;
    color: var(--text);
}
.empty-note-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--gap-inline);
    margin-top: 0.9rem;
}
/* "Nothing left to do" — the one place a tick carries meaning rather than
   decoration, so it takes the semantic positive token. */
.done-tick { color: var(--pos); }

/* --------------------------------------------------------------------------
   11. Home hero
   -------------------------------------------------------------------------- */
/* The one deliberately dark surface in the app: the two domain colours lit
   against ink, which is where the "Stagelight" name comes from. */
.hero {
    background:
        radial-gradient(ellipse 60% 80% at 20% 0%, color-mix(in srgb, var(--concert) 35%, transparent), transparent 60%),
        radial-gradient(ellipse 50% 70% at 85% 100%, color-mix(in srgb, var(--festival) 22%, transparent), transparent 60%),
        linear-gradient(135deg, #1b1530 0%, #2a2145 100%);
    border-radius: var(--r-l);
    color: #ffffff;
    padding: 4.5rem 2rem;
    text-align: center;
    box-shadow: var(--shadow-2);
}
.hero-mark { font-size: 3.5rem; line-height: 1; margin-bottom: 1rem; }
.hero h1 { font-size: 2.1rem; font-weight: 800; color: #ffffff; margin-bottom: 0.75rem; }
.hero .lead { color: rgba(255, 255, 255, 0.75); max-width: 38rem; margin: 0 auto 2rem; }
.hero .actions { justify-content: center; }
.hero .hero-alt { margin: 1rem 0 0; font-size: 0.85rem; }
.hero .hero-alt a { color: rgba(255, 255, 255, 0.75); }
.hero .hero-alt a:hover { color: #ffffff; }

/* The hero is the one dark surface, so a button on it cannot use the card
   surface tokens — it borrows the hero's own white instead. */
.btn-on-dark {
    background: rgba(255, 255, 255, 0.1);
    border-color: rgba(255, 255, 255, 0.35);
    color: #ffffff;
}
.btn-on-dark:hover { background: rgba(255, 255, 255, 0.18); border-color: #ffffff; }

.btn-lg { padding: 0.6rem 1.5rem; font-size: 0.95rem; }
.btn-sm { padding: 0.3rem 0.55rem; font-size: 0.82rem; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Destructive action. shared/ui deliberately ships only .btn and .btn-primary,
   so "delete" borrows the semantic negative token rather than a third colour. */
.btn-danger { color: var(--neg); border-color: color-mix(in srgb, var(--neg) 40%, var(--border)); }
.btn-danger:hover { background: color-mix(in srgb, var(--neg) 10%, var(--card)); border-color: var(--neg); }

.feature-card { text-align: center; }
.feature-card h2 { margin-bottom: 0.4rem; }
.feature-card p { margin: 0; }

.feature-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: var(--r-m);
    background: var(--primary-soft);
    color: var(--primary-ink);
    font-size: 1.5rem;
    margin-bottom: 1rem;
}

/* --------------------------------------------------------------------------
   12. Badges
   -------------------------------------------------------------------------- */
/* shared/ui's .badge takes its colour from --badge-accent; these are the
   variants this app needs. */
.badge-concert { --badge-accent: var(--concert); }
.badge-festival { --badge-accent: var(--festival-ink); }
.badge-warn { --badge-accent: var(--warn); }
.badge-primary { --badge-accent: var(--primary); }
.badge-muted { --badge-accent: var(--muted); }
.badge-pos { --badge-accent: var(--pos); }
.badge-neg { --badge-accent: var(--neg); }
.badge-wishlist { --badge-accent: var(--primary); }
.badge-radar { --badge-accent: var(--radar); }

/* Radar card highlight for wishlist-matched shows. */
.card--wishlist { border-color: var(--primary); }
.badge-pill { border-radius: 999px; padding-inline: 0.5rem; }

/* --------------------------------------------------------------------------
   13. Statistics: bar charts
   -------------------------------------------------------------------------- */
.barchart { margin: 0; }
.barchart .bar-row {
    display: grid;
    grid-template-columns: minmax(0, 8.5rem) minmax(0, 1fr) 3rem;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.45rem;
}
.barchart .bar-label { font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.barchart .bar-track {
    display: flex;
    gap: 2px; /* surface gap between stacked segments */
    height: 1.05rem;
    background: var(--series-track);
    border-radius: var(--r-s);
    overflow: hidden;
}
.barchart .bar-fill { height: 100%; min-width: 2px; }
/* Rounded data-end on the outermost segment only; baseline stays flat. */
.barchart .bar-fill:last-child { border-radius: 0 var(--r-s) var(--r-s) 0; }
.barchart .bar-value {
    font-size: 0.85rem;
    font-weight: 600;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.fill-concert { background: var(--concert); }
.fill-festival { background: var(--festival); }
.fill-neutral { background: var(--series-neutral); }

.legend-dot {
    display: inline-block;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 0.2rem;
    margin-right: 0.3rem;
    vertical-align: baseline;
}

/* Twelve months across, one bar each — a fixed grid rather than a scroller,
   because the category count never changes. */
.month-grid {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 0.35rem;
    align-items: end;
    height: 130px;
}
.month-col { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.month-bar { width: 70%; min-height: 2px; background: var(--series-neutral); border-radius: 4px 4px 0 0; }
.month-name { font-size: 0.7rem; color: var(--muted); margin-top: 0.25rem; }

/* Nachhall: four weeks before the show, four after; the after bars carry the concert colour. */
.nachhall-grid {
    display: grid;
    grid-template-columns: repeat(8, minmax(0, 1fr));
    gap: 0.35rem;
    align-items: end;
    height: 110px;
}
.nachhall-col { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.nachhall-col:nth-child(5) { border-left: 1px dashed var(--border); padding-left: 0.2rem; }
.nachhall-bar { width: 70%; min-height: 2px; background: var(--series-neutral); border-radius: 4px 4px 0 0; }
.nachhall-col.is-after .nachhall-bar { background: var(--concert); }
.nachhall-name { font-size: 0.7rem; color: var(--muted); margin-top: 0.25rem; }

/* --------------------------------------------------------------------------
   14. Statistics: activity tile map
   -------------------------------------------------------------------------- */
.activity-wrap { overflow-x: auto; padding-bottom: 0.25rem; }
.activity-months-row { display: flex; gap: 3px; align-items: flex-start; }
.activity-month-col { flex: 1 1 0; min-width: 1.75rem; max-width: 2.5rem; display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.activity-month-label { font-size: 0.75rem; color: var(--muted); white-space: nowrap; line-height: 1; text-align: center; }
.activity-month-cells { display: flex; flex-direction: column; gap: 3px; }

.activity-cell { height: 16px; border-radius: 3px; background: var(--activity-0); }
.activity-legend .activity-cell { width: 16px; }
.activity-cell.level-0 { background: var(--activity-0); }
.activity-cell.level-1 { background: var(--activity-1); }
.activity-cell.level-2 { background: var(--activity-2); }
.activity-cell.level-3 { background: var(--activity-3); }
.activity-cell.level-future { background: transparent; border: 1px solid var(--border); }

.activity-legend { display: flex; align-items: center; gap: 4px; justify-content: flex-end; margin-top: 8px; }

/* --------------------------------------------------------------------------
   15. Forms
   -------------------------------------------------------------------------- */
/* shared/ui's .form-card is sized for a login box; the concert and festival
   forms are full-width editors with nested entries. */
.form-wide { max-width: none; }

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    gap: 0 1rem;
}
.form-grid .field-full { grid-column: 1 / -1; }

.checkbox-row {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin-top: 0.9rem;
    font-size: 0.9rem;
}
.checkbox-row input { width: auto; accent-color: var(--primary); }
.checkbox-row label { margin: 0; font-weight: 400; }

.required-mark,
label.required::after { color: var(--neg); }
label.required::after { content: " *"; }

/* A field with its unit attached, e.g. the currency of a price. */
.input-group { display: flex; align-items: stretch; }
.input-group input { flex: 1; min-width: 0; }
.input-group input:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.input-group input:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.input-group-text {
    display: flex;
    align-items: center;
    padding: 0 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    background: var(--card-2);
    color: var(--muted);
    white-space: nowrap;
}
.input-group-text:first-child { border-right: none; border-top-right-radius: 0; border-bottom-right-radius: 0; }
.input-group-text:last-child { border-left: none; border-top-left-radius: 0; border-bottom-left-radius: 0; }

/* Discogs lookup under the band-name field: a button, the hits it returns,
   and a preview of the image it would import. */
.discogs-suggest { margin-top: 0.4rem; }
.suggest-list {
    margin-top: 0.4rem;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    overflow: hidden;
}
.suggest-item,
.suggest-note {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.35rem 0.6rem;
    border: none;
    border-bottom: 1px solid var(--border);
    background: var(--card);
    color: var(--text);
    text-align: left;
    font: inherit;
    font-size: 0.85rem;
}
.suggest-item:last-child,
.suggest-note:last-child { border-bottom: none; }
.suggest-item { cursor: pointer; }
.suggest-item:hover { background: var(--card-2); }
.suggest-thumb { flex: none; border-radius: var(--r-s); object-fit: cover; }
.suggest-image { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }

/* Irreversible actions, fenced off at the bottom of an edit form. */
.danger-zone { margin-top: 1rem; border-color: color-mix(in srgb, var(--neg) 40%, var(--border)); }
.danger-zone h2 { color: var(--neg); font-size: 0.9rem; }
.danger-zone p { margin: 0; }

/* Geocoding panel on the venue form: current coordinates and the lookup. */
.coord-card { margin-top: 1rem; }
.coord-card h2 { margin: 0 0 0.2rem; font-size: 0.95rem; }

/* A titled block between or below the field grids — the bands on a concert, the
   acts on a festival. */
.form-section { margin-top: 2rem; }
.form-section > .row-between { margin-bottom: 0.75rem; }

/* Rarely needed fields, folded away under a "More" toggle. */
.form-more > summary { cursor: pointer; font-weight: 600; }
.form-more[open] > summary { margin-bottom: 0.5rem; }
.forecast-guard > summary { cursor: pointer; font-weight: 600; }
.forecast-guard[open] > summary { margin-bottom: 0.5rem; }

/* One card per band on a concert, per act on a festival. */
.entry {
    padding: 1rem 1.15rem;
    margin-bottom: 0.75rem;
    background: var(--card-2);
    border: 1px solid var(--border);
    border-radius: var(--r-m);
}
/* The fields sit on one line and align on their baseline — the inputs, not
   the labels above them — so the row reads as a single control strip. */
.entry-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem;
}
.entry-row > .grow { flex: 1 1 14rem; min-width: 0; }
.entry-row > .entry-narrow { flex: 0 1 10rem; min-width: 0; }
.entry-row label { margin-top: 0; }
.entry-row .checkbox-row { margin: 0 0 0.5rem; }
.entry-row > .btn { margin-bottom: 0.15rem; }

/* A text field with its submit button attached to the end. */
.input-row { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
.input-row .grow { flex: 1; min-width: 0; }
.input-row input {
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    background: var(--card);
    color: var(--text);
}
.input-row input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}

/* --- Paste-a-setlist editor --- */
.paste-text {
    width: 100%;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    background: var(--card);
    color: var(--text);
    resize: vertical;
}
.paste-text:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}
.paste-controls { margin-top: 0.75rem; }
.error-text { color: var(--neg); }

/* The editor's running order: a handle, an editable title and the row's controls.
   The encore is ruled off by a divider the script keeps in front of its first row. */
.setlist-form { margin-top: 1rem; }
.setlist-rows {
    margin: 0 0 1rem;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    counter-reset: setlist-row;
}
.setlist-row {
    counter-increment: setlist-row;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.setlist-row::before {
    content: counter(setlist-row, decimal-leading-zero);
    flex: none;
    min-width: 1.5rem;
    font-family: var(--font-display);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--muted);
}
.setlist-row.is-dragging { opacity: 0.45; }
.setlist-row input[type="text"] {
    flex: 1;
    min-width: 0;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    font-size: 0.88rem;
    background: var(--card);
    color: var(--text);
}
.setlist-handle { cursor: grab; touch-action: none; }
.setlist-handle .grip {
    display: block;
    width: 0.6rem;
    height: 1rem;
    background: radial-gradient(circle, currentColor 1.1px, transparent 1.6px) 0 0 / 0.3rem 0.33rem;
}
.btn.setlist-encore[aria-pressed="true"] { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.setlist-encore-divider {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0 0.2rem;
    border-bottom: 2px solid var(--text);
    font-family: var(--font-display);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--primary-ink);
}
.paste-box { margin-bottom: 0.5rem; }
.paste-box > summary { cursor: pointer; font-weight: 600; padding: 0.4rem 0; }
.paste-orphans { margin-bottom: 1rem; }
.paste-orphans .checkbox-row { margin-top: 0.3rem; }
.setlist-editor .list > li { display: flex; align-items: center; gap: 0.5rem; }
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Inline search/filter bar above a list. */
.filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 1.25rem;
}
.filter-bar input,
.filter-bar select {
    padding: 0.45rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    font-size: 0.88rem;
    background: var(--card);
    color: var(--text);
}
.filter-bar input:focus,
.filter-bar select:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}
.filter-bar .grow { flex: 1 1 12rem; min-width: 0; }

/* --------------------------------------------------------------------------
   16. Flash variants
   -------------------------------------------------------------------------- */
/* A flash that lists what went wrong, rather than stating it in one line. */
.flash-list { margin: 0.35rem 0 0; padding-left: 1.1rem; }

/* --------------------------------------------------------------------------
   17. Band modal
   -------------------------------------------------------------------------- */
/* A native <dialog>: the browser supplies the backdrop, the top layer, focus
   trapping and Escape-to-close, which is all Bootstrap's modal JS was doing. */
.modal {
    width: min(30rem, calc(100vw - 2rem));
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--r-l);
    background: var(--card);
    color: var(--text);
    box-shadow: var(--shadow-2);
}
.modal::backdrop { background: rgba(0, 0, 0, 0.45); backdrop-filter: blur(2px); }

.modal-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.9rem 1.15rem;
    border-bottom: 1px solid var(--border);
    font-family: var(--font-display);
    font-weight: 650;
}
.modal-head .ui-icon { color: var(--primary); }
.modal-head .icon-btn { margin-left: auto; border: none; }

.modal-body { padding: 1.15rem; }
.modal-body label { display: block; font-weight: 600; font-size: 0.9rem; margin: 0.85rem 0 0.3rem; }
.modal-body > label:first-child { margin-top: 0; }
.modal-body input[type="text"] {
    width: 100%;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    background: var(--card);
    color: var(--text);
}
.modal-body input[type="text"]:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}
.modal-body .help-text { display: block; margin-top: 0.6rem; font-size: 0.8rem; color: var(--muted); }

.modal-foot {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0.9rem 1.15rem;
    border-top: 1px solid var(--border);
    background: var(--card-2);
    border-radius: 0 0 var(--r-l) var(--r-l);
}

/* --------------------------------------------------------------------------
   18. Pagination
   -------------------------------------------------------------------------- */
.pagination {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    justify-content: center;
    margin: 1.5rem 0 0;
    padding: 0;
    list-style: none;
}
.pagination a,
.pagination span {
    display: inline-block;
    min-width: 2rem;
    padding: 0.35rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    background: var(--card);
    color: var(--text-2);
    text-align: center;
    text-decoration: none;
    font-size: 0.85rem;
}
.pagination a:hover { border-color: var(--border-strong); color: var(--text); }
.pagination .is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
    font-weight: 600;
}
.pagination .is-disabled { color: var(--muted); opacity: 0.6; }

/* --------------------------------------------------------------------------
   19. Band images
   -------------------------------------------------------------------------- */
/* The pixel size is a per-instance argument of the partial, so it arrives as
   an inline width/height; everything else about the thumbnail lives here. */
.band-thumb {
    flex: none;
    border-radius: var(--r-s);
    object-fit: cover;
    background: var(--card-2);
}
.band-thumb-empty {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
}
.photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr));
    gap: .75rem;
}
.photo-item { position: relative; min-width: 0; }
.photo-item a { display: block; }
.photo-item img {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--r-m);
    background: var(--card-2);
}
.photo-caption { margin: .25rem 0 0; overflow-wrap: anywhere; }
.photo-delete { position: absolute; top: .375rem; right: .375rem; margin: 0; }
/* Band cover: the portrait sits top right, the live record runs along the bottom. */
.show-cover.show-cover--band { min-height: 17rem; }
.band-cover-portrait { position: absolute; top: 1.25rem; right: 1.5rem; }
.band-cover-portrait .band-thumb { border: 1px solid color-mix(in srgb, var(--stage-ink) 30%, transparent); border-radius: var(--r-m); }
.band-record {
    flex: 1 1 100%;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
    gap: 1px;
    margin: 0.75rem 0 0;
    overflow: hidden;
    border: 1px solid color-mix(in srgb, var(--stage-ink) 22%, transparent);
    border-radius: var(--r-m);
    background: color-mix(in srgb, var(--stage-ink) 22%, transparent);
}
.band-record > div { padding: 0.5rem 0.75rem; background: color-mix(in srgb, var(--stage) 80%, transparent); }
.band-record dt, .band-record dd { color: var(--stage-ink); }
.band-record dt { font-size: 0.75rem; opacity: 0.8; }
.band-record dd { margin: 0; font-family: var(--font-display); font-size: 1.25rem; font-weight: 800; line-height: 1.2; }
.band-record dd small { font-size: 0.75rem; font-weight: 600; opacity: 0.8; }
@media (max-width: 40rem) {
    .band-cover-portrait { display: none; }
}
.card-head .card-head-link { margin-left: auto; font-size: 0.82rem; font-weight: 600; }

.band-portrait { text-align: center; margin-bottom: 1rem; }
.band-portrait .band-thumb { border-radius: var(--r-m); }

/* --------------------------------------------------------------------------
   20. Inline rating widget
   -------------------------------------------------------------------------- */
/* This section was written against a --space-N scale and a --card-bg that
   exist in neither shared/ui nor this app's theme. An unresolved var() with no
   fallback makes the whole declaration invalid, so every one of them was
   dropped: the rating pills had no padding at all and the row no gap. Mapped
   onto tokens that exist. */
.card-section {
    padding: var(--card-pad-y) var(--card-pad-x) 0;
}
/* The rating: ten rising segments in the domain colour on its soft step.
   Size S sits in rows, L on covers and in the input. */
.pegel {
    --pegel-on: var(--concert);
    --pegel-off: var(--concert-soft);
    display: inline-flex;
    align-items: flex-end;
    gap: 2px;
    height: var(--pegel-h);
    vertical-align: middle;
}
.pegel--s { --pegel-h: 1rem; --pegel-w: 4px; }
.pegel--l { --pegel-h: 2rem; --pegel-w: 10px; gap: 3px; }
.pegel--festival { --pegel-on: var(--festival); --pegel-off: var(--festival-soft); }
.pegel--radar { --pegel-on: var(--radar); --pegel-off: var(--radar-soft); }
.pegel-step {
    width: var(--pegel-w);
    border-radius: 1px;
    background: var(--pegel-off);
}
.pegel-step.is-on { background: var(--pegel-on); }
.pegel-step:nth-child(1) { height: 22%; }
.pegel-step:nth-child(2) { height: 30%; }
.pegel-step:nth-child(3) { height: 38%; }
.pegel-step:nth-child(4) { height: 46%; }
.pegel-step:nth-child(5) { height: 54%; }
.pegel-step:nth-child(6) { height: 62%; }
.pegel-step:nth-child(7) { height: 70%; }
.pegel-step:nth-child(8) { height: 78%; }
.pegel-step:nth-child(9) { height: 89%; }
.pegel-step:nth-child(10) { height: 100%; }
.pegel-field { display: inline-flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.pegel--input .pegel-step:not(.is-on) { background: color-mix(in srgb, var(--pegel-on) 25%, var(--card)); }
.pegel--input { cursor: pointer; padding: 0.25rem 0.1rem; box-sizing: content-box; border-radius: var(--r-s); touch-action: manipulation; }
.pegel--input .pegel-step { cursor: pointer; }
.pegel--input .pegel-step:hover { background: var(--pegel-on); opacity: 0.6; }
.pegel--input:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.pegel-word { font-size: 0.9rem; color: var(--text-2); font-variant-numeric: tabular-nums; }
.pegel--l.pegel--input { --pegel-h: 2.5rem; --pegel-w: 14px; }

/* A tour of the same pieces on a stage surface. */
.show-cover {
    --cover-tone: var(--concert);
    position: relative;
    isolation: isolate;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 0.35rem;
    min-height: 11rem;
    padding: 1.25rem 1.5rem;
    border-radius: var(--r-l);
    background: var(--stage);
    color: var(--stage-ink);
    text-decoration: none;
    margin-bottom: var(--gap-section);
}
.show-cover--festival { --cover-tone: var(--festival); }
.show-cover--venue { --cover-tone: var(--radar); }
.show-cover-photo {
    position: absolute;
    inset: 0;
    z-index: -3;
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: grayscale(1) contrast(1.15);
    mix-blend-mode: luminosity;
    opacity: 0.4;
}
/* Spotlight, haze and the crowd are gradients only. */
.show-cover::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -2;
    background:
        linear-gradient(to top, color-mix(in srgb, var(--stage) 92%, transparent) 0%, transparent 75%),
        radial-gradient(ellipse 55% 90% at 78% -10%, color-mix(in srgb, var(--cover-tone) 70%, transparent), transparent 70%),
        linear-gradient(to bottom, transparent 40%, color-mix(in srgb, var(--cover-tone) 22%, transparent) 100%);
    mix-blend-mode: screen;
}
.show-cover::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 2.75rem;
    z-index: -1;
    background:
        radial-gradient(circle 0.9rem at 8% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 1.1rem at 17% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 0.8rem at 27% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 1.2rem at 38% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 0.9rem at 49% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 1.15rem at 59% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 0.85rem at 70% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 1.1rem at 81% 100%, var(--stage) 98%, transparent),
        radial-gradient(circle 0.9rem at 92% 100%, var(--stage) 98%, transparent);
    opacity: 0.85;
}
a.show-cover:hover .show-cover-title { text-decoration: underline; }
.show-cover-kicker {
    align-self: flex-start;
    padding: 0.1rem 0.55rem;
    border-radius: var(--r-s);
    background: color-mix(in srgb, var(--cover-tone) 35%, transparent);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.show-cover-title {
    font-family: var(--font-display);
    font-size: var(--step-4);
    font-weight: 700;
    line-height: 1.05;
    overflow-wrap: anywhere;
    text-wrap: balance;
}
.show-cover-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 1rem;
    font-size: 0.9rem;
    opacity: 0.9;
}
.show-cover .pegel-word { color: inherit; }
.show-cover-type { font-weight: 600; }
.show-cover .pegel { --pegel-on: var(--stage-ink); --pegel-off: color-mix(in srgb, var(--stage-ink) 25%, transparent); }
@media (max-width: 40rem) {
    .show-cover { min-height: 9rem; padding: 1rem; }
    .show-cover-title { font-size: var(--step-2); }
}

/* One show as a line: a stub, the bill, the place, the state and the Pegel.
   The stub is a ticket for a concert, a woven wristband for a festival and a
   dashed outline for a radar hit. */
.show-line {
    --line-tone: var(--concert);
    --line-soft: var(--concert-soft);
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--border);
    color: inherit;
    text-decoration: none;
}
.show-line:last-child { border-bottom: none; }
.show-line--festival { --line-tone: var(--festival-ink); --line-soft: var(--festival-soft); }
.show-line--radar { --line-tone: var(--radar); --line-soft: var(--radar-soft); }
.show-line-stub {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 3.1rem;
    padding: 0.3rem 0;
    background: var(--line-soft);
    color: var(--line-tone);
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.05;
    text-transform: uppercase;
    border-radius: var(--r-s);
    border-right: 2px dashed color-mix(in srgb, var(--line-tone) 45%, transparent);
}
.show-line-day { font-size: var(--step-1); color: var(--text); font-variant-numeric: tabular-nums; }
.show-line-month { font-size: 0.65rem; letter-spacing: 0.08em; }
.show-line--festival .show-line-stub {
    border-right: none;
    background: repeating-linear-gradient(135deg, var(--line-soft) 0 5px, color-mix(in srgb, var(--line-tone) 22%, var(--line-soft)) 5px 8px);
    border-radius: var(--r-m);
}
.show-line--radar .show-line-stub {
    background: transparent;
    border: 2px dashed var(--line-tone);
}
.show-line-main { flex: 1; min-width: 0; }
.show-line-bill, .show-line-where { display: block; }
.show-line-bill { font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
a.show-line:hover .show-line-bill { color: var(--primary); }
.show-line-where { font-size: 0.85rem; color: var(--muted); }
.show-line-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 0.25rem; }
.show-lines { padding-inline: var(--card-pad-x); }
.show-line-year { font-size: 0.65rem; color: var(--muted); font-variant-numeric: tabular-nums; }

/* The shows archive: planned shows, then one section per year. */
.archive-section { margin-bottom: 1.25rem; }
.archive-head {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    margin: 0 0 0.5rem;
    padding: 0.5rem 0;
    background: var(--bg);
    font-size: var(--step-1);
}
.archive-count { font-size: 0.85rem; font-weight: 500; color: var(--muted); }
.archive-poster-link { margin-left: auto; font-size: 0.85rem; font-weight: 500; color: var(--muted); text-decoration: none; }
.archive-poster-link:hover { color: var(--text); }

/* Gig posters: the SVG is a 4:5 stage in the domain colour, so it keeps its own dark ground in both themes. */
.poster-wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); gap: 1rem; }
.poster-wall-item { display: block; border-radius: var(--r-m); overflow: hidden; box-shadow: 0 0.25rem 1rem color-mix(in srgb, var(--stage) 35%, transparent); transition: transform 0.15s ease; }
.poster-wall-item:hover { transform: translateY(-0.2rem); }
.poster-wall-item svg, .poster-single svg { display: block; width: 100%; height: auto; }
.poster-single { max-width: 34rem; margin: 0 auto; border-radius: var(--r-m); overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
    .poster-wall-item { transition: none; }
}

.archive-more { display: flex; justify-content: center; margin: 1.5rem 0; }
@media (max-width: 720px) {
    .archive-head { top: calc(var(--topbar-h) + env(safe-area-inset-top)); }
}

.chip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-bottom: 1rem; }
.chip { position: relative; }
.chip > summary,
a.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    min-height: 2rem;
    padding: 0.25rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    color: var(--text);
    font-size: 0.8rem;
    text-decoration: none;
    cursor: pointer;
    list-style: none;
}
.chip > summary::-webkit-details-marker { display: none; }
.chip > summary::marker { content: ''; }
.chip > summary:hover,
a.chip:hover { border-color: var(--border-strong); }
.chip.is-active > summary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.chip-list {
    position: absolute;
    z-index: 5;
    top: calc(100% + 0.25rem);
    left: 0;
    display: flex;
    flex-direction: column;
    min-width: 11rem;
    max-height: 18rem;
    overflow-y: auto;
    padding: 0.25rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    background: var(--card);
    box-shadow: var(--shadow-2);
}
.chip-list a {
    padding: 0.4rem 0.6rem;
    border-radius: var(--r-s);
    color: var(--text);
    font-size: 0.85rem;
    text-decoration: none;
}
.chip-list a:hover { background: var(--primary-soft); }
.chip-list a.is-active { font-weight: 600; color: var(--primary-ink); }
.chip-list .chip-clear { color: var(--muted); }
.chip-reset { color: var(--muted); }


/* --------------------------------------------------------------------------
   21. Radar card grid & tour groups
   -------------------------------------------------------------------------- */
/* Wishlist: a ranked list of rows, reordered by the handle. */
.wish-order { display: grid; gap: 0.5rem; }
.wish-order > .sub { margin: 0; }
.wish-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.wish-row {
    display: grid;
    grid-template-columns: auto 2rem auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.65rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    background: var(--card);
}
.wish-row.is-dragging { opacity: 0.55; box-shadow: var(--shadow-2); }
.wish-handle { cursor: grab; touch-action: none; }
.wish-rank { justify-self: center; }
.wish-main { display: grid; gap: 0.1rem; min-width: 0; }
.wish-main .name { font-weight: 600; overflow-wrap: anywhere; }
.wish-tour { justify-self: start; margin-top: 0.25rem; text-decoration: none; }

/* Month sections and the feed card. A Turbo Stream removes one show at a time, so
   a tour card and a month go with their last show. */
.radar-bar { justify-content: space-between; }
.radar-month { margin-bottom: 1.75rem; }
.radar-month:not(:has(.radar-single, .radar-tour:has(.radar-stop))) { display: none; }
.radar-month-title {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0 0 0.75rem;
    font-family: var(--font-body);
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted);
}
.radar-month-title::after { content: ""; flex: 1; border-top: 1px solid var(--border); }
.radar-cards { display: grid; gap: 0.75rem; }
.radar-tour:not(:has(.radar-stop)) { display: none; }

.radar-card {
    display: grid;
    grid-template-columns: 112px minmax(0, 1fr);
    gap: 1rem;
    padding: 0.9rem;
    border: 1px solid var(--border);
    border-radius: var(--r-l, var(--r-m));
    background: var(--card);
    box-shadow: var(--shadow-1);
    container-type: inline-size;
}
.radar-card .band-thumb { border-radius: var(--r-m); }
.radar-body { display: grid; gap: 0.5rem; align-content: start; min-width: 0; }
.radar-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.75rem; }
.radar-head h3 { margin: 0; font-size: 1.4rem; line-height: 1.1; overflow-wrap: anywhere; }
.radar-head h3 a { color: inherit; text-decoration: none; }
.radar-head h3 a:hover { text-decoration: underline; }
.radar-signal { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; font-weight: 700; color: var(--text-2); }
.radar-signal-bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 13px; }
.radar-signal-bars i { display: block; width: 4px; border-radius: 1px; background: var(--border-strong); }
.radar-signal-bars i.on { background: var(--radar); }
.radar-signal-bars i:nth-child(1) { height: 30%; }
.radar-signal-bars i:nth-child(2) { height: 55%; }
.radar-signal-bars i:nth-child(3) { height: 78%; }
.radar-signal-bars i:nth-child(4) { height: 100%; }
.radar-sub { display: flex; flex-wrap: wrap; gap: 0.15rem 0.75rem; margin: 0; font-size: 0.875rem; color: var(--text-2); }
.radar-sub > span { display: inline-flex; align-items: center; gap: 0.3rem; }

.radar-reasons { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.radar-reasons:empty { display: none; }
.radar-reason {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.1rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-2);
    color: var(--text-2);
    font-size: 0.78rem;
    font-weight: 600;
}
.radar-reason.is-wish svg { color: var(--primary); }
.radar-reason.is-trip { border-color: transparent; background: var(--radar-soft); color: color-mix(in srgb, var(--radar) 55%, var(--text)); }
.radar-reason.is-warn { border-color: transparent; background: color-mix(in srgb, var(--warn) 16%, var(--card)); color: var(--warn); }

.radar-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    padding-top: 0.6rem;
    border-top: 1px dashed var(--border);
}
.radar-foot-info { flex: 1 1 14rem; min-width: 0; }
.radar-foot-info p, .radar-stop p { margin: 0; }
.radar-offer-line, .radar-on-sale { font-size: 0.85rem; color: var(--text-2); }
.radar-on-sale { font-weight: 600; }
.radar-offer-line a { color: var(--primary-ink); font-weight: 600; }
.drop-board { margin-bottom: 1rem; }
.drop-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 0.75rem 1rem; padding: 0.6rem 0; }
.drop-info { display: grid; min-width: 0; }
.drop-clock { display: flex; gap: 0.4rem; font-variant-numeric: tabular-nums; color: var(--text-2); }
.drop-unit { display: grid; justify-items: center; gap: 0.1rem; }
.drop-unit i { font-style: normal; font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-2); }
.drop-digits {
    display: block; min-width: 2.2ch; padding: 0.2rem 0.4rem; border-radius: var(--r-s, 0.3rem);
    background: var(--stage); color: #fff; font-size: 1.25rem; text-align: center;
}
.drop-digits.is-flipping { animation: drop-flip 0.35s ease-out; }
@keyframes drop-flip { from { transform: rotateX(-90deg); } to { transform: rotateX(0); } }
.drop-links { display: flex; flex-wrap: wrap; gap: 0.3rem; opacity: 0.55; }
.drop-row.is-live .drop-clock { color: var(--primary-ink); font-weight: 700; }
.drop-row.is-live .drop-links { opacity: 1; }
.drop-row.is-live .drop-links .btn { background: var(--primary); border-color: var(--primary); color: #fff; }
.radar-offer-line.is-live { padding: 0.25rem 0.5rem; border-radius: var(--r-s, 0.3rem); background: color-mix(in srgb, var(--primary) 14%, transparent); }
@media (max-width: 720px) {
    .drop-row { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
    .drop-digits.is-flipping { animation: none; }
}
.radar-actions { display: flex; align-items: center; gap: 0.4rem; }
.radar-actions form { margin: 0; }
.icon-btn.is-on { color: var(--primary); border-color: var(--primary); }

/* The tour strip: one dashed stub per stop, nearest first, wrapping instead of scrolling sideways. */
.radar-strip { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); gap: 0.5rem; }
.radar-stop {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.25rem 0.65rem;
    align-items: center;
    padding: 0.5rem 0.65rem 0.5rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    background: var(--card-2);
}
.radar-stop.is-best { border-color: var(--radar); background: var(--card); }
.radar-stop > :nth-child(n+3) { grid-column: 1 / -1; }
.radar-stop-text { display: grid; line-height: 1.25; min-width: 0; }
.radar-stop-text small { color: var(--muted); font-size: 0.78rem; overflow-wrap: anywhere; }
.radar-stub {
    display: grid;
    justify-items: center;
    width: 2.9rem;
    padding: 0.4rem 0 0.45rem;
    border: 1.5px dashed var(--radar);
    border-radius: 10px;
    background: var(--card);
    color: var(--radar);
    font-family: var(--font-display);
    line-height: 1;
}
.radar-stub i { font-style: normal; font-size: 0.6rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.radar-stub b { margin-top: 0.15rem; font-size: 1.2rem; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }

@container (max-width: 34rem) {
    .radar-foot { flex-direction: column; align-items: stretch; }
    .radar-foot-info { flex: none; }
    .radar-actions { justify-content: flex-start; }
}
@media (max-width: 480px) {
    .radar-card { grid-template-columns: minmax(0, 1fr); }
    .radar-photo { display: none; }
}

/* --------------------------------------------------------------------------
   22. Mobile radar pass
   -------------------------------------------------------------------------- */
@media (max-width: 480px) {
    /* `.cards` collapsing to one column and `.btn` reaching --tap-min are both
       in shared/ui's mobile tier now, so what is left here is the part that is
       actually about this app's cards. */

    /* Score/distance badges sit above the title on small screens */
    .card-head .actions {
        flex-wrap: wrap;
        gap: 0.25rem;
    }

    /* Prevent badge clusters from overflowing narrow cards */
    .badge { white-space: nowrap; }

    .pegel--input { min-height: var(--tap-min); }
}

/* --------------------------------------------------------------------------
   22. In-app calendar view
   -------------------------------------------------------------------------- */
.cal-nav {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding: 0.6rem 0.75rem;
}

.cal-month-title {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.05rem;
    grid-column: 2;
    grid-row: 1;
    text-align: center;
}

.cal-nav-controls {
    grid-column: 1;
    grid-row: 1;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.search-results { display: flex; flex-direction: column; gap: 1rem; }

.tile-label { hyphens: manual; }

.cal-legend {
    display: flex;
    gap: 1rem;
    margin-bottom: 0.75rem;
    font-size: 0.8rem;
    color: var(--muted);
}

.cal-legend-item {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}

.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 3px;
    margin-bottom: 1.5rem;
}

.cal-th {
    text-align: center;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    padding: 0.4rem 0;
}

.cal-cell {
    min-height: 5.5rem;
    padding: 0.35rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    background: var(--card);
}

.cal-cell.cal-empty {
    background: transparent;
    border-color: transparent;
}

.cal-cell.cal-today {
    border-color: var(--primary);
    background: var(--primary-soft);
}

.cal-cell { position: relative; }

.cal-day-num {
    display: block;
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--text-2);
    line-height: 1;
}

/* The day number links to the show (or the month list); its pseudo-element covers the whole cell, and the labels sit above it. */
a.cal-day-link { text-decoration: none; }
a.cal-day-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
a.cal-day-link:hover::after { background: color-mix(in srgb, var(--primary) 8%, transparent); }
a.cal-day-link:focus-visible::after { outline: 2px solid var(--primary); outline-offset: -2px; }

.cal-today-num {
    color: var(--primary-ink);
    font-weight: 700;
}

.cal-dots {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    margin-top: 0.3rem;
}

.cal-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex: none;
}

.cal-dot-concert { background: var(--concert); }
.cal-dot-festival { background: var(--festival); }
.cal-dot-radar { background: var(--radar); }

.cal-labels {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-top: 0.2rem;
    overflow: hidden;
}

.cal-label {
    position: relative;
    z-index: 1;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.35;
    padding: 1px 4px;
    border-radius: 3px;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cal-label-concert { background: var(--concert-soft); color: var(--primary-ink); }
.cal-label-festival { background: var(--festival-soft); color: var(--festival-ink); }
.cal-label-radar { background: var(--radar-soft); color: var(--radar-ink); }
.cal-label:hover { text-decoration: underline; }
.cal-empty-month { text-align: center; padding: 1.5rem 0; }

@media (max-width: 480px) {
    .cal-cell { min-height: 2.75rem; padding: 0.25rem; }
    .cal-dot { width: 5px; height: 5px; }
    .cal-legend { flex-wrap: wrap; gap: 0.5rem; }
    .cal-labels { display: none; }
    .cal-dots { display: flex; }
}

@media (min-width: 481px) {
    .cal-dots { display: none; }
}

/* ── Radar vacation-budget context strip ──────────────────────────────── */

.radar-budget-strip {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-radius: var(--r-s);
    background: color-mix(in srgb, var(--primary) 8%, var(--card));
    border: 1px solid color-mix(in srgb, var(--primary) 20%, transparent);
    font-size: 0.82rem;
    color: var(--muted);
    margin-bottom: 1rem;
}
.radar-budget-strip--warn {
    background: var(--warn-soft);
    border-color: var(--warn-border);
    color: var(--text);
}


/* Map page */
        #concert-map {
            height: 70vh;
            min-height: 420px;
            border-radius: var(--r-m);
        }
        #concert-map .leaflet-popup-content {
            margin: .75rem 1rem;
        }
        .map-popup h2 {
            margin-bottom: .35rem;
        }
        .map-popup h2 {
            font-size: .95rem;
        }
        .map-popup ul {
            margin: 0;
            padding-left: 1.1rem;
        }
        .map-popup .popup-group {
            margin-top: .35rem;
        }
        /* The map fills its card edge to edge; the card supplies the frame. */
        .map-frame {
            padding: .5rem;
        }

        /* Calm tiles: muted in light, inverted into a dark map in dark. A filter isn't a colour,
           so it can't be a light-dark() token; this is the app's one scheme rule. Pins sit in
           another pane and keep their colour. */
        .leaflet-tile-pane { filter: saturate(0.32) brightness(1.05) contrast(0.9); }
        :root[data-theme="dark"] .leaflet-tile-pane { filter: invert(1) hue-rotate(180deg) saturate(0.35) brightness(0.82) contrast(0.92); }
        @media (prefers-color-scheme: dark) {
            :root:not([data-theme="light"]) .leaflet-tile-pane { filter: invert(1) hue-rotate(180deg) saturate(0.35) brightness(0.82) contrast(0.92); }
        }

        .map-layout {
            display: grid;
            grid-template-columns: minmax(0, 1fr) 21rem;
            gap: var(--gap-grid);
            align-items: start;
            margin-bottom: 1.25rem;
        }
        @media (max-width: 64rem) {
            .map-layout { grid-template-columns: minmax(0, 1fr); }
        }
        .map-layout > .card { margin-bottom: 0; }
        .map-list { display: flex; flex-direction: column; max-height: calc(70vh + 1rem); min-height: 0; }
        @media (max-width: 64rem) {
            .map-list { max-height: 24rem; }
        }
        .map-rows { list-style: none; margin: 0; padding: 0 0.5rem 0.5rem; overflow-y: auto; }
        .map-rows > li { display: flex; align-items: center; gap: 0.25rem; border-radius: var(--r-s); }
        .map-rows > li[hidden] { display: none; }
        .map-rows > li:hover { background: var(--card-2); }
        .map-row {
            flex: 1; min-width: 0; display: flex; align-items: center; gap: 0.6rem;
            padding: 0.5rem; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; border-radius: var(--r-s);
        }
        .map-row-name { flex: 1; min-width: 0; display: grid; font-weight: 600; overflow-wrap: anywhere; }
        .map-row-name small { color: var(--muted); font-weight: 400; font-size: 0.8rem; }
        .map-row-link { display: grid; place-items: center; width: 2rem; height: 2rem; flex: none; color: var(--muted); border-radius: var(--r-s); }
        .map-row-link:hover { color: var(--primary); }
        .map-row-dot { flex: none; width: 0.75rem; height: 0.75rem; border-radius: 50%; }
        .map-row-dot--concert { background: var(--concert); }
        .map-row-dot--festival { background: var(--festival); }
        .map-row-dot--mixed { background: linear-gradient(135deg, var(--concert) 0 50%, var(--festival) 50% 100%); }

        /* Spotlight pins: a lit disc in the domain colour, ringed in the card colour. The count sits on ink that clears 4.5:1 on each fill. */
        .map-pin {
            --pin: var(--concert);
            --pin-ink: var(--on-primary);
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            background: var(--pin);
            border: 2px solid var(--card);
            box-shadow: 0 0 0 5px color-mix(in oklab, var(--pin) 30%, transparent), var(--shadow-1);
            color: var(--pin-ink);
            font-weight: 700;
            line-height: 1;
            box-sizing: border-box;
        }
        .map-pin::after {
            content: "";
            position: absolute;
            bottom: -6px;
            left: 50%;
            width: 10px;
            height: 10px;
            background: inherit;
            border-right: 2px solid var(--card);
            border-bottom: 2px solid var(--card);
            transform: translateX(-50%) rotate(45deg);
            z-index: -1;
        }
        .map-pin .ui-icon {
            font-size: .8em;
            opacity: .55;
            position: absolute;
            top: -2px;
            font-weight: 400;
        }
        .map-pin .map-pin-count {
            margin-top: .35em;
        }
        .map-pin--concert { --pin: var(--concert); --pin-ink: var(--on-primary); }
        .map-pin--festival { --pin: var(--festival); --pin-ink: var(--stage); }
        .map-pin--mixed {
            --pin: var(--concert);
            background: linear-gradient(135deg, var(--concert) 0 50%, var(--festival) 50% 100%);
            --pin-ink: #fff;
            text-shadow: 0 0 3px rgb(0 0 0 / .65);
        }

        .map-legend {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            align-items: center;
            font-size: .875rem;
        }
        .map-legend .legend-swatch {
            display: inline-block;
            width: 1rem;
            height: 1rem;
            border-radius: 50%;
            border: 2px solid var(--card);
            box-shadow: 0 0 0 3px color-mix(in oklab, var(--concert) 25%, transparent);
            vertical-align: -2px;
            margin-right: .35rem;
        }
        .legend-swatch--concert { background: var(--concert); }
        .legend-swatch--festival { background: var(--festival); box-shadow: 0 0 0 3px color-mix(in oklab, var(--festival) 25%, transparent); }
        .legend-swatch--mixed { background: linear-gradient(135deg, var(--concert) 0 50%, var(--festival) 50% 100%); }

        .map-filter-bar {
            display: flex;
            flex-wrap: wrap;
            gap: .75rem 1.5rem;
            align-items: center;
            padding-top: .75rem;
            border-top: 1px solid var(--border);
            margin-top: .75rem;
            font-size: .875rem;
        }
        .map-filter-group {
            display: flex;
            align-items: center;
            gap: .4rem;
        }
        .map-filter-group .sub { white-space: nowrap; }
        .filter-chips {
            display: flex;
            gap: .25rem;
        }
        .filter-chip {
            padding: .25rem .65rem;
            border: 1px solid var(--border);
            border-radius: 999px;
            background: var(--card);
            color: var(--text);
            font: inherit;
            font-size: .8rem;
            cursor: pointer;
            transition: border-color .12s, background .12s;
        }
        .filter-chip:hover { border-color: var(--border-strong); }
        .filter-chip.is-active { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
        .filter-select {
            padding: .25rem .5rem;
            border: 1px solid var(--border);
            border-radius: var(--r-s);
            background: var(--card);
            color: var(--text);
            font: inherit;
            font-size: .8rem;
            cursor: pointer;
        }
        .filter-select:focus {
            outline: none;
            border-color: var(--primary);
        }
        .map-popup .popup-venue-link {
            margin-top: .5rem;
            padding-top: .4rem;
            border-top: 1px solid var(--border);
            font-size: .85rem;
        }

/* Venue page: history beside a mini map and the facts. */
.venue-layout { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: var(--gap-grid); align-items: start; margin-top: 1.25rem; }
@media (max-width: 52rem) {
    .venue-layout { grid-template-columns: minmax(0, 1fr); }
}
.venue-aside { display: grid; gap: 1.25rem; min-width: 0; }
.venue-aside .card { margin: 0; }
.venue-map { padding: 0.5rem; }
.venue-map-canvas { height: 15rem; border-radius: var(--r-m); }

/* --------------------------------------------------------------------------
   Public year recap

   Its own chrome, because the recap is served outside the app shell: no
   topbar, no nav, nothing that links back into a private account. Just a
   centred column on the app's own tokens so it still looks like it came
   from here.
   -------------------------------------------------------------------------- */
.recap-page {
    background: var(--bg);
    color: var(--text);
    min-height: 100vh;
}

.recap-shell {
    max-width: 52rem;
    margin: 0 auto;
    padding: 2.5rem 1rem 3rem;
}

.recap-head {
    text-align: center;
    margin-bottom: 1.75rem;
}

.recap-mark {
    color: var(--primary);
    font-size: 2.5rem;
    display: block;
    margin-bottom: 0.5rem;
}

.recap-head h1 { margin: 0 0 0.25rem; }

.recap-head .subtitle { color: var(--muted); margin: 0; }

.recap-foot {
    margin-top: 2rem;
    text-align: center;
}

@media (max-width: 480px) {
    .recap-shell { padding: 1.5rem 0.75rem 2rem; }
    .recap-mark { font-size: 2rem; }

/* The recap story: swipeable cards, plain anchors for tap navigation, a stacked list under reduced motion. */
.story-deck { margin-bottom: 2rem; }
.story-cards { display: flex; gap: 1rem; margin: 0; padding: 0; list-style: none; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
.story-cards:focus-visible { outline: 2px solid var(--primary); outline-offset: 4px; }
@media (prefers-reduced-motion: no-preference) { .story-cards { scroll-behavior: smooth; } }
.story-card { position: relative; display: grid; flex: 0 0 100%; align-content: center; justify-items: center; gap: 0.75rem; min-height: 26rem; padding: 2rem 1.5rem 4rem; border: 1px solid color-mix(in oklab, var(--primary) 24%, var(--border)); border-radius: var(--r-l); background: color-mix(in oklab, var(--primary) 8%, var(--card)); text-align: center; scroll-snap-align: center; }
.story-kicker { margin: 0; color: var(--muted); font-size: 0.8rem; font-weight: 650; letter-spacing: 0.1em; text-transform: uppercase; }
.story-figure { margin: 0; color: var(--primary); font-family: var(--font-display); font-size: clamp(3.5rem, 2rem + 8vw, 6rem); font-weight: 700; line-height: 1; letter-spacing: -0.03em; overflow-wrap: anywhere; }
.story-figure-name { font-size: clamp(1.9rem, 1.4rem + 3vw, 3rem); line-height: 1.1; }
.story-text { max-width: 28rem; margin: 0; color: var(--muted); }
.story-image { display: block; width: 100%; max-width: 30rem; height: auto; border: 1px solid var(--border); border-radius: var(--r-m); }
.story-nav { position: absolute; right: 1rem; bottom: 1rem; left: 1rem; display: flex; align-items: center; justify-content: center; gap: 1.5rem; }
.story-count { color: var(--muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.story-prev, .story-next { display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border: 1px solid var(--border); border-radius: 50%; background: var(--card); color: var(--text); text-decoration: none; }
.story-prev:hover, .story-next:hover { border-color: var(--primary); color: var(--primary); }
.story-all { margin: 0 0 1rem; font-size: 1.1rem; }
@media (prefers-reduced-motion: reduce) {
    .story-cards { flex-direction: column; overflow: visible; scroll-snap-type: none; }
    .story-card { flex-basis: auto; min-height: 0; padding-bottom: 2rem; }
    .story-nav { display: none; }
}
}

/* --------------------------------------------------------------------------
   23. Lineup board
   -------------------------------------------------------------------------- */
/* Bare controls outside a .form-card: text fields, selects, textareas and file pickers. */
.field {
    width: 100%;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    background: var(--card);
    color: var(--text);
}
.field:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}
.field-sm { width: auto; padding: 0.3rem 0.55rem; font-size: 0.82rem; }
.field-grow { flex: 1; min-width: 0; }
input[type="file"].field::file-selector-button {
    margin-right: 0.65rem;
    padding: 0.3rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    background: var(--card-2);
    color: var(--text);
    font: inherit;
    cursor: pointer;
}
input[type="file"].field::file-selector-button:hover { border-color: var(--primary); }

/* Small uppercase caption above a group inside a card. */
.section-label {
    margin: 0 0 0.4rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
}

/* A list-row that is not a link: no hover highlight, no title recolour. */
.list-row--static:hover { background: none; }
.list-row--static:hover .list-row-title { color: inherit; }
.list-row--static .list-row-title { white-space: normal; }

.section-divider {
    margin: 1.25rem 0;
    border: 0;
    border-top: 1px solid var(--border);
}

/* Compact row of controls (e.g. two buttons side by side). */
.btn-group { display: flex; gap: var(--gap-inline); align-items: center; }

/* A form that sits in a row of buttons instead of breaking onto its own line. */
.inline-form { display: inline; margin: 0; }

.lineup-bulk-add > summary {
    cursor: pointer;
}
.lineup-bulk-form {
    padding: var(--card-pad-y) var(--card-pad-x);
}
.lineup-bulk-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1rem;
    margin: 0.75rem 0;
}

.lineup-board {
    display: flex;
    gap: 1rem;
    overflow-x: auto;
    padding-bottom: 0.5rem;
    align-items: flex-start;
}
.lineup-day {
    flex: 0 0 18rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.lineup-day-head {
    margin: 0;
    font-size: 1rem;
    text-align: center;
}
.lineup-tier {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    padding: 0.6rem;
}
.lineup-tier h3 {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0 0.5rem;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--muted);
}
.lineup-tier-unranked {
    border-style: dashed;
}
.lineup-act-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    min-height: 0.5rem;
}
.lineup-act {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    background: var(--card-2);
}
.lineup-act-main {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex: 1 1 100%;
    min-width: 0;
}
.lineup-act-main .name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lineup-tier-form {
    display: flex;
    gap: 0.2rem;
}
.tier-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.6rem;
    padding: 0.15rem 0.35rem;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    background: var(--card);
    color: var(--text);
    font-size: 0.75rem;
    font-weight: 600;
    cursor: pointer;
    line-height: 1.4;
}
.tier-pill:hover {
    border-color: var(--primary);
}
.tier-pill.is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
}

/* A sibling's frame that answered with nothing leaves no gap in its stack. */
.lazy-frame[complete]:not(:has(*)) { display: none; }
.dropzone + .sub:empty { display: none; }

/* Show day on Home and the fullscreen ticket */
.show-day { display: grid; gap: var(--gap-grid); margin-bottom: var(--gap-section); }
.show-day-head { display: grid; justify-items: start; }
.show-day-title { margin: 0.25rem 0 0; }
.show-day-times { display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 0; }
.show-day-times dt { font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.06em; }
.show-day-times dd { margin: 0; font-size: var(--step-2); }
.show-day-times dd .sub { font-size: var(--step-0); margin-left: 0.4rem; }
.show-day-actions { flex-wrap: wrap; }
.show-day-companions { margin: 0; }
.show-day-after { display: grid; gap: 1rem; justify-items: start; }

.ticket-view { display: grid; }
.ticket-view-bar { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
.ticket-view-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ticket-view-stage { display: grid; place-items: center; background: #fff; border-radius: var(--r-m, 0.5rem); }
.ticket-view-stage:fullscreen { border-radius: 0; }
.ticket-view-image { max-width: 100%; max-height: 80dvh; object-fit: contain; }
.ticket-view-stage:fullscreen .ticket-view-image { max-height: 100dvh; }
.ticket-view-pdf { width: 100%; height: 80dvh; border: 0; }
.ticket-view-stage:fullscreen .ticket-view-pdf { height: 100dvh; }

.sticker-meter { display: block; width: calc(100% - 2 * var(--card-pad-x, 1rem)); margin: 0 auto 0.75rem; accent-color: var(--primary); }
.sticker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 0.5rem; margin: 0; padding: 0 var(--card-pad-x, 1rem) var(--card-pad-y, 1rem); list-style: none; }
.sticker { position: relative; display: grid; align-content: start; gap: 0.2rem; padding: 0.5rem 0.6rem; border: 1px dashed var(--border-strong); border-radius: var(--r-s); color: var(--muted); }
.sticker.is-heard { border-style: solid; border-color: var(--primary); background: var(--card-2); color: var(--text); }
.sticker-title { font-weight: 600; overflow-wrap: anywhere; }
.sticker-meta { font-size: var(--step--1); }
.sticker.is-new { box-shadow: 0 0 0.6rem var(--primary); }
.sticker-new { position: absolute; top: -0.4rem; right: 0.4rem; padding: 0 0.4rem; border-radius: var(--r-s); background: var(--primary); color: var(--on-primary); font-size: var(--step--1); text-transform: uppercase; }

/* Milestone passes */
.pass-wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 1rem; padding: 0 var(--card-pad-x, 1rem) var(--card-pad-y, 1rem); }
.pass { display: grid; justify-items: center; }
.pass-lanyard { width: 0.5rem; height: 1.5rem; background: var(--primary); border-radius: 0 0 var(--r-s) var(--r-s); }
.pass-card { display: grid; gap: 0.35rem; justify-items: center; text-align: center; width: 100%; padding: 0.9rem 0.75rem; border: 1px solid var(--border-strong); border-radius: var(--r-m, 0.5rem); background: var(--card-2); }
.pass.is-unlocked .pass-card { border-color: var(--primary); box-shadow: 0 0 0.6rem color-mix(in srgb, var(--primary) 35%, transparent); }
.pass.is-locked .pass-card { border-style: dashed; color: var(--muted); }
.pass-icon { font-size: var(--step-2); }
.pass-name { margin: 0; font-size: var(--step-0); }
.pass-text { margin: 0; font-size: var(--step--1); }
.pass-meta { font-size: var(--step--1); color: var(--muted); }
.pass-progress { width: 100%; accent-color: var(--primary); }

/* Map timelapse */
.timelapse { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem; }
.timelapse [hidden] { display: none; }
.timelapse-year { font-size: 1.5rem; font-variant-numeric: tabular-nums; min-width: 4ch; }
.timelapse-scrub { flex: 1 1 12rem; min-width: 8rem; accent-color: var(--primary); }
.timelapse-figure { font-variant-numeric: tabular-nums; white-space: nowrap; }
.tl-pin { stroke: var(--card); stroke-width: 2px; fill-opacity: .9; }
.tl-pin--concert { fill: var(--concert); }
.tl-pin--festival { fill: var(--festival); filter: drop-shadow(0 0 6px var(--festival)); }
.tl-arc { fill: none; stroke-linecap: round; }
.tl-arc--concert { stroke: var(--concert); }
.tl-arc--festival { stroke: var(--festival); }
.tl-home { fill: var(--text); stroke: var(--card); }
@media (prefers-reduced-motion: no-preference) {
    .tl-pin { animation: tl-pop .6s ease-out; transform-box: fill-box; transform-origin: center; }
}
@keyframes tl-pop { from { transform: scale(2.6); fill-opacity: .2; } to { transform: scale(1); fill-opacity: .9; } }
