/* ==========================================================================
   Creek Crosby — "Paper & Oxblood"

   A screenprint idiom taken from the band's own artwork: the "Ashtray's Full,
   Bottle's Empty" EP sleeve (oxblood halftone on white) and the Da Rock flyer
   (hot-rod red on white). Six inks, no gradients, no soft shadows, no radii.

   ---------------------------------------------------------------------------
   JS CONTRACT — these names are queried or injected by static/js/main.js.
   Renaming any of them silently breaks behaviour. Do not "clean up" as unused.

   IDs        #header #nav #lightbox-modal #lightbox-image #events-results
   Queried    .header .scrolled .mobile-menu-toggle .mobile-open .search-input
              .band-content .band-grid .band-grid.sortable .band-member
              .band-member.draggable .drag-handle .dragging .drop-zone
              .drag-over .contact-content .event-image-container
              .media-subsection .no-media .media-image .loaded
              .lightbox-trigger .lightbox-title .lightbox-image .lightbox-open
              .show .video-thumbnail .play-button .uploading .upload-progress
              .editing .fast-loading .loading
   Injected   .edit-input .edit-buttons .edit-form .save-btn .cancel-btn
   by JS      .format-btn .formatting-toolbar .formatting-help
              .video-frame-status .video-frame-status--error
              .video-frame-status-hint
   Data attrs data-original-text data-event-id data-section-id data-member-id
              data-index data-field data-value data-edit-url data-video-url
              data-embed-url data-lightbox-src data-lightbox-title
   ---------------------------------------------------------------------------

   Cascade layers order the file so staff chrome always beats component styles
   without a single !important. A browser with no @layer support drops the lot;
   the site already requires modern JS, so that floor is acceptable.
   ========================================================================== */

@layer reset, tokens, base, layout, components, staff, utilities;

/* ---------- Fonts: self-hosted, so no cross-origin request and no CSP
   dependency when the site is embedded in the portfolio. All three faces are
   preloaded in base.html -- same-origin and preloaded, they arrive before
   first paint, so `swap` never actually swaps and nothing reflows. The italic
   carries a lot of the band history, so it is preloaded too. ----------- */

@font-face {
    font-family: 'Archivo';
    src: url('../fonts/archivo-var.woff2') format('woff2-variations');
    font-weight: 400 900;
    font-stretch: 62% 125%;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Newsreader';
    src: url('../fonts/newsreader-var.woff2') format('woff2-variations');
    font-weight: 300 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Newsreader';
    src: url('../fonts/newsreader-italic-var.woff2') format('woff2-variations');
    font-weight: 300 500;
    font-style: italic;
    font-display: swap;
}

@layer tokens {
    :root {
        /* --- Six inks. Sampled from the band's own artwork. --- */
        --paper: #F7F6F3;
        --paper-shade: #EAE7E1;
        --ink: #000000;
        --ink-muted: #57514C;
        --oxblood: #3F1113;
        --oxblood-deep: #2A0B0D;
        --hotrod: #FF1717;
        --rule-ink: #D6D1C9;

        /* Contrast on --paper: ink 19.6:1, oxblood 14.9:1, muted 7.4:1.
           --hotrod is 3.6:1 — it NEVER carries small text. Large display
           text (>=24px) or non-text only. Budget: three uses per page. */

        --font-display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
        --font-prose: 'Newsreader', Georgia, 'Times New Roman', serif;

        --step--1: 0.875rem;
        --step-0: 1.0625rem;
        --step-1: 1.3125rem;
        --step-2: 1.625rem;
        --step-3: clamp(1.9rem, 1.35rem + 2.2vw, 2.6rem);
        --step-4: clamp(2.5rem, 1.1rem + 6vw, 5.5rem);

        --lh-prose: 1.65;
        --lh-tight: 1.15;
        --track-tight: -0.02em;
        --track-label: 0.08em;
        --measure: 66ch;

        --space-3xs: 0.25rem;
        --space-2xs: 0.5rem;
        --space-xs: 0.75rem;
        --space-s: 1rem;
        --space-m: 1.5rem;
        --space-l: 2.5rem;
        --space-xl: 4rem;
        --space-2xl: 6rem;
        --section-y: clamp(3.5rem, 8vw, 6.5rem);

        --rule-hair: 1px solid var(--rule-ink);
        --rule-mid: 2px solid var(--oxblood);
        --rule-heavy: 4px solid var(--oxblood);

        --header-h: 4rem;
        --container: 72rem;

        --t-fast: 120ms cubic-bezier(0.2, 0, 0.2, 1);
        --t-base: 220ms cubic-bezier(0.2, 0, 0.2, 1);
    }
}

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

    body,
    h1, h2, h3, h4, h5, h6,
    p, figure, blockquote, dl, dd, ol, ul {
        margin: 0;
    }

    ol[class], ul[class] {
        list-style: none;
        padding: 0;
    }

    img, picture, video, canvas, svg {
        display: block;
        max-width: 100%;
    }

    input, button, textarea, select {
        font: inherit;
        color: inherit;
    }

    /* Long pasted URLs in the markdown-authored About copy are the other
       source of horizontal overflow besides viewport units. */
    p, li, dd, h1, h2, h3, h4 {
        overflow-wrap: break-word;
    }
}

@layer base {
    html {
        /* clip, not hidden: `overflow-x: hidden` on body makes it a scroll
           container, which breaks position:sticky on the header. */
        overflow-x: clip;
        scroll-behavior: smooth;
        scroll-padding-top: calc(var(--header-h) + var(--space-s));
        -webkit-text-size-adjust: 100%;
    }

    body {
        background: var(--paper);
        color: var(--ink);
        font-family: var(--font-prose);
        font-size: var(--step-0);
        line-height: var(--lh-prose);
        font-synthesis-weight: none;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    h1, h2, h3, h4, h5, h6 {
        font-family: var(--font-display);
        line-height: var(--lh-tight);
        font-weight: 700;
        text-wrap: balance;
    }

    a {
        color: var(--oxblood);
        text-decoration-thickness: 2px;
        text-underline-offset: 0.18em;
    }

    a:hover {
        text-decoration-color: var(--hotrod);
    }

    ::selection {
        background: var(--oxblood);
        color: var(--paper);
    }

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

    hr {
        border: 0;
        border-top: var(--rule-hair);
        margin: var(--space-l) 0;
    }
}

@layer layout {
    .container {
        width: 100%;
        max-width: var(--container);
        margin-inline: auto;
        padding-inline: clamp(1rem, 4vw, 2rem);
    }

    .section {
        padding-block: var(--section-y);
    }

    .section--shade {
        background: var(--paper-shade);
    }

    /* Section heading: heavy poster caps with a left-anchored rule the width
       of the text. Promoted from media.html, the best-written thing here. */
    .section-title {
        font-size: var(--step-3);
        font-weight: 800;
        font-stretch: 88%;
        text-transform: uppercase;
        letter-spacing: var(--track-tight);
        margin-bottom: var(--space-l);
    }

    .section-title::after {
        content: "";
        display: block;
        width: 3.5rem;
        margin-top: var(--space-xs);
        border-top: var(--rule-heavy);
    }

    .measure {
        max-width: var(--measure);
    }

    .grid-auto {
        display: grid;
        gap: var(--space-l);
        grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
    }

    /* Grid and flex children need this or long words force overflow. */
    .grid-auto > * {
        min-width: 0;
    }
}

@layer components {
    /* ---------------------------------------------------------- skip link */
    .skip-link {
        position: absolute;
        left: var(--space-s);
        top: -4rem;
        z-index: 200;
        padding: var(--space-2xs) var(--space-s);
        background: var(--oxblood);
        color: var(--paper);
        font-family: var(--font-display);
        text-decoration: none;
        transition: top var(--t-fast);
    }

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

    /* -------------------------------------------------------------- header */
    .header {
        position: sticky;
        top: 0;
        z-index: 100;
        background: var(--paper);
        border-bottom: var(--rule-hair);
        transition: border-color var(--t-base);
    }

    .header.scrolled {
        border-bottom-color: var(--oxblood);
    }

    .header-container {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-s);
        min-height: var(--header-h);
    }

    .logo a {
        font-family: var(--font-display);
        font-size: 1.25rem;
        font-weight: 800;
        font-stretch: 88%;
        text-transform: uppercase;
        letter-spacing: var(--track-tight);
        color: var(--ink);
        text-decoration: none;
        white-space: nowrap;
    }

    .nav-list {
        display: flex;
        gap: var(--space-m);
        list-style: none;
        margin: 0;
        padding: 0;
    }

    .nav-list a {
        display: block;
        position: relative;
        padding-block: var(--space-2xs);
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
        text-decoration: none;
    }

    .nav-list a:hover {
        color: var(--oxblood);
    }

    /* Red use 1 of 3. A bar, not text — 3.6:1 is legal for non-text. */
    .nav-list a::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        height: 3px;
        background: var(--hotrod);
        transform: scaleX(0);
        transform-origin: left;
        transition: transform var(--t-fast);
    }

    .nav-list a:hover::after,
    .nav-list a:focus-visible::after {
        transform: scaleX(1);
    }

    .mobile-menu-toggle {
        display: grid;
        place-items: center;
        /* Above the open drawer (z-index 105), or the drawer covers the very
           control you need to close it. */
        position: relative;
        z-index: 110;
        width: 2.75rem;
        height: 2.75rem;
        padding: 0;
        background: none;
        border: 2px solid var(--ink);
        cursor: pointer;
    }

    .hamburger,
    .hamburger::before,
    .hamburger::after {
        display: block;
        width: 1.25rem;
        height: 2px;
        background: var(--ink);
        transition: transform var(--t-fast), opacity var(--t-fast);
    }

    .hamburger::before,
    .hamburger::after {
        content: "";
        position: absolute;
    }

    .hamburger::before {
        transform: translateY(-6px);
    }

    .hamburger::after {
        transform: translateY(6px);
    }

    /* Kept out of any media query on purpose. These only apply when
       .mobile-open is set, which only happens while the toggle is visible —
       the old sheet scoped them to 768px while the drawer lived at 1200px,
       so between 769 and 1200 the menu opened but never animated. */
    .mobile-menu-toggle.mobile-open .hamburger {
        background: transparent;
    }

    .mobile-menu-toggle.mobile-open .hamburger::before {
        transform: translateY(0) rotate(45deg);
    }

    .mobile-menu-toggle.mobile-open .hamburger::after {
        transform: translateY(0) rotate(-45deg);
    }

    /* ---------------------------------------------------------------- hero */
    .hero {
        padding-block: clamp(2rem, 6vw, 4rem) 0;
    }

    .hero-genres {
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
    }

    /* The one loud thing on the page. */
    .hero-wordmark {
        font-size: var(--step-4);
        font-weight: 900;
        font-stretch: 80%;
        line-height: 0.9;
        text-transform: uppercase;
        letter-spacing: var(--track-tight);
        margin-block: var(--space-xs) 0;
    }

    /* Red use 2 of 3. */
    .hero-rule {
        width: min(100%, 18rem);
        height: 4px;
        background: var(--hotrod);
        margin-block: var(--space-s) var(--space-m);
    }

    .hero-origin {
        max-width: 52ch;
        font-size: var(--step-1);
        color: var(--ink-muted);
        text-wrap: balance;
    }

    .hero-photo {
        margin-top: var(--space-l);
    }

    .hero-photo img {
        width: 100%;
        height: auto;
        filter: grayscale(1);
    }

    .hero-actions {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-m);
        margin-top: var(--space-l);
    }

    /* -------------------------------------------------------------- buttons */
    .btn {
        display: inline-block;
        padding: var(--space-xs) var(--space-m);
        border: 2px solid var(--oxblood);
        background: none;
        color: var(--oxblood);
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        text-decoration: none;
        text-align: center;
        cursor: pointer;
        transition: background-color var(--t-fast), color var(--t-fast);
    }

    .btn:hover {
        background: var(--oxblood);
        color: var(--paper);
    }

    .btn-primary {
        background: var(--oxblood);
        color: var(--paper);
    }

    .btn-primary:hover {
        background: var(--oxblood-deep);
        border-color: var(--oxblood-deep);
    }

    /* Used in base.html and seven partials; the old sheet never defined it. */
    .btn-outline {
        background: none;
        color: var(--oxblood);
    }

    .btn-sm {
        padding: var(--space-3xs) var(--space-2xs);
        font-size: 0.75rem;
    }

    /* A text link that reads as a second-tier action next to a filled button. */
    .link-rule {
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink);
        text-decoration: none;
        border-bottom: var(--rule-mid);
        padding-bottom: 2px;
    }

    .link-rule:hover {
        color: var(--oxblood);
        border-bottom-color: var(--hotrod);
    }

    /* --------------------------------------------------------------- about */
    /* Outer HTMX slot is .about-body; the inner prose div is .prose. The old
       sheet called both .about-content, so a delete targeting .about-content
       replaced the wrapper instead of the list. */
    .about-body > * + * {
        margin-top: var(--space-xl);
    }

    .about-item {
        position: relative;
    }

    .about-item h3 {
        font-size: var(--step-2);
        font-weight: 800;
        margin-bottom: var(--space-m);
    }

    .prose {
        max-width: var(--measure);
    }

    .prose > * + * {
        margin-top: var(--space-s);
    }

    .prose h2,
    .prose h3,
    .prose h4 {
        font-size: var(--step-0);
        font-weight: 700;
        margin-top: var(--space-l);
        margin-bottom: var(--space-2xs);
    }

    .prose h2 {
        font-size: var(--step-1);
        color: var(--oxblood);
    }

    .prose strong {
        font-weight: 700;
    }

    .prose ul,
    .prose ol {
        padding-left: 1.25rem;
    }

    .prose li + li {
        margin-top: var(--space-3xs);
    }

    .prose li::marker {
        color: var(--oxblood);
    }

    .prose blockquote {
        margin-block: var(--space-l);
        padding-left: var(--space-m);
        border-left: var(--rule-heavy);
        font-size: var(--step-1);
        font-style: italic;
    }

    .prose hr {
        margin-block: var(--space-l);
    }

    .prose code {
        font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
        font-size: 0.9em;
        background: var(--paper-shade);
        padding: 0.1em 0.3em;
    }

    /* -------------------------------------------------------------- events */
    .events-toolbar {
        margin-bottom: var(--space-l);
    }

    .search-input {
        width: min(100%, 22rem);
        padding: var(--space-2xs) var(--space-xs);
        background: var(--paper);
        border: var(--rule-hair);
        border-bottom: 2px solid var(--ink);
        font-family: var(--font-display);
        font-size: var(--step--1);
    }

    .search-input:focus {
        outline: none;
        border-bottom-color: var(--oxblood);
    }

    .search-input::placeholder {
        color: var(--ink-muted);
    }

    .events-heading {
        font-size: var(--step--1);
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
        padding-bottom: var(--space-2xs);
        border-bottom: var(--rule-hair);
        margin-bottom: var(--space-2xs);
    }

    .events-heading + .events-list {
        margin-top: 0;
    }

    .events-list {
        list-style: none;
        margin: 0 0 var(--space-xl);
        padding: 0;
    }

    /* A gig listing, not a card grid. The columns label themselves, which is
       what makes the calendar/clock/pin icons unnecessary rather than
       replaced. This is how the band sets it on their own posters. */
    .event-row {
        display: grid;
        grid-template-columns: minmax(7rem, 9rem) 1fr;
        gap: var(--space-2xs) var(--space-m);
        align-items: baseline;
        padding-block: var(--space-m);
        border-bottom: var(--rule-hair);
    }

    .event-row:first-child {
        border-top: var(--rule-hair);
    }

    .event-date {
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 700;
        font-variant-numeric: tabular-nums;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        color: var(--ink);
    }

    .event-body > * + * {
        margin-top: var(--space-3xs);
    }

    .event-title {
        font-size: var(--step-1);
        font-weight: 700;
    }

    .event-where {
        font-family: var(--font-display);
        font-size: var(--step--1);
        color: var(--ink-muted);
    }

    .event-description {
        max-width: var(--measure);
        margin-top: var(--space-2xs);
    }

    .event-description > * + * {
        margin-top: var(--space-2xs);
    }

    .event-figure {
        margin-top: var(--space-s);
    }

    /* Gig posters are artwork in their own right -- the hot-rod flyer, the
       sunburst pin-up. They keep their colour and are never cropped; the
       uploads are portrait (~0.66) and a fixed ratio cut most of them away.
       Bounded on both axes so a tall poster can't run away with the row. */
    .event-figure img {
        max-width: min(100%, 22rem);
        max-height: 26rem;
        width: auto;
        height: auto;
    }

    /* The next upcoming show is the only thing on the page that earns red. */
    .event-row--next .event-flag {
        display: inline-block;
        margin-bottom: var(--space-3xs);
        font-family: var(--font-display);
        font-size: 1.5rem;
        font-weight: 800;
        font-stretch: 85%;
        text-transform: uppercase;
        letter-spacing: var(--track-tight);
        color: var(--hotrod);
    }

    .events-empty {
        max-width: var(--measure);
        padding-block: var(--space-m) var(--space-xl);
    }

    .events-empty p {
        font-size: var(--step-1);
    }

    .events-empty .link-rule {
        margin-top: var(--space-m);
        display: inline-block;
    }

    .loading-more {
        padding: var(--space-m) 0;
        font-family: var(--font-display);
        font-size: var(--step--1);
        color: var(--ink-muted);
    }

    /* ---------------------------------------------------------------- band */
    .band-grid {
        display: grid;
        gap: var(--space-l);
        grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
    }

    .band-grid > * {
        min-width: 0;
    }

    .band-member {
        position: relative;
    }

    .member-image {
        position: relative;
        aspect-ratio: 3 / 4;
        background: var(--paper-shade);
        overflow: hidden;
    }

    /* Straight black and white -- no ink, no blend. Done in CSS rather than
       pre-processing because these are staff uploads at runtime and the
       originals must stay intact for the lightbox. */
    .member-image img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        filter: grayscale(1);
    }

    .member-placeholder {
        display: grid;
        place-items: center;
        background: var(--paper-shade);
    }

    .initials {
        font-family: var(--font-display);
        font-size: 2rem;
        font-weight: 800;
        color: var(--ink-muted);
        letter-spacing: var(--track-tight);
    }

    .member-info {
        margin-top: var(--space-s);
    }

    .member-info h3 {
        font-size: var(--step-1);
        font-weight: 700;
    }

    .member-position {
        font-family: var(--font-display);
        font-size: var(--step--1);
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
        margin-top: var(--space-3xs);
    }

    .member-bio {
        margin-top: var(--space-2xs);
    }

    /* --------------------------------------------------------------- media */
    .media-subsection + .media-subsection {
        margin-top: var(--space-2xl);
    }

    .media-subsection-header {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--space-s);
        flex-wrap: wrap;
        padding-bottom: var(--space-2xs);
        border-bottom: var(--rule-hair);
        margin-bottom: var(--space-m);
    }

    .media-subsection-title {
        font-size: var(--step-2);
        font-weight: 800;
        text-transform: uppercase;
        letter-spacing: var(--track-tight);
    }

    .media-subsection-count {
        font-family: var(--font-display);
        font-size: var(--step--1);
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
    }

    /* Music: a numbered track list. 01/02 markers are legitimate here —
       this genuinely is a sequence. */
    .audio-track-list {
        list-style: none;
        margin: 0;
        padding: 0;
    }

    .audio-track {
        display: grid;
        grid-template-columns: 2.25rem 1fr;
        gap: var(--space-2xs) var(--space-m);
        align-items: baseline;
        padding-block: var(--space-m);
        border-bottom: var(--rule-hair);
    }

    .audio-track:first-child {
        border-top: var(--rule-hair);
    }

    .audio-track-index {
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 700;
        font-variant-numeric: tabular-nums;
        color: var(--ink-muted);
    }

    .audio-track-title {
        font-family: var(--font-display);
        font-size: var(--step-1);
        font-weight: 700;
    }

    .audio-track-description {
        font-size: var(--step--1);
        color: var(--ink-muted);
        margin-top: var(--space-3xs);
    }

    .audio-track-player {
        grid-column: 2;
        width: 100%;
        max-width: 32rem;
        margin-top: var(--space-2xs);
    }

    .video-grid {
        display: grid;
        gap: var(--space-l);
        grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
    }

    .video-grid > * {
        min-width: 0;
    }

    .video-frame {
        position: relative;
        aspect-ratio: 16 / 9;
        background: var(--ink);
        overflow: hidden;
    }

    .video-frame img,
    .video-frame video,
    .video-frame iframe {
        width: 100%;
        height: 100%;
        object-fit: cover;
        border: 0;
    }

    .video-thumbnail {
        position: absolute;
        inset: 0;
        cursor: pointer;
        border: 0;
        padding: 0;
        background: var(--ink);
        width: 100%;
    }

    .video-placeholder {
        position: absolute;
        inset: 0;
        display: grid;
        place-items: center;
        align-content: center;
        gap: var(--space-s);
        background: var(--oxblood-deep);
    }

    .video-placeholder-hint {
        font-family: var(--font-display);
        font-size: var(--step--1);
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: color-mix(in srgb, var(--paper) 70%, transparent);
    }

    .play-button {
        display: grid;
        place-items: center;
        width: 3.5rem;
        height: 3.5rem;
        padding: 0;
        background: var(--paper);
        border: 0;
        color: var(--oxblood-deep);
        cursor: pointer;
        transition: background-color var(--t-fast);
    }

    .play-button svg {
        width: 1.1rem;
        height: 1.1rem;
        fill: currentColor;
    }

    .video-thumbnail:hover .play-button,
    .play-button:hover {
        background: var(--hotrod);
        color: var(--paper);
    }

    .media-info {
        margin-top: var(--space-s);
    }

    .media-title {
        font-family: var(--font-display);
        font-size: var(--step-1);
        font-weight: 700;
    }

    .media-description {
        font-size: var(--step--1);
        color: var(--ink-muted);
        margin-top: var(--space-3xs);
    }

    .gallery-grid {
        display: grid;
        gap: var(--space-m);
        grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 1fr));
    }

    .gallery-grid > * {
        min-width: 0;
    }

    .gallery-item {
        margin: 0;
    }

    /* Sleeve and poster artwork, shown as it was printed. `contain` keeps
       the grid square without cutting into the artwork -- the matte is the
       letterbox, which is how a print would be mounted anyway. */
    .gallery-item img {
        width: 100%;
        height: 100%;
        aspect-ratio: 1;
        object-fit: contain;
        background: var(--paper-shade);
        cursor: zoom-in;
    }

    .gallery-item-caption {
        font-family: var(--font-display);
        font-size: 0.75rem;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
        margin-top: var(--space-2xs);
    }

    .no-media,
    .no-events,
    .no-band {
        max-width: var(--measure);
        color: var(--ink-muted);
    }

    .fast-loading,
    .loading {
        font-family: var(--font-display);
        font-size: var(--step--1);
        color: var(--ink-muted);
    }

    .video-frame-status {
        position: absolute;
        inset: 0;
        display: grid;
        place-items: center;
        gap: var(--space-2xs);
        background: var(--paper-shade);
        padding: var(--space-s);
        text-align: center;
    }

    .video-frame-status--error {
        color: var(--oxblood);
    }

    .video-frame-status-hint {
        font-size: 0.75rem;
        color: var(--ink-muted);
    }

    /* ---------------------------------------------------------------- shop */
    .section-note {
        max-width: var(--measure);
        margin-bottom: var(--space-l);
        color: var(--ink-muted);
    }

    .merchandise-grid {
        display: grid;
        gap: var(--space-l);
        grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
    }

    .merchandise-grid > * {
        min-width: 0;
    }

    .merchandise-item-image {
        aspect-ratio: 1;
        background: var(--paper-shade);
    }

    .merchandise-item-image img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        mix-blend-mode: multiply;
    }

    .merchandise-item-card h3 {
        font-family: var(--font-display);
        font-size: var(--step-0);
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        margin-top: var(--space-s);
    }

    /* ------------------------------------------------------------- contact */
    .contact-content {
        display: grid;
        gap: var(--space-xl);
        align-items: start;
    }

    /* Labelled rows, so the envelope/phone/pin emoji have nothing to do. */
    .contact-list {
        display: grid;
        grid-template-columns: max-content 1fr;
        gap: var(--space-2xs) var(--space-m);
        margin: 0;
        max-width: var(--measure);
    }

    .contact-list dt {
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
        padding-top: 0.35em;
    }

    .contact-list dd {
        margin: 0;
        font-size: var(--step-1);
        overflow-wrap: anywhere;
    }

    .contact-list address {
        font-style: normal;
    }

    .contact-list a {
        color: var(--ink);
        text-decoration-color: var(--rule-ink);
    }

    .contact-list a:hover {
        color: var(--oxblood);
        text-decoration-color: var(--hotrod);
    }

    .social-links {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-m);
        margin-top: var(--space-l);
        padding: 0;
        list-style: none;
    }

    .social-links a {
        display: inline-flex;
        align-items: center;
        gap: var(--space-2xs);
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink);
        text-decoration: none;
        border-bottom: var(--rule-mid);
        padding-bottom: 2px;
    }

    .social-links a:hover {
        color: var(--oxblood);
        border-bottom-color: var(--hotrod);
    }

    .social-links svg {
        width: 1rem;
        height: 1rem;
        fill: currentColor;
    }

    /* --------------------------------------------------------------- forms */
    /* The contact form include is currently commented out in contact.html,
       but the view, form and email templates all still work — this is here so
       switching it back on is a one-line change, not a restyle. */
    .contact-form-container {
        max-width: 34rem;
    }

    .form-group + .form-group {
        margin-top: var(--space-m);
    }

    .form-group label {
        display: block;
        font-family: var(--font-display);
        font-size: var(--step--1);
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--ink-muted);
        margin-bottom: var(--space-3xs);
    }

    .form-control {
        width: 100%;
        padding: var(--space-2xs) var(--space-xs);
        background: var(--paper);
        border: var(--rule-hair);
        border-bottom: 2px solid var(--ink);
        font-family: var(--font-prose);
        font-size: var(--step-0);
    }

    .form-control:focus {
        outline: none;
        border-bottom-color: var(--oxblood);
    }

    textarea.form-control {
        min-height: 9rem;
        resize: vertical;
    }

    .form-actions {
        margin-top: var(--space-m);
    }

    .form-error,
    .form-errors {
        color: var(--oxblood);
        font-size: var(--step--1);
        margin-top: var(--space-3xs);
    }

    .form-errors {
        padding: var(--space-xs);
        border-left: var(--rule-heavy);
        background: var(--paper-shade);
    }

    .contact-success,
    .success-message {
        max-width: var(--measure);
    }

    /* -------------------------------------------------------------- footer */
    /* A halftone dot screen on the ink slab — a real screenprint cue, two
       lines, no image. Replaces the old fixed-position grain overlay, which
       cost a composite on every scroll and was an iframe hazard. */
    .footer {
        padding-block: var(--space-xl);
        background-color: var(--oxblood-deep);
        background-image: radial-gradient(
            var(--paper) 1px,
            transparent 1.15px
        );
        background-size: 7px 7px;
        color: var(--paper);
        border-top: 4px solid var(--hotrod);
    }

    .footer-inner {
        background: var(--oxblood-deep);
        padding: var(--space-m);
        max-width: var(--measure);
    }

    .footer h2 {
        font-size: var(--step-2);
        font-weight: 800;
        text-transform: uppercase;
        letter-spacing: var(--track-tight);
        margin-bottom: var(--space-m);
    }

    .credits {
        list-style: none;
        margin: 0;
        padding: 0;
        display: grid;
        gap: var(--space-2xs);
    }

    .credits li {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-2xs);
        align-items: baseline;
    }

    .credit-role {
        font-family: var(--font-display);
        font-size: 0.75rem;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: color-mix(in srgb, var(--paper) 62%, transparent);
        min-width: 8rem;
    }

    .footer a {
        color: var(--paper);
        text-decoration-color: color-mix(in srgb, var(--paper) 45%, transparent);
    }

    .footer a:hover {
        text-decoration-color: var(--hotrod);
    }

    .footer :focus-visible {
        outline-color: var(--paper);
    }

    /* ------------------------------------------------------------ lightbox */
    /* Built as a template literal in main.js, not in any template. */
    .lightbox-modal {
        display: none;
        position: fixed;
        inset: 0;
        z-index: 9999;
        background: color-mix(in srgb, var(--oxblood-deep) 94%, transparent);
        padding: var(--space-m);
        overflow: auto;
    }

    /* JS toggles only the .show class -- it must not write an inline
       `display`, or that beats this rule and the flex fallback pins the
       image to the left (place-items sets justify-items, which flex
       ignores). justify-content is here so both layouts centre. */
    .lightbox-modal.show {
        display: grid;
        place-items: center;
        justify-content: center;
        align-content: center;
    }

    .lightbox-content {
        position: relative;
        max-width: min(100%, 60rem);
    }

    .lightbox-image {
        width: auto;
        max-width: 100%;
        max-height: 80vh;
        margin-inline: auto;
    }

    .lightbox-title {
        font-family: var(--font-display);
        font-size: var(--step--1);
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        color: var(--paper);
        text-align: center;
        margin-top: var(--space-s);
    }

    .lightbox-close {
        position: absolute;
        top: 0;
        right: 0;
        width: 2.75rem;
        height: 2.75rem;
        background: var(--paper);
        color: var(--ink);
        border: 0;
        font-family: var(--font-display);
        font-size: 1.25rem;
        line-height: 1;
        cursor: pointer;
    }

    .lightbox-close:hover {
        background: var(--hotrod);
        color: var(--paper);
    }
}

/* ==========================================================================
   Staff editing chrome. Declared after `components`, so a single-class rule
   here beats a component rule regardless of specificity — which is what
   retired all 14 !important declarations from the old stylesheet.
   Several of these classes are injected as HTML strings by main.js and
   appear in no template. See the JS contract at the top.
   ========================================================================== */

@layer staff {
    .staff-controls {
        position: fixed;
        right: var(--space-s);
        bottom: var(--space-s);
        left: var(--space-s);
        z-index: 1001;
        max-width: 20rem;
        margin-left: auto;
        background: var(--oxblood);
        color: var(--paper);
        padding: var(--space-xs);
    }

    .edit-mode-indicator {
        display: grid;
        gap: var(--space-2xs);
        font-family: var(--font-display);
        font-size: 0.75rem;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
    }

    .staff-buttons {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-2xs);
    }

    .staff-controls .btn {
        border-color: var(--paper);
        color: var(--paper);
        padding: var(--space-3xs) var(--space-2xs);
        font-size: 0.7rem;
    }

    .staff-controls .btn:hover {
        background: var(--paper);
        color: var(--oxblood);
    }

    /* Staff-only affordance. main.js only ever queries .editing, never
       .editable, so this class is a pure CSS hook. */
    .editable {
        cursor: text;
        outline: 1px dashed color-mix(in srgb, var(--oxblood) 45%, transparent);
        outline-offset: 3px;
        transition: outline-color var(--t-fast);
    }

    .editable:hover {
        outline: 2px dashed var(--oxblood);
    }

    .editable.editing {
        outline: none;
    }

    .edit-controls,
    .member-controls {
        display: flex;
        gap: var(--space-2xs);
        margin-bottom: var(--space-2xs);
    }

    .edit-form {
        display: grid;
        gap: var(--space-2xs);
    }

    .edit-input {
        width: 100%;
        padding: var(--space-2xs);
        background: var(--paper);
        border: 2px solid var(--oxblood);
        font-family: var(--font-prose);
        font-size: var(--step-0);
    }

    textarea.edit-input {
        min-height: 12rem;
        line-height: 1.5;
        resize: vertical;
    }

    .edit-buttons {
        display: flex;
        gap: var(--space-2xs);
    }

    .save-btn,
    .cancel-btn,
    .delete-btn {
        padding: var(--space-3xs) var(--space-xs);
        font-family: var(--font-display);
        font-size: 0.75rem;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        border: 2px solid var(--oxblood);
        background: none;
        color: var(--oxblood);
        cursor: pointer;
    }

    .save-btn {
        background: var(--oxblood);
        color: var(--paper);
    }

    /* No second semantic red on this site — the brand ink already is blood
       red, and a Bootstrap danger red beside it reads as a mistake. */
    .save-btn:hover,
    .cancel-btn:hover,
    .delete-btn:hover {
        background: var(--oxblood-deep);
        border-color: var(--oxblood-deep);
        color: var(--paper);
    }

    .formatting-toolbar {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-3xs);
    }

    .format-btn {
        min-width: 2rem;
        padding: var(--space-3xs) var(--space-2xs);
        background: var(--paper-shade);
        border: var(--rule-hair);
        font-family: var(--font-display);
        font-size: 0.75rem;
        cursor: pointer;
    }

    .format-btn:hover {
        background: var(--oxblood);
        color: var(--paper);
    }

    .formatting-help {
        font-size: 0.75rem;
        color: var(--ink-muted);
    }

    .image-edit-btn,
    .image-placeholder-btn {
        display: inline-block;
        margin-top: var(--space-2xs);
        padding: var(--space-3xs) var(--space-2xs);
        background: var(--paper);
        border: 2px solid var(--oxblood);
        color: var(--oxblood);
        font-family: var(--font-display);
        font-size: 0.75rem;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: var(--track-label);
        cursor: pointer;
    }

    .image-edit-btn:hover,
    .image-placeholder-btn:hover {
        background: var(--oxblood);
        color: var(--paper);
    }

    .uploading {
        opacity: 0.55;
    }

    .upload-progress {
        display: block;
        height: 3px;
        background: var(--hotrod);
        transition: width var(--t-fast);
    }

    .drag-handle {
        display: inline-grid;
        place-items: center;
        width: 1.75rem;
        height: 1.75rem;
        color: var(--ink-muted);
        cursor: grab;
    }

    .drag-handle svg {
        width: 0.9rem;
        height: 0.9rem;
        fill: currentColor;
    }

    .band-member.draggable {
        cursor: grab;
    }

    .band-member.dragging {
        opacity: 0.4;
        cursor: grabbing;
    }

    .band-member.drop-zone {
        outline: 1px dashed var(--rule-ink);
        outline-offset: var(--space-2xs);
    }

    .band-member.drag-over {
        outline: 2px solid var(--oxblood);
        outline-offset: var(--space-2xs);
    }

    .htmx-indicator {
        display: none;
        font-family: var(--font-display);
        font-size: var(--step--1);
        color: var(--ink-muted);
    }

    .htmx-request .htmx-indicator,
    .htmx-indicator.htmx-request {
        display: inline-block;
    }
}

@layer utilities {
    .sr-only {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }

    /* File inputs are hidden behind styled labels. Clipped rather than
       display:none so they stay focusable and reachable by keyboard. */
    .visually-hidden-input {
        position: absolute;
        width: 1px;
        height: 1px;
        opacity: 0;
        pointer-events: none;
    }
}

/* ==========================================================================
   Responsive. Three min-width breakpoints, mobile-first, replacing 24 ad-hoc
   queries (including thirteen separate max-width:768px blocks and a 767/768
   one-pixel gap). Everything else is intrinsic via auto-fill grids.
   Custom properties can't be used in media queries, hence the literals.
   ========================================================================== */

/* Below ~30rem the two-column gig row leaves too little for the venue. */
@media (max-width: 29.999rem) {
    .event-row {
        grid-template-columns: 1fr;
        gap: var(--space-2xs);
    }
}

/* --bp-m: 48rem / 768px */
@media (min-width: 48rem) {
    .event-row {
        grid-template-columns: 10rem 1fr;
        gap: var(--space-m) var(--space-l);
    }

    .hero-photo {
        margin-top: var(--space-xl);
    }
}

/* --bp-l: 64rem / 1024px — hamburger gives way to the horizontal nav.
   One breakpoint for both, so the toggle and its animation can never
   disagree the way they did between 769 and 1200px. */
@media (max-width: 63.999rem) {
    .nav {
        position: fixed;
        inset: 0 0 0 auto;
        width: min(80%, 20rem);
        height: 100%;
        z-index: 105;
        padding: calc(var(--header-h) + var(--space-m)) var(--space-m) var(--space-m);
        background: var(--paper);
        border-left: var(--rule-mid);
        transform: translateX(100%);
        transition: transform var(--t-base);
        overflow-y: auto;
    }

    .nav.mobile-open {
        transform: translateX(0);
    }

    .nav-list {
        flex-direction: column;
        gap: var(--space-s);
    }

    .nav-list a {
        font-size: var(--step-1);
    }

    .nav-list a::after {
        display: none;
    }

    body:has(.nav.mobile-open) {
        overflow: hidden;
    }
}

@media (min-width: 64rem) {
    .mobile-menu-toggle {
        display: none;
    }

    .contact-content:has(.contact-form) {
        grid-template-columns: 1.1fr 0.9fr;
        gap: var(--space-2xl);
    }
}

/* ==========================================================================
   Reduced motion. Unlayered and last: !important inverts layer precedence,
   so a layered block would lose. These are the only !important rules here.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
