/* ==========================================================================
   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`
   (styles/vendor/{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);

    /* 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
   -------------------------------------------------------------------------- */
body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

main.container { flex: 1; width: 100%; }

h1, h2, h3, h4, h5, h6 { font-family: var(--font-display); }

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

/* --------------------------------------------------------------------------
   3. Top bar: menus
   -------------------------------------------------------------------------- */
.brand { white-space: nowrap; font-family: var(--font-display); }

/* The open state of shared/ui's `.menu`, which ships the closed and hover
   states only. */
.menu[open] > summary { color: var(--text); background: var(--card-2); }

/* Two of the menus live in the nav rather than the user area, so their
   summaries have to read as nav links: same pill, same active treatment as
   shared/ui's `.topnav a`. */
.topnav .menu > summary {
    padding: 0.35rem 0.7rem;
    border: none;
    border-radius: 999px;
    font-size: 0.88rem;
}
.topnav .menu > summary:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
.topnav .menu.is-active > summary,
.topnav .menu[open] > summary {
    color: var(--primary-ink);
    background: var(--primary-soft);
    font-weight: 600;
}
.topnav .menu-list { right: auto; left: 0; }

/* Count of open to-dos, riding along in the nav link. */
.nav-count {
    min-width: 1.25rem;
    padding: 0.05rem 0.35rem;
    border-radius: 999px;
    background: var(--warn);
    color: var(--on-primary);
    font-size: 0.72rem;
    font-weight: 700;
    text-align: center;
}

/* Both rules that used to live here are gone with the nav drawer: the topnav
   is no longer a horizontal scroller on narrow screens (so nothing clips an
   absolutely positioned menu, and menus inside the drawer are accordions
   anyway), and the username has room to show rather than being hidden. */

/* --------------------------------------------------------------------------
   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; }

/* 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, for pages reachable from more than one place. */
.btn-back-group { display: flex; flex-direction: column; gap: 0.2rem; flex: none; }
.btn-back-link {
    display: inline-flex;
    align-items: center;
    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); }

.btn-back-bands > summary { cursor: pointer; list-style: none; }
.btn-back-bands > summary::-webkit-details-marker { display: none; }
.btn-back-bands > summary::marker { content: ''; }
.btn-back-bands-list {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.25rem 0 0 0.9rem;
}

/* --------------------------------------------------------------------------
   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 .bi { 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; margin: 0.6rem 0 0; 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 .bi { opacity: 0.5; font-size: 0.75em; }
.sort-link.is-sorted .bi { opacity: 1; color: var(--primary); }

/* 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: numbered, tight, and secondary to the band name above it. */
.setlist {
    margin: 0 0 0.5rem;
    padding-left: 1.2rem;
    font-size: 0.85rem;
    color: var(--text-2);
}

/* --------------------------------------------------------------------------
   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. */

/* The dashboard's six figures want to stay on one line on a wide screen and
   pair up on a phone, rather than following the default tile width. */
.dashboard-tiles { --tile-min: 9rem; margin-bottom: 1.25rem; }

/* 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; }
.show-main { flex: 1; min-width: 0; overflow: hidden; }

/* Two flashes on the dashboard carry structure rather than one line of text. */
.on-this-day,
.todo-flash { align-items: flex-start; }
.todo-flash { flex-wrap: wrap; }
.todo-flash .grow { flex: 1 1 15rem; }
.todo-flash .row-wrap { gap: 0.25rem 1rem; }
.on-this-day a,
.todo-flash 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; }

/* Six figures side by side, wrapping in pairs on a phone. */
.milestones {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(8rem, 100%), 1fr));
    gap: 1rem;
    text-align: center;
}

/* 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
   -------------------------------------------------------------------------- */
/* Richer than shared/ui's .empty-note: an icon, a heading, an explanation and
   usually the action that fixes it. */
.empty-state {
    border: 1.5px dashed var(--border-strong);
    border-radius: var(--r-m);
    background: var(--card);
    text-align: center;
    padding: 3.5rem 2rem;
}
.empty-state-icon { font-size: 2.75rem; margin-bottom: 0.75rem; color: var(--muted); }
/* "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); }
.empty-state h2 { margin: 0 0 0.35rem; }
.empty-state p { color: var(--muted); max-width: 34rem; margin: 0 auto 1.25rem; }

/* --------------------------------------------------------------------------
   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; }

/* 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); }
.badge-warn { --badge-accent: var(--warn); }
.badge-pos { --badge-accent: var(--pos); }
.badge-neg { --badge-accent: var(--neg); }
.badge-wishlist { --badge-accent: var(--warn); }

/* Radar card highlight for wishlist-matched shows. */
.card--wishlist { outline: 2px solid var(--warn); }
.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; }

/* --------------------------------------------------------------------------
   14. Statistics: activity tile map
   -------------------------------------------------------------------------- */
.activity-wrap { overflow-x: auto; padding-bottom: 0.25rem; }
.activity-months-row { display: flex; gap: 2px; align-items: flex-start; }
.activity-month-col { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.activity-month-label { font-size: 0.6rem; color: var(--muted); white-space: nowrap; line-height: 1; }
.activity-month-cells { display: flex; flex-direction: column; gap: 2px; }

.activity-cell { width: 14px; height: 14px; border-radius: 2px; 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 { color: var(--neg); }

/* 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 below the main field grid — the bands on a concert, the
   acts on a festival. */
.form-section { margin-top: 2rem; }
.form-section > .row-between { margin-bottom: 0.75rem; }

/* 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 proposed running order: numbered rows of an editable title plus the
   controls that reorder or drop it. */
.paste-rows {
    margin: 0 0 1rem;
    padding-left: 1.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.paste-rows li { display: flex; align-items: center; gap: 0.4rem; }
.paste-rows 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);
}
.paste-orphans { margin-bottom: 1rem; }
.paste-orphans .checkbox-row { margin-top: 0.3rem; }

/* One song in the setlist editor: tick, title, running-order number. */
.song-row { gap: 0.75rem; }
.song-row input[type="checkbox"] { accent-color: var(--primary); flex: none; }
.song-row label { flex: 1; min-width: 0; cursor: pointer; }
.song-row .setlist-position {
    width: 5rem;
    flex: none;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    font-size: 0.85rem;
    background: var(--card);
    color: var(--text);
}

/* 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; }

.flash .flash-close {
    margin-left: auto;
    padding: 0 0.25rem;
    border: none;
    background: none;
    color: inherit;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.6;
}
.flash .flash-close:hover { opacity: 1; }

/* --------------------------------------------------------------------------
   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 .bi { 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);
}
.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;
}
.inline-rating {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: 0.5rem;
}
.rating-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.2rem;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    background: var(--card);
    color: var(--text);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    line-height: 1;
}
.rating-pill:hover {
    border-color: var(--primary);
    background: var(--primary);
    color: var(--on-primary);
}

/* --------------------------------------------------------------------------
   21. 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; }

    /* The rating pills are a touch target, not a label. */
    .rating-pill { min-width: var(--tap-min); min-height: var(--tap-min); }
}

/* --------------------------------------------------------------------------
   22. In-app calendar view
   -------------------------------------------------------------------------- */
.cal-nav {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding: 0.6rem 0.75rem;
}

.cal-nav-center { flex: 1; text-align: center; }

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

.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: 3.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-day-num {
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--text-2);
    line-height: 1;
}

.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(--primary); }

@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; }
}
