/* This file: /home/bhhstudi/salt-music.org/public_html/css/saltStyle.css */
/* Last updated: 09/20/2026 15:08 EDT */

@charset "utf-8";

/* ============================================================
   CANONICAL SOURCE
   ------------------------------------------------------------
   Not a copy of /css/stringTherapyStyle.css. That file is 3400
   lines carrying rules for pages Salt does not have; this one is
   built section by section as Salt's pages are written.

   Carried over from it by design:
     - the :root token idea (renamed --st-* to --music-*)
     - the auth-page rules
     - the alphabet menu, the search bar, .btn-primary, and the
       face of the sheet/audio buttons -- Brian chose String
       Therapy's look (and its blue) for these on 09/01/2026,
       so those rules are its rules with --music-* names.

   Deliberately NOT carried over yet: setlist tables, tag chips,
   roulette, the audio modal. Each arrives with the include that
   emits its markup.

   Layout was settled in a mockup (songbook_mockup_v19.html,
   09/01/2026), which is the reference for every songbook rule
   below.
   ============================================================ */


/* ============================================================
   DESIGN TOKENS
   Every color on the site resolves to one of these. Change a
   value here and it ripples everywhere.

   Plain CSS cannot put a var() inside a media-query condition,
   so breakpoint NUMBERS still live in their own @media blocks.
   The site's breakpoints are 639px (stack) and 767px (narrow).
   ============================================================ */
:root {

    /* ---------- Brand: navy ---------- */
    --music-navy-logo:    #0c386e;  /* the logo artwork's own navy */
    --music-navy-dark:    #203961;  /* alphabet bar, active nav tab, narrow-screen nav */
    --music-navy:         #2a4b7f;  /* alphabet cell borders, hello-row link */

    /* ---------- Brand: blue ----------
       String Therapy's blues, adopted 09/01/2026: song title bars,
       the Search button, the current alphabet letter, links. */
    --music-blue:         #0066cc;  /* primary action, title bars */
    --music-blue-hover:   #004f9e;
    --music-title-aka:    #d9e8f7;  /* a song's AKA on a title bar: a pale step of --music-blue, 4.5:1 on it (white is 5.6:1) */
    --music-brand:        #2c68c7;  /* sheet/audio button border and text */
    --music-btn-face:     #ffffff;  /* sheet button fill (white, 09/01/2026) */
    --music-btn-face-audio: #e2e8f6; /* audio sample button fill (09/09/2026) */
    --music-btn-hover:    #cfdcf3;
    --music-btn-shadow:   #4875be6b;
    --music-blue-pale:    #e8f0fa;  /* pale fills */
    --music-alt-row:      #f5f8fc;
    --music-divider:      #2c68c752;

    /* ---------- Page background ----------
       The old Salt site's own sky blue, kept ONLY for the page
       gradient behind the white column. */
    --music-sky:          #b7dff2;  /* gradient mid */
    --music-sky-deep:     #4ba0d8;  /* gradient bottom, and below the fold */

    /* ---------- Brand: purple and green ----------
       Sampled from the band image (BG_top.webp). The image draws
       the diagonals; these are the flat fills the CSS uses where
       the image is not (fallback color, title band). */
    --music-purple:       #992e72;
    --music-purple-dark:  #801157;
    --music-green:        #7db524;
    --music-green-dark:   #68961e;

    /* ---------- Neutrals ---------- */
    --music-white:        #ffffff;
    --music-black:        #000000;
    --music-off-white:    #f1f3f4;  /* inset panels, hover fills */
    --music-gray-dark:    #333333;  /* headings, song block text */
    --music-gray-body:    #4d4d4d;  /* body copy, as measured on the live site */
    --music-gray-medium:  #555555;  /* muted text, disabled fills */
    --music-gray-light:   #cccccc;  /* input borders, dividers */
    --music-gray-icon:    #999999;  /* the search magnifier */
    --music-gray-shadow:  #8e99ad;  /* cool medium gray: the toolbar's drop shadow */

    /* ---------- Status ---------- */
    --music-danger:       #b00020;
    --music-error-bg:     #fde8ee;
    --music-success:      #2c7a2c;
    --music-success-bg:   #e8f4e8;  /* saved-flash fill on editor fields */
    /* Page-level outcome flashes (.message-success / .message-error):
       Excel's Good / Bad cell colors, chosen 09/12/2026 so the strip
       stands out from the page. */
    --music-flash-ok:     #2c7a2c;
    --music-flash-ok-bg:  #c6efce;
    --music-flash-bad:    #9c0006;
    --music-flash-bad-bg: #ffc7ce;
    --music-break-bg:     #fcf986;  /* a break band on a published setlist */

    /* ---------- Semantic aliases ----------
       Components reference these, not the brand names above, so a
       later palette change is one edit rather than a search for
       every place that happened to use purple. */
    --music-page-bg-top:  var(--music-white);
    --music-page-bg-btm:  var(--music-sky-deep);
    --music-text:         var(--music-gray-body);
    --music-heading:      var(--music-gray-dark);
    --music-link:         var(--music-blue);        /* 5.9:1 on white */
    --music-link-hover:   var(--music-blue-hover);  /* 8.0:1 on white */
    --music-focus-ring:   var(--music-navy-dark);
    --music-card-bg:      var(--music-white);
    --music-border:       var(--music-gray-light);

    /* ---------- Type ---------- */
    --music-font-body:    Arial, Helvetica, sans-serif;
    --music-line-body:    1.5;

    /* Type scale (decided 09/09/2026). One fluid base -- 14px at a
       360px viewport rising to 16px at 640px and up -- and four
       ratio steps (1.125 major second) off it. Every font-size on
       the site is one of these five; change the base and the whole
       site follows. The only per-element clamp()s left are where
       an element has its own geometry constraint (the alphabet bar
       fitting 26 cells, the sheet/audio buttons, the editor table),
       and those clamp between tokens. Icon glyphs use lg / xl. */
    --music-text-base:    clamp(0.875rem, 0.714rem + 0.714vw, 1rem);
    --music-text-sm:      calc(var(--music-text-base) * 0.875);
    --music-text-md:      calc(var(--music-text-base) * 1.125);
    --music-text-lg:      calc(var(--music-text-base) * 1.25);
    --music-text-xl:      calc(var(--music-text-base) * 1.5);

    /* ---------- Layout ---------- */
    --music-content-max:  1024px;  /* matches String Therapy's #contentWrap */
    --music-card-pad-x:   16px;
    --music-radius:       6px;
    --music-radius-sm:    4px;
    --music-shadow-card:  0 2px 6px rgba(0, 0, 0, 0.15);
    --music-label-weight: bold;

    /* The header: bands are 74px tall (BG_top.webp is 1920x74);
       the round logo is 100px tall and overhangs 26px below them.
       Content that must clear the logo starts at --music-logo-clear. */
    --music-band-h:       74px;
    --music-logo-h:       100px;
    --music-logo-clear:   190px;
}


/* ============================================================
   BASE
   ============================================================ */

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

html {
    min-height: 100%;
}

body {
    margin: 0;
    padding: 0;
    min-height: 100vh;
    min-height: 100dvh;
    font-family: var(--music-font-body);
    font-size: var(--music-text-base);
    line-height: var(--music-line-body);
    color: var(--music-text);

    /* White at the top of the viewport fading to blue, then solid
       blue for anything below the fold. background-attachment:
       fixed is avoided on purpose -- it is unreliable on iOS
       Safari, and Salt's members are on iPads. */
    background-color: var(--music-page-bg-btm);
    background-image: linear-gradient(
        to bottom,
        var(--music-page-bg-top) 0%,
        var(--music-sky) 55%,
        var(--music-page-bg-btm) 100%
    );
    background-repeat: no-repeat;
    background-size: 100% 100vh;
}

h1, h2, h3, h4 {
    color: var(--music-heading);
}

a {
    color: var(--music-link);
}

a:hover {
    color: var(--music-link-hover);
}

/* Keyboard focus must stay visible. Individual controls below may
   restyle the ring, but none of them may remove it. */
:focus-visible {
    outline: 2px solid var(--music-focus-ring);
    outline-offset: 2px;
}

img {
    max-width: 100%;
    height: auto;
}


/* ============================================================
   PAGE SHELL
   The white column every signed-in page sits in: the header
   (#siteHeader, emitted by SSI/menuTop_include.php) and the
   content (#contentWrap) share one width and read as one card,
   square at the top and rounded at the bottom.

   It fills the viewport up to 1024px and stops there, the same
   ceiling String Therapy uses. An iPad in landscape is 1024pt,
   so the ceiling is the tablet.

   No side padding on #contentWrap: the alphabet bar runs edge to
   edge and the song listing pads itself.
   ============================================================ */

#siteHeader,
#contentWrap {
    width: 100%;
    max-width: var(--music-content-max);
    margin-left: auto;
    margin-right: auto;
    background-color: var(--music-card-bg);
}

#contentWrap {
    padding: 0 0 32px;
    border-radius: 0 0 var(--music-radius) var(--music-radius);
    box-shadow: var(--music-shadow-card);
}

#contentWrap h1 {
    margin-top: 0;
    font-size: var(--music-text-lg);
    color: var(--music-navy);
}

@media (max-width: 639px) {
    #contentWrap {
        border-radius: 0;
        box-shadow: none;
    }
}


/* ============================================================
   SITE HEADER
   Emitted by SSI/menuTop_include.php on every signed-in page:

     #helloRow   "Hello {first_name}" -> profile, right-aligned;
                 empty for the shared 'salt' login (same height)
     #bandArea
       #siteLogo   the round logo: flush top-left over the bands,
                   overhanging onto the white below, on wide
                   screens; its own row above the bands at 639px
                   and below, with the hello link overlaid at its
                   right (decided 09/02/2026)
       #topStrip   the band image (BG_top.webp, 1920x74: purple
                   over green), anchored RIGHT so a narrower
                   viewport slides the left of the image away
         #pageBand   optional page title, white, centered in
                     the green half
       #mainNav    All Songs | All Setlists | Latest Setlist |
                   Logout -- tabs over the purple half down to
                   481px; a hamburger at 480px and below. No JS.
   ============================================================ */

#siteHeader {
    position: relative;
}

/* ---------- hello row ---------- */

#helloRow {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    min-height: 26px;
    padding: 2px var(--music-card-pad-x);
}

#helloRow a {
    color: var(--music-navy);
    text-decoration: none;
}

#helloRow a:hover {
    text-decoration: underline;
}

/* ---------- bands and logo ---------- */

#bandArea {
    position: relative;
}

#topStrip {
    position: relative;
    height: var(--music-band-h);
    background-color: var(--music-green);
    background-image: url("/images/BG_top.webp");
    background-repeat: no-repeat;
    background-position: right top;
    background-size: auto 100%;
}

#siteLogo {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 2;
    line-height: 0;
}

#siteLogo img {
    height: var(--music-logo-h);
    width: auto;
}

/* ---------- title band (green half) ---------- */

#pageBand {
    position: absolute;
    left: var(--music-logo-clear);
    right: var(--music-card-pad-x);
    top: 37px;
    height: 37px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--music-white);
    font-size: var(--music-text-base);
}

/* Page tabs in the green half (menuTop's $page_menu). Same tab
   recipe as #mainNav, left-aligned, but always carrying the dark
   fill for contrast against the green; hover turns purple. */
#pageBand.has-nav {
    justify-content: flex-start;
    align-items: flex-end;
    font-size: var(--music-text-base);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

#pageNav {
    display: flex;
    align-items: flex-end;
    height: 100%;
}

#pageNav a,
#pageNav button {
    display: block;
    margin: 0 4px 0 0;
    padding: 9px 12px;
    border: none;
    border-radius: 5px 5px 0 0;
    background-color: rgba(0, 0, 0, 0.18);
    color: var(--music-white);
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    text-decoration: none;
    cursor: pointer;
}

#pageNav a:hover,
#pageNav button:not(:disabled):hover {
    background-color: var(--music-purple);
    color: var(--music-white);
}

#pageNav .active {
    background-color: var(--music-navy-dark);
}

#pageNav button:disabled {
    opacity: 0.45;
    cursor: default;
}

/* ---------- nav (purple half) ----------
   Uppercase, nothing bold, no underlines; the active page is a
   solid navy-dark tab with its top corners rounded. Navy-dark is
   also the alphabet bar, so the dark elements read as one family
   around the two colored bands.

   The tabs live in .nav-links. #navToggle / #navIcon are the
   hamburger's checkbox and label: hidden here, shown at 480px and
   below (see the last media query in this section). */

#mainNav {
    position: absolute;
    top: 0;
    right: var(--music-card-pad-x);
    height: 37px;
    /* Full size (base, 16px) at 730px and up; from there down to
       640px the tabs shrink with the viewport so the logo overhang
       never meets the first tab. The middle term is 12.8px at 640px
       and 15.2px at 730px (decided 09/02/2026; a fixed breakpoint
       size collided with the logo at 720px); the sm floor (14px) now
       wins from about 685px down (type-scale change 09/09/2026,
       collision not yet retested). */
    font-size: clamp(var(--music-text-sm), 0.8rem + 2.667vw - 17.07px, var(--music-text-base));
    text-transform: uppercase;
    letter-spacing: 0.02em;
    z-index: 1;
}

#navToggle {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

#navIcon {
    display: none;
}

.nav-links {
    display: flex;
    align-items: flex-end;
    height: 100%;
}

#mainNav a {
    display: block;
    /* Side padding shrinks with the font: 12px at 730px, 7px at 640px. */
    padding: 9px clamp(7px, 7px + 5.556vw - 35.56px, 12px);
    color: var(--music-white);
    text-decoration: none;
    border-radius: 5px 5px 0 0;
    /* A tab must never wrap: a second line rises out of the purple
       band onto the white page and is unreadable. */
    white-space: nowrap;
}

#mainNav a:hover {
    background-color: rgba(0, 0, 0, 0.18);
    color: var(--music-white);
}

#mainNav a.active {
    background-color: var(--music-navy-dark);
}

/* 639px and below: the logo takes its own row above the bands, the
   hello link overlaid at the right of that row, so the bands run
   full width. The tabs stay in the purple half, tighter. */
@media (max-width: 639px) {

    /* Bottom-aligned with the logo row, so the link sits just above
       the purple band as it does on wide screens. */
    #helloRow {
        position: absolute;
        top: 0;
        right: 0;
        height: 96px;   /* the logo row: 88px image + 4px padding each side */
        min-height: 0;
        align-items: flex-end;
        padding: 0 8px 4px;
    }

    #siteLogo {
        position: static;
        padding: 4px 8px;
    }

    #siteLogo img {
        height: 88px;
    }

    #pageBand {
        left: 8px;
        right: 8px;
    }

    /* The bands are full width here, so the tabs hold sm and a
       7px gap all the way down to the hamburger (decided 09/02/2026). */
    #mainNav {
        right: 8px;
        font-size: var(--music-text-sm);
        letter-spacing: 0;
    }

    #mainNav a {
        padding: 10px 7px;
    }

    #pageBand.has-nav {
        font-size: var(--music-text-sm);
        letter-spacing: 0;
    }

    #pageNav a,
    #pageNav button {
        padding: 10px 7px;
    }
}

/* 480px and below (raised from 420px on 09/02/2026 so five tabs
   never crowd): the hamburger (decided 09/02/2026 from
   header_mockup_v1.html). The icon sits at the right of the purple
   half; checking it drops .nav-links down under the bands as a navy
   panel, and the icon becomes an X. Nothing else on the page moves. */
@media (max-width: 480px) {

    #navIcon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 37px;
        cursor: pointer;
        color: var(--music-white);
        font-size: var(--music-text-xl);
        line-height: 1;
    }

    #navIcon .close-icon {
        display: none;
    }

    #navToggle:checked ~ #navIcon .open-icon {
        display: none;
    }

    #navToggle:checked ~ #navIcon .close-icon {
        display: block;
    }

    .nav-links {
        display: none;
        position: absolute;
        top: var(--music-band-h);
        right: -8px;
        width: 220px;
        height: auto;
        flex-direction: column;
        background-color: var(--music-navy-dark);
        box-shadow: var(--music-shadow-card);
        z-index: 5;
    }

    #navToggle:checked ~ .nav-links {
        display: flex;
    }

    #mainNav {
        font-size: var(--music-text-base);
    }

    #mainNav a {
        padding: 14px 20px;
        border-radius: 0;
        border-top: 1px solid var(--music-divider);
        text-align: center;
    }

    #mainNav a:first-child {
        border-top: 0;
    }

    #mainNav a.active {
        background-color: var(--music-blue);
    }
}


/* ============================================================
   FIXED-HEADER LAYOUT
   String Therapy's recipe: the page is locked at viewport height,
   the header (and on the songbook, the search bar and alphabet
   bar) are fixed flex rows, and #scrollingContent below them
   scrolls the content. The alphabet include's JS resets
   #scrollingContent.scrollTop on letter clicks.

   Every signed-in page carries body.fixed-header and wraps its
   content in #scrollingContent (decided 09/02/2026; it was
   songbook-only until then). The auth pages do not.
   ============================================================ */

body.fixed-header {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
}

body.fixed-header #siteHeader {
    flex-shrink: 0;
}

body.fixed-header #contentWrap {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;      /* lets #scrollingContent shrink and scroll */
    padding: 0;
    border-radius: 0;
}

body.fixed-header .search-bar,
body.fixed-header #alphaMenu,
body.fixed-header .sm-toolbar {
    flex-shrink: 0;
}

#scrollingContent {
    flex: 1;
    overflow-y: auto;
    background: var(--music-white);
    padding-bottom: 6rem;
}


/* ============================================================
   SEARCH BAR (rendered by /SSI/search_bar_include.php)
   One row: [---- search input ----] [Search]. It sits in the
   white strip directly under the bands, to the right of the
   logo's overhang, so that strip carries the search instead of
   empty space. String Therapy's rules, minus its Ricola button.
   ============================================================ */

.search-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 6px var(--music-card-pad-x) 8px var(--music-logo-clear);
    background: var(--music-white);
}

.search-form {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 0;
    margin: 0;
}

.search-input-wrap {
    position: relative;
    flex: 1;
    min-width: 0;
}

.search-input {
    width: 100%;
    padding: 10px 36px 10px 38px;
    font-size: var(--music-text-base);
    font-family: inherit;
    border: 1px solid var(--music-gray-light);
    border-radius: 30px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: 12px center;
    outline: none;
}

.search-input:focus {
    border-color: var(--music-brand);
}

/* Circled X inside the input, vertically centered */
.search-clear-btn {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 22px;
    height: 22px;
    border: none;
    border-radius: 50%;
    background: var(--music-gray-medium);
    color: var(--music-white);
    font-size: var(--music-text-base);
    line-height: 1;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: inherit;
}

.search-clear-btn[hidden] {
    display: none;
}

.search-submit-btn {
    flex-shrink: 0;
}

/* Result banner under the row while a search is active */
.search-info {
    margin: 0;
    padding: 8px var(--music-card-pad-x);
    font-size: var(--music-text-base);
    background: var(--music-alt-row);
    border-top: 1px solid var(--music-divider);
}

@media (max-width: 767px) {
    .search-bar {
        flex-wrap: wrap;
        gap: 8px;
    }
    .search-form {
        flex: 1 1 100%;
    }
    .search-submit-btn {
        min-width: 70px;
    }
}

@media (max-width: 639px) {
    /* The logo no longer overhangs (see #siteLogo above). */
    .search-bar {
        padding: 8px 8px;
    }
}


/* ============================================================
   BUTTONS
   String Therapy's .btn-primary / .btn-secondary, so every page
   can reuse them. The Search button is one.
   ============================================================ */

.btn-primary,
.btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 14px;
    min-width: 80px;
    font-size: var(--music-text-base);
    font-family: inherit;
    line-height: 1.2;
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius-sm);
    cursor: pointer;
    text-decoration: none;
    user-select: none;
}

.btn-primary {
    background: var(--music-blue);
    color: var(--music-white);
    border-color: var(--music-blue);
    font-weight: bold;
}

.btn-primary:hover {
    background: var(--music-blue-hover);
    border-color: var(--music-blue-hover);
    color: var(--music-white);
    text-decoration: none;
}

.btn-primary:disabled {
    background: var(--music-gray-medium);
    border-color: var(--music-gray-medium);
    cursor: not-allowed;
}

.btn-secondary {
    background: var(--music-white);
    color: var(--music-gray-dark);
}

.btn-secondary:hover:not(:disabled) {
    background: var(--music-off-white);
    text-decoration: none;
}

/* Never styled before 09/11/2026: a disabled secondary button (the editor
   toolbar with nothing selected) looked live. Dimmed like the primary. */
.btn-secondary:disabled {
    color: var(--music-gray-medium);
    background: var(--music-off-white);
    opacity: 0.55;
    cursor: not-allowed;
}


/* ============================================================
   ALPHABET MENU (rendered by /SSI/alphabet_menu_include.php)
   String Therapy's cells and sizing. Its bar is near-black; under
   Salt's purple and green bands that would be a fourth heavy
   color, so the bar is the site's own navy-dark instead. Tall
   targets: more vertical than horizontal padding, so 27 cells fit
   a phone in two rows and are still easy to touch.
   ============================================================ */

#alphaMenu {
    margin: 0;
    padding: 6px 4px;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0;
    background-color: var(--music-navy-dark);
}

#alphaMenu .alpha-letter {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 calc(100% / 14);
    margin: 0;
    padding: clamp(8px, 2.2vw, 24px) clamp(2px, 0.8vw, 10px);
    font-size: clamp(var(--music-text-sm), 2.8vw, var(--music-text-lg));
    font-weight: normal;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    color: var(--music-white);
    border: 1px solid var(--music-navy);
    border-radius: 0;
    transition: background-color 0.15s ease;
}

#alphaMenu a.alpha-letter:hover {
    background-color: var(--music-off-white);
    color: var(--music-black);
}

#alphaMenu .alpha-letter.current {
    background: var(--music-blue);
    color: var(--music-white);
}

#alphaMenu .alpha-letter.disabled {
    color: var(--music-gray-medium);
    background-color: rgba(0, 0, 0, 0.28);
    cursor: default;
}


/* ============================================================
   SONG LISTING (rendered by /SSI/song_listing_include.php)
   One block per song, no table, no table header. The title bar is
   the header: full width, --music-blue, the title in white and the
   AKA paler and smaller, whichever the row is filed under first.
   Under the bar, CENTERED: the sheet buttons in one wrapping row,
   then the audio chips in another, no labels. Decided 09/01/2026
   from mockup v19 (labels removed and button fill set to white
   after seeing it live).
   ============================================================ */

.song-list {
    list-style: none;
    margin: 0;
    padding: 0 10px;
}

.song-block {
    margin: 12px 0 0;
    padding-bottom: 12px;
    font-size: var(--music-text-lg);
    color: var(--music-gray-dark);
}

.song-title-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 14px;
    padding: 8px 10px;
    background: var(--music-blue);
    color: var(--music-white);
    border-radius: var(--music-radius-sm);
}

/* A song's AKA in the title bar: secondary to the title by being one
   size smaller (base; the title inherits lg from .song-block) and a
   pale step of the bar's own blue instead of white. That is the
   whole signal: there is no "AKA:" label, and the AKA comes first on
   a row filed under it (09/20/2026). It replaces the 09/09/2026
   opacity 0.6 at the title's size, which measured 3.0:1. A second
   hue was tried and dropped: on the blue bar it read as a highlight
   and took priority over the title. On a narrow screen the second
   name wraps under the first. */
.song-aka-note {
    font-size: var(--music-text-base);
    color: var(--music-title-aka);
}

/* A setlist row's per-song label ("Part of Service: Gathering Song"):
   its own line under the title bar, left-aligned, regular weight,
   one step below the title's size (decided 09/07/2026). */
.song-custom1 {
    margin: 6px 10px 0;
    font-size: var(--music-text-md);
    color: var(--music-text);
}

/* A setlist row's notes: one line directly under the title bar,
   only on setlist pages, only when the row has any. */
.song-notes {
    margin: 8px 10px 0;
    font-size: var(--music-text-base);
    color: var(--music-gray-medium);
}

.sheetLinks,
.playLinks {
    list-style: none;
    margin: 12px 0 0;
    padding: 0 10px;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
}

.sheetLinks li,
.playLinks li {
    margin: 0;
    padding: 0;
}

/* ONE rule for both button kinds. A sheet button and an audio chip
   are the same control with different contents; nothing about them
   may differ except the play icon. The face (fill, border, shadow,
   padding, size) is String Therapy's sheet button.

   String Therapy capitalizes the label with ::first-letter, which
   does not apply to a flex container; song_listing_include.php
   capitalizes in PHP instead. */
.sheet-btn-link,
.playLinks li a.audio-link,
.playLinks li .audio-link-trashed {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: clamp(3px, 1.0vw, 10px);
    font-size: clamp(var(--music-text-md), 2.4vw, var(--music-text-lg));
    font-weight: normal;
    line-height: 1.2;
    white-space: nowrap;
    text-align: center;
    text-decoration: none;
    color: var(--music-brand);
    background-color: var(--music-btn-face);
    border: 1px solid var(--music-brand);
    border-radius: var(--music-radius-sm);
    box-shadow: 0 2px 5px var(--music-btn-shadow);
    cursor: pointer;
    transition: background-color 0.15s ease;
}

/* Audio chips carry a pale blue face so the two button kinds read
   as different at a glance (decided 09/09/2026). */
.playLinks li a.audio-link,
.playLinks li .audio-link-trashed {
    background-color: var(--music-btn-face-audio);
}

.sheet-btn-link:hover,
.sheet-btn-link:focus,
.playLinks li a.audio-link:hover,
.playLinks li a.audio-link:focus {
    background-color: var(--music-btn-hover);
    text-decoration: none;
    outline: none;
}

/* Sized in em so the icon can never exceed the button's line box
   (line-height 1.2em); the two button kinds then share one height.
   REQUIRES: /images/icon_play.png (32x32). */
.play-icon {
    height: 1em;
    width: 1em;
    display: inline-block;
    vertical-align: middle;
}

.audio-artist-text {
    vertical-align: middle;
}

/* ---------- Confirmation dialog ----------
   The logout confirmation the header emits: a native <dialog>, opened
   by script from the Logout tab. The .confirm-panel below is the same
   look for the no-JS fallback PAGE at /logout/. */

.confirm-dialog {
    max-width: 340px;
    width: calc(100% - 32px);
    padding: 28px 20px 22px;
    text-align: center;
    font-size: var(--music-text-md);
    color: var(--music-text);
    background: var(--music-white);
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius);
    box-shadow: var(--music-shadow-card);
}

/* A dialog with a sentence, not a question: twice the width on wide
   screens, and enough top padding that the corner X clears the text.
   width stays calc(100% - 32px), so phones get the full width. */
.confirm-dialog.wide {
    max-width: 640px;
    padding-top: 48px;
}

.confirm-dialog::backdrop {
    background: rgba(0, 0, 0, 0.5);
}

.confirm-dialog p {
    margin: 0 0 18px;
}

/* The corner X, in its own method="dialog" form */
.confirm-close {
    position: absolute;
    top: 6px;
    right: 6px;
    margin: 0;
}

.confirm-x {
    width: 36px;
    height: 36px;
    padding: 0;
    border: none;
    border-radius: var(--music-radius-sm);
    background: var(--music-off-white);
    color: var(--music-gray-dark);
    font-size: var(--music-text-lg);
    line-height: 1;
    cursor: pointer;
}

.confirm-x:hover {
    background: var(--music-gray-light);
}

/* Centered confirmation panel (the logout page; any yes/no page) */
.confirm-panel {
    max-width: 380px;
    margin: 32px auto;
    padding: 24px 20px;
    text-align: center;
    font-size: var(--music-text-md);
    background: var(--music-alt-row);
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius);
}

.confirm-panel p {
    margin: 0 0 18px;
}

.confirm-actions {
    display: flex;
    justify-content: center;
    gap: 12px;
    margin: 0;
}

.confirm-actions form {
    margin: 0;
}

/* Empty-letter / no-results line inside #alphabetContent */
.song-list-empty {
    margin: 0;
    padding: 24px 10px;
    text-align: center;
    color: var(--music-gray-medium);
}


/* ============================================================
   SETLISTS (rendered by /setlists/index.php)
   Two views. The index: setlists grouped by year, each year a
   centered heading over a thin rule, each setlist one link row of
   three content-sized columns (date, title, song count), the
   whole block centered, alternate rows tinted. From
   setlists_mockup_v3.html (approved 09/01/2026). The single
   setlist: the zip link right-aligned under the bands -- two lines of
   text beside the ZIP icon, the whole thing one link (09/02/2026)
   -- then the songbook's song blocks.

   #contentWrap has no side padding under the fixed-header layout,
   so these blocks pad themselves.
   ============================================================ */

/* ---------- single setlist: the zip link ---------- */

/* The zip link sits at the right of .setlist-page-head (09/11/2026). */
.setlist-zip {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: var(--music-link);
    text-decoration: none;
    font-size: var(--music-text-md);
    line-height: 1.3;
}

.setlist-zip-text {
    text-align: center;
}

.setlist-zip img {
    height: 64px;
    width: auto;
}

.setlist-zip:hover {
    color: var(--music-link-hover);
    text-decoration: underline;
}

/* ---------- index: one year ---------- */
/* A <details> per year (09/11/2026): the heading is its summary, with
   a triangle to the left of the year that turns down when open. The
   native marker is hidden so the arrow sits with the centered text. */

.setlist-year {
    margin: 28px 24px 0;
}

.setlist-year-summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
}
.setlist-year-summary::-webkit-details-marker {
    display: none;
}

.setlist-year h2 {
    margin: 0 0 6px;
    padding-bottom: 4px;
    font-size: var(--music-text-lg);
    font-weight: normal;
    text-align: center;
    color: var(--music-navy);
    border-bottom: 1px solid var(--music-divider);
}

.setlist-year h2::before {
    content: '\25B6';   /* right-pointing triangle, the native marker's shape */
    display: inline-block;
    width: 1em;
    margin-right: 8px;
    font-size: var(--music-text-base);   /* the trash summary's marker size */
    line-height: 1;
    vertical-align: middle;
    color: var(--music-gray-medium);
    transition: transform 0.15s;
}
.setlist-year[open] h2::before {
    transform: rotate(90deg);
}
.setlist-year-summary:hover h2 {
    color: var(--music-link-hover);
}

/* One shared grid for the year: three content-sized columns, the
   whole block centered. Each link spans the row on the subgrid so
   the columns line up across rows and the row can carry a fill. */
.setlist-index {
    list-style: none;
    margin: 0 auto;
    padding: 0;
    display: grid;
    grid-template-columns: auto auto auto;
    width: max-content;
    max-width: 100%;
}

.setlist-index li {
    display: contents;
}

.setlist-index a {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
    column-gap: 28px;
    align-items: baseline;
    padding: 8px 14px;
    color: var(--music-link);
    text-decoration: none;
    font-size: var(--music-text-md);
}

.setlist-index li:nth-child(even) a {
    background: var(--music-alt-row);
}

.setlist-index a:hover {
    background: var(--music-blue-pale);
    color: var(--music-link-hover);
}

.setlist-index .setlist-date {
    color: var(--music-gray-medium);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.setlist-index .setlist-count {
    color: var(--music-gray-medium);
    font-size: var(--music-text-base);
    white-space: nowrap;
}

/* The unknown-slug notice and the empty-index line sit where the
   first year would, with the same side margins. */
#scrollingContent > .message-error,
#scrollingContent > .message-notice {
    margin: 28px 24px 0;
}

@media (max-width: 639px) {
    .setlist-year,
    #scrollingContent > .message-error,
    #scrollingContent > .message-notice {
        margin: 24px 12px 0;
    }
    .setlist-index a {
        column-gap: 14px;
        padding: 8px 8px;
        font-size: var(--music-text-base);
    }
}


/* ============================================================
   SETLIST EDITOR (/setlists/?setlist={slug}&edit=1) and the
   index page's create form + Deleted Setlists trash
   ------------------------------------------------------------
   CANONICAL SOURCE: String Therapy's stringTherapyStyle.css
   setlist sections (08/2026), ported 09/07/2026 onto --music-*
   variables. The editor is String Therapy's table (decided
   09/03/2026: port the mature editor as-is), so the shared
   .song-table base comes with it; Salt's public listing and
   published setlist are song BLOCKS and never use .song-table.
   Differences from String Therapy:
     - the fixed strip under the bands is #setlistHeader (String
       Therapy: #fixedHeader) and holds the Add Song / Add Break
       panels in flow, since the tabs that open them are in the
       green band;
     - toolbar and modal buttons are Salt's .btn-secondary /
       .btn-primary (.btn-small), not String Therapy's .tb-btn;
     - the trash list reuses .setlist-index rows, not a table;
     - no PDF, no listing table, no view table.
   ============================================================ */

/* ---------- the fixed strip under the bands ---------- */
/* Title / date / label control, then the toolbar.
   Sits between the bands and #scrollingContent, and never scrolls. */
#setlistHeader {
    flex-shrink: 0;
    position: relative;
    z-index: 3;
    padding: 42px 0 0;   /* the logo overhangs the bands by ~30px on wide screens */
    /* No background: the strip is above the logo in stacking order, so a
       fill would cover the overhang (seen 09/07/2026). */
    border-bottom: 1px solid var(--music-brand);
    box-shadow: 0 2px 32px var(--music-gray-shadow);
    clip-path: inset(0 0 -40px 0);
}

/* Small screens: title, date and the two controls are set-once, so under
   640px they collapse behind a toggle to give the song list more room.
   The toolbar stays visible since it works with the table. */
.setlist-settings-toggle {
    position: absolute;
    left: -9999px;
}
.setlist-settings-label {
    display: none;   /* shown only at small widths */
}
@media (max-width: 639px) {
    .setlist-settings-label {
        display: flex;
        align-items: center;
        gap: 6px;
        margin: 0 12px 10px;
        padding: 6px 12px;
        border: 1px solid var(--music-brand);
        border-radius: var(--music-radius-sm);
        background: var(--music-blue-pale);
        color: var(--music-brand);
        cursor: pointer;
        user-select: none;
    }
    .setlist-settings-label::before {
        content: "\25B8";
        flex: 0 0 auto;
        font-size: var(--music-text-sm);
        line-height: 1;
    }
    .setlist-settings-toggle:checked ~ .setlist-settings-label::before {
        content: "\25BE";
    }
    .setlist-settings-label-prefix {
        flex: 0 0 auto;
        font-weight: bold;
    }
    .setlist-settings-label-title {
        flex: 1 1 auto;
        min-width: 0;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .setlist-editing-line {
        display: none;
    }
    .setlist-settings {
        display: none;
    }
    .setlist-settings-toggle:checked ~ .setlist-settings {
        display: block;
    }
}

/* Top label above a header field (Title / Event date). */
.setlist-fieldlabel {
    font-size: var(--music-text-base);
    color: var(--music-gray-medium);
    margin-bottom: 2px;
}
.setlist-date-group {
    display: inline-flex;
    flex-direction: column;
}

/* Title + event date row, each with a gray hint below. Commit on
   blur / group-exit via update_header. */
.setlist-title-edit {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 10px;
    margin: 0 20px 6px;
}
.setlist-field {
    display: flex;
    flex-direction: column;
}
.setlist-field-title {
    flex: 1 1 320px;
    min-width: 0;
}
.setlist-field-hint {
    font-size: var(--music-text-base);
    color: var(--music-gray-medium);
    margin-top: 2px;
}
.setlist-title-input {
    box-sizing: border-box;
    width: 100%;
    padding: 6px 10px;
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius);
    background: var(--music-white);
    transition: border-color 0.15s ease, background-color 0.6s ease;
    font: inherit;
    font-size: var(--music-text-xl);
    font-weight: bold;
}
.setlist-title-input:focus {
    border-color: var(--music-brand);
    outline: none;
}
.setlist-title-input.row-edit-saved {
    border-color: var(--music-success);
    background: var(--music-success-bg);
}
.setlist-title-input.row-edit-error {
    border-color: var(--music-danger);
    background: var(--music-error-bg);
}

/* Event date: three numeric fields (mm / dd / yyyy). The saved flash
   lands on the whole group; per-field pink marks an invalid entry. */
.setlist-date-fields {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: flex-start;
    gap: 4px;
    padding: 4px 8px;
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius);
    background: var(--music-white);
    transition: border-color 0.15s ease, background-color 0.6s ease;
}
.setlist-date-field {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
}
.setlist-date-num {
    box-sizing: border-box;
    border: none;
    outline: none;
    background: transparent;
    font: inherit;
    font-size: var(--music-text-md);
    line-height: 1.2;
    text-align: center;
    padding: 2px 0;
}
.setlist-date-mm,
.setlist-date-dd   { width: 2.2em; }
.setlist-date-yyyy { width: 3.4em; }
.setlist-date-sep {
    align-self: flex-start;
    color: var(--music-gray-medium);
    font-size: var(--music-text-md);
    line-height: 1.2;
    padding: 2px 0;
}
.setlist-date-fields:focus-within {
    border-color: var(--music-brand);
}
.setlist-date-fields.row-edit-saved {
    border-color: var(--music-success);
    background: var(--music-success-bg);
}
.setlist-date-num.date-field-invalid {
    background: var(--music-error-bg);
    border-radius: var(--music-radius-sm);
}

/* Second row: the per-row label control (custom column 1). */
.setlist-options-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 28px;
    margin: 0 20px 8px;
}
.setlist-custom1-control {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}
.setlist-custom1-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--music-text-base);
    cursor: pointer;
}
.setlist-custom1-toggle input {
    width: 20px;
    height: 20px;
    margin: 0;
    cursor: pointer;
}
.setlist-custom1-header {
    box-sizing: border-box;
    padding: 6px 10px;
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius);
    font: inherit;
    font-size: var(--music-text-md);
}
.setlist-custom1-header:disabled {
    background: var(--music-off-white);
    color: var(--music-gray-medium);
}

/* ---------- page heading: title + date, last-edited line ---------- */
/* Shared by the editor and the published page (09/11/2026). Left-
   aligned with a 100px gutter to clear the logo's overhang (the
   profile page's rule). On the published page the zip link shares
   the row on the right, its top level with the h1. In the editor the
   last-edited line is the save clock: it turns red on a failed save
   (clock-error) and returns to the stamp a few seconds later. */
.setlist-page-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin: 0;
    padding: 20px 24px 0 100px;
    text-align: left;
}
.setlist-page-head-text {
    flex: 1 1 auto;
    min-width: 0;
}
#contentWrap .setlist-page-title {
    margin: 0;
    font-size: var(--music-text-xl);
    font-weight: normal;
    line-height: 1rem;   /* tightens the strip; measured in dev tools 09/11/2026 */
    color: var(--music-navy);
}
.setlist-page-edited {
    margin: 4px 0 0;
}
.setlist-saved-clock {
    font-size: var(--music-text-base);
    color: var(--music-gray-medium);
}
.setlist-saved-clock.clock-error {
    color: var(--music-danger);
    font-weight: bold;
}

/* ---------- toolbar: acts on the selected row ---------- */
.setlist-toolbar {
    margin: 4px 20px 0;
    padding: 8px 0 10px;
    border-top: 1px solid var(--music-divider);
}
.setlist-toolbar-label {
    margin: 0 0 6px;
    font-size: var(--music-text-base);
    color: var(--music-gray-medium);
}
.setlist-toolbar-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px;
}
.toolbar-group {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.toolbar-group-end {
    margin-left: auto;   /* Remove, pushed to the far right */
}
.tb-remove {
    color: var(--music-danger);
    border-color: var(--music-danger);
}
.tb-remove:hover:not(:disabled) {
    background: var(--music-error-bg);
    color: var(--music-danger);
}

/* ---------- Add Song / Add Break panels ---------- */
/* Opened by the green-band tabs; they sit in the strip, in flow, above
   the toolbar, and push the list down while open. */
.setlist-panels {
    margin: 0 20px;
}
.addsong-panel {
    margin: 4px 0 10px;
    padding: 12px;
    background: var(--music-white);
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius);
    box-shadow: var(--music-shadow-card);
    max-height: 55vh;
    overflow-y: auto;
}
.addsong-panel-head {
    display: flex;
    align-items: center;
    gap: 8px;
}
.addsong-search {
    flex: 1 1 auto;
    width: 100%;
    box-sizing: border-box;
    padding: 10px 14px;
    font: inherit;
    font-size: var(--music-text-md);
    border: 2px solid var(--music-brand);
    border-radius: var(--music-radius);
}
/* The panel's Cancel button, at the right end of the search field
   (09/20/2026; it replaced a corner x that was easy to miss). */
.addsong-cancel {
    flex: 0 0 auto;
}
/* Replace mode: the "Replacing <song>" line above the search field. */
.addsong-replacing {
    margin: 0 0 8px;
    color: var(--music-gray-dark);
    font-size: var(--music-text-base);
}
.addsong-status {
    margin-top: 10px;
    padding: 8px 2px;
    color: var(--music-success);
    font-size: var(--music-text-base);
}
.addsong-results {
    margin-top: 10px;
}
.addsong-msg {
    padding: 10px 2px;
    color: var(--music-gray-medium);
    font-style: italic;
}
.addsong-error {
    color: var(--music-danger);
    font-style: normal;
}
.addsong-song {
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius);
    padding: 12px 14px;
    margin-bottom: 10px;
    background: var(--music-white);
}
.addsong-song-head {
    margin-bottom: 8px;
}
.addsong-song-title {
    font-weight: bold;
    font-size: var(--music-text-md);
    color: var(--music-gray-dark);
}
.addsong-song-aka {
    color: var(--music-gray-medium);
    font-size: var(--music-text-base);
}
.addsong-sheets {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 18px;
    margin-bottom: 12px;
}
.addsong-sheet {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--music-text-md);
    cursor: pointer;
}
.addsong-sheet input {
    width: 20px;
    height: 20px;
}
.addsong-nosheets {
    color: var(--music-gray-medium);
    font-style: italic;
    margin-bottom: 12px;
}
.addsong-rowmsg {
    margin-left: 12px;
    font-size: var(--music-text-base);
    color: var(--music-success);
}
.addsong-rowmsg.addsong-error {
    color: var(--music-danger);
}
/* Add Break: a multi-line text field, then "Add to setlist" and
   "Cancel" side by side under it (09/20/2026). */
.addbreak-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
}
.addbreak-text {
    width: 100%;
    box-sizing: border-box;
    padding: 12px 14px;
    font: inherit;
    font-size: var(--music-text-md);
    line-height: 1.35;
    border: 2px solid var(--music-brand);
    border-radius: var(--music-radius);
    resize: vertical;
    color: var(--music-gray-dark);
}

/* ---------- the editor table (String Therapy's .song-table) ---------- */
.setlist-zone {
    padding: 0 0 20px;
}
.song-table {
    width: 100%;
    border-collapse: collapse;
    font-size: clamp(var(--music-text-base), 3vw, var(--music-text-lg));
    color: var(--music-gray-dark);
}
.song-table th,
.song-table td {
    padding: 8px 10px;
    text-align: left;
    vertical-align: top;
    color: var(--music-gray-dark);
}
.song-table th {
    background: var(--music-blue);
    color: var(--music-white);
    font-weight: bold;
}
.song-table thead th {
    position: sticky;
    top: 0;
    z-index: 10;
}
.song-row.song-odd  { background: var(--music-alt-row); }
.song-row.song-even { background: var(--music-white); }
.song-row .song-title-cell {
    padding: 12px 10px;
    font-weight: bold;
}
.song-table td.setlist-num-cell {
    text-align: center;
    font-weight: bold;
}
.song-table th.th-num { width: 42px; text-align: center; }

/* Fixed layout so the Music Sheets column only takes the width its
   checkbox list needs, freeing width for the multi-line textareas. */
.song-table.setlist-edit-table {
    table-layout: fixed;
}
.song-table.setlist-edit-table th.th-select { width: 40px; }
.song-table.setlist-edit-table th.th-lead,
.song-table.setlist-edit-table td.setlist-lead-cell  { width: 18%; }
.song-table.setlist-edit-table th.th-notes,
.song-table.setlist-edit-table td.setlist-notes-cell { width: 30%; }
.song-table.setlist-edit-table.no-col3 th.th-notes,
.song-table.setlist-edit-table.no-col3 td.setlist-notes-cell { width: 40%; }
.song-table.setlist-edit-table td.song-title-cell { padding: 8px 10px; }
.song-table.setlist-edit-table td.song-sheets-cell {
    white-space: normal;
    text-align: left;
    overflow-wrap: anywhere;
}
@media (min-width: 921px) {
    .song-table.setlist-edit-table tr.song-row > td {
        border-top: 2px solid var(--music-divider);
    }
}

/* Select column: the radio sits at the top of the row, level with the
   number and the title, however tall the row's textareas are. */
.song-table td.setlist-select-cell {
    width: 40px;
    text-align: center;
    vertical-align: top;
    padding-top: 10px;
}
.setlist-select {
    width: 22px;
    height: 22px;
    cursor: pointer;
}

/* Break row: a light band; its text is editable */
.song-table tr.setlist-break-row td {
    background: var(--music-white);
    color: var(--music-gray-dark);
    font-weight: normal;
    text-align: center;
    border-top: 2px solid var(--music-navy);
    border-bottom: 2px solid var(--music-navy);
}
.song-table tr.setlist-break-row td:empty::before {
    content: "\00a0";
}
.song-table tr.setlist-break-row td.setlist-break-cell textarea.row-edit {
    text-align: left;
    font-style: italic;
}

/* In-row text editing (label / notes / break text), multi-line; JS
   auto-grows the height to fit the content. */
.song-table textarea.row-edit {
    width: 100%;
    box-sizing: border-box;
    padding: 6px 8px;
    font: inherit;
    font-size: var(--music-text-md);
    line-height: 1.35;
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius);
    background: var(--music-white);
    resize: vertical;
    overflow-y: hidden;
    min-height: 2.4em;
    transition: border-color 0.15s ease, background-color 0.6s ease;
}
.song-table textarea.row-edit:focus {
    border-color: var(--music-brand);
    outline: none;
}
.song-table textarea.row-edit.row-edit-saved {
    border-color: var(--music-success);
    background: var(--music-success-bg);
}
.song-table textarea.row-edit.row-edit-error {
    border-color: var(--music-danger);
    background: var(--music-error-bg);
}

/* Per-sheet checkbox + link */
ul.sheet-choices {
    list-style: none;
    margin: 0;
    padding: 0;
}
.sheet-choice {
    display: flex;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 8px;
    margin: 0 0 6px;
}
.sheet-choice input.sheet-include {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    cursor: pointer;
}
.sheet-choice input.sheet-include:disabled {
    cursor: not-allowed;
}
.sheet-choice-link {
    flex: 0 0 auto;
    align-self: flex-start;
    color: var(--music-link);
    text-decoration: none;
    font-size: var(--music-text-md);
    padding: 4px 6px;
}
.sheet-choice-link:hover {
    text-decoration: underline;
}
.sheet-choice.sheet-choice-missing .sheet-choice-missing-label {
    color: var(--music-gray-medium);
}
.sheet-choices-empty {
    color: var(--music-gray-medium);
    font-style: italic;
    font-size: var(--music-text-base);
}

/* Zero chosen sheets: the row stays on the setlist; the note says what
   the published page will show. Always rendered; JS toggles it. */
.sheet-draft-note {
    color: var(--music-danger);
    font-size: var(--music-text-base);
    margin-bottom: 6px;
    text-align: left;
}
.sheet-draft-note-hidden {
    display: none;
}

/* Problem row: a chosen sheet is archived, orphaned or file-missing. */
.song-table tr.song-row.setlist-sheet-problem {
    background: var(--music-error-bg);
}
.sheet-problem-banner {
    margin: 0 0 8px;
}
.sheet-problem-item {
    color: var(--music-danger);
    font-size: var(--music-text-base);
    line-height: 1.35;
    margin: 0 0 4px;
}

/* Reorder/add: jumped-to row must clear the sticky table header */
.song-table tr[data-row-id] { scroll-margin-top: 60px; }

/* ---------- editor table stacked (<= 920px) ---------- */
@media (max-width: 920px) {
    .song-table.setlist-edit-table thead {
        display: none;
    }
    .song-table.setlist-edit-table,
    .song-table.setlist-edit-table tbody,
    .song-table.setlist-edit-table tr,
    .song-table.setlist-edit-table td {
        display: block;
        width: 100% !important;
        box-sizing: border-box;
    }
    .song-table.setlist-edit-table tbody tr.song-row {
        border-top: 2px solid var(--music-divider);
        padding: 6px 12px 16px;
    }
    .song-table.setlist-edit-table tbody tr.song-row:first-of-type {
        border-top: none;
    }
    .song-table.setlist-edit-table td,
    .song-table.setlist-edit-table tr.song-row > td {
        border: none;
        padding: 2px 0;
    }
    .song-table.setlist-edit-table td.setlist-select-cell,
    .song-table.setlist-edit-table td.setlist-num-cell {
        display: inline-block;
        width: auto !important;
        padding-right: 6px;
        vertical-align: middle;
    }
    .song-table.setlist-edit-table td.song-title-cell {
        display: block;
        width: 100% !important;
    }
    .song-table.setlist-edit-table td.setlist-lead-cell::before,
    .song-table.setlist-edit-table td.setlist-notes-cell::before {
        content: attr(data-label) ": ";
        display: block;
        margin-top: 4px;
        font-weight: bold;
    }
    .song-table.setlist-edit-table td.song-sheets-cell {
        padding-top: 4px;
    }
    .song-table.setlist-edit-table tr.setlist-break-row {
        padding: 6px 12px;
    }
    .song-table.setlist-edit-table tr.setlist-break-row td.setlist-num-cell {
        display: none;
    }
    .song-table.setlist-edit-table tr.setlist-break-row td.setlist-break-cell {
        display: inline-block;
        width: auto !important;
        border: none;
    }
    .song-table.setlist-edit-table tr.setlist-break-row td {
        border-top: none;
        border-bottom: none;
    }
}

/* ---------- confirm modals (Remove / Delete Setlist / Delete permanently) ---------- */
/* A dimming overlay the editor JS shows and hides with [hidden]. */
.setlist-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 20px;
}
.setlist-modal-overlay[hidden] { display: none; }
.setlist-modal {
    position: relative;
    background: var(--music-white);
    border-radius: var(--music-radius);
    padding: 28px 26px 22px;
    max-width: 440px;
    width: 100%;
    box-shadow: var(--music-shadow-card);
}
.setlist-modal-x {
    position: absolute;
    top: 8px;
    right: 12px;
    background: none;
    border: none;
    font-size: var(--music-text-xl);
    line-height: 1;
    color: var(--music-gray-medium);
    cursor: pointer;
}
.setlist-modal-x:hover { color: var(--music-gray-dark); }
.setlist-modal-msg {
    margin: 6px 0 22px;
    font-size: var(--music-text-md);
    color: var(--music-text);
}
.setlist-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
}

/* ---------- editor: the "no songs yet" line ---------- */
.setlist-empty {
    margin: 24px 20px;
    color: var(--music-gray-medium);
    font-style: italic;
}

/* ---------- index: "+ Create New Setlist" form ---------- */
/* The button and its form live in the index's fixed strip (09/20/2026;
   see "index: the fixed strip" below, which places them). The form
   reuses the editor's title/date fields. */
.setlist-create-form .setlist-title-edit {
    margin: 0 0 8px;
}
.setlist-create-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}
.setlist-create-error {
    color: var(--music-danger);
}

/* ---------- index: the per-setlist Edit and Copy links ---------- */
/* Two extra columns on the index rows for editors: the row link spans
   the first three, the Edit link takes the fourth and the Copy link
   (09/11/2026) the fifth. All are grid items, since li is display:
   contents. */
.setlist-index.has-actions {
    grid-template-columns: auto auto auto auto auto;
}
.setlist-index.has-actions a:not(.setlist-edit-link):not(.setlist-copy-link) {
    grid-column: 1 / 4;
}
.setlist-index .setlist-edit-link,
.setlist-index .setlist-copy-link {
    display: block;
    grid-template-columns: none;
    padding: 8px 14px;
    font-size: var(--music-text-base);
    color: var(--music-link);
    text-decoration: none;
}
.setlist-index .setlist-edit-link { grid-column: 4; }
.setlist-index .setlist-copy-link { grid-column: 5; }
.setlist-index li:nth-child(even) .setlist-edit-link,
.setlist-index li:nth-child(even) .setlist-copy-link {
    background: var(--music-alt-row);
}
.setlist-index a.setlist-edit-link:hover,
.setlist-index a.setlist-copy-link:hover {
    background: var(--music-blue-pale);
    color: var(--music-link-hover);
}

/* Copy mode on the create form: the "Copying ..." line above the fields. */
.setlist-copy-note {
    margin: 0 0 8px;
    color: var(--music-gray-medium);
}

/* ---------- index: the fixed strip (09/20/2026) ---------- */
/* #setlistIndexHeader (its border and shadow are #setlistViewHeader's
   rule, below) holds one grid:

       [ song search ................ ] [+ Create New Setlist]
       [ the search's result list, while typing               ]
       [ the create form, while open                          ]
       [ the count line                                       ]

   The search is for everyone; the button and form are editors'. The
   left padding is the songbook search bar's, so the two search fields
   sit in the same place on both pages, clear of the logo's overhang.
   #setlistCreateBar is display: contents: its button and form are
   cells of this grid, while the element still wraps both for
   js/setlist_maintenance.js. The result list is in flow, not a
   floating dropdown: the strip's clip-path would cut one off. */
.setlist-index-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "search  create"
        "results results"
        "form    form"
        "count   count";
    column-gap: 16px;
    align-items: center;
    padding: 6px var(--music-card-pad-x) 0 var(--music-logo-clear);
}
.setlist-index-head .setlist-create-bar {
    display: contents;
}
.setlist-search {
    grid-area: search;
    min-width: 0;
}
#setlistCreateToggle {
    grid-area: create;
}
.setlist-index-head .setlist-create-form {
    grid-area: form;
    margin-top: 10px;
}
.setlist-index-count {
    grid-area: count;
    margin: 6px 0 0;
    font-size: var(--music-text-base);
    color: var(--music-gray-medium);
}
.setlist-index-count strong {
    color: var(--music-navy);
}

/* The result list: one button per song. It is only as wide as its
   contents (justify-self: start sizes a grid item to fit), never wider
   than the search row. Two columns shared by every row through subgrid:
   the titles, left-aligned, and the setlist counts, which line up with
   each other just right of the longest title shown; a title too long
   for the row wraps. So the list resizes with every search. */
.setlist-search-results {
    grid-area: results;
    justify-self: start;
    display: grid;
    grid-template-columns: auto max-content;
    max-width: 100%;
    box-sizing: border-box;
    margin-top: 6px;
    max-height: 45vh;
    overflow-y: auto;
    background: var(--music-white);
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius);
    box-shadow: var(--music-shadow-card);
}
.setlist-search-results[hidden] {
    display: none;
}
.setlist-search-option {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    column-gap: 28px;
    align-items: baseline;
    box-sizing: border-box;
    padding: 10px 14px;
    border: 0;
    border-bottom: 1px solid var(--music-divider);
    background: none;
    font: inherit;
    font-size: var(--music-text-md);
    color: var(--music-link);
    text-align: left;
    cursor: pointer;
}
.setlist-search-option:last-child {
    border-bottom: 0;
}
.setlist-search-option:not(.is-none):hover,
.setlist-search-option.is-active {
    background: var(--music-blue-pale);
    color: var(--music-link-hover);
}
/* A song on no setlist: listed, dimmed, not pickable (the JS skips it). */
.setlist-search-option.is-none {
    color: var(--music-gray-medium);
    opacity: 0.55;
    cursor: default;
}
.setlist-search-option-title {
    min-width: 0;
}
.setlist-search-option-aka,
.setlist-search-option-count {
    font-size: var(--music-text-base);
    color: var(--music-gray-medium);
}
.setlist-search-option-count {
    white-space: nowrap;
}
.setlist-search-msg {
    grid-column: 1 / -1;
    padding: 10px 14px;
    color: var(--music-gray-medium);
    font-style: italic;
}
.setlist-search-error {
    color: var(--music-danger);
    font-style: normal;
}

/* The search's highlight: the break band's yellow on every setlist that
   holds the picked song (all of the row's cells, the Edit and Copy links
   included) and on each year line with a match, whose count sits in
   .setlist-year-match. Placed after the even-row fills above, which it
   must beat at equal specificity. A year's open/closed state is never
   changed by a search. */
.setlist-index li.is-match a,
.setlist-index li.is-match .setlist-edit-link,
.setlist-index li.is-match .setlist-copy-link {
    background: var(--music-break-bg);
}
.setlist-index li.is-match a:hover {
    background: var(--music-blue-pale);
}
.setlist-year.has-match h2 {
    background: var(--music-break-bg);
}
.setlist-year-match {
    font-size: var(--music-text-base);
    color: var(--music-gray-dark);
}

@media (max-width: 639px) {
    /* The logo is static above the bands here; no overhang to clear.
       One column: the field full width, its results under it, then the
       button, the form and the count line. */
    .setlist-index-head {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "search"
            "results"
            "create"
            "form"
            "count";
        padding: 8px 8px 0;
    }
    #setlistCreateToggle {
        justify-self: start;
        margin-top: 8px;
    }
}

/* ---------- published setlist: the fixed heading strip ---------- */
/* #setlistViewHeader is the editor's #setlistHeader for the published
   page (09/11/2026): a fixed strip between the bands and
   #scrollingContent, same border and shadow, the songs scroll under it.
   Same no-background reasoning: the logo overhangs into it. */
#setlistViewHeader,
#setlistIndexHeader {
    flex-shrink: 0;
    position: relative;
    z-index: 3;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--music-brand);
    box-shadow: 0 2px 32px var(--music-gray-shadow);
    clip-path: inset(0 0 -40px 0);
}
.setlist-page-count {
    margin: 4px 0 0;
    font-size: var(--music-text-lg);   /* the song titles' size */
    color: var(--music-navy);          /* the h1's color */
}

/* Disabled button-styled link (Edit during maintenance): non-clickable
   and dimmed. Used where an <a> can't take :disabled. */
.link-disabled {
    pointer-events: none;
    opacity: 0.5;
    cursor: not-allowed;
    text-decoration: none;
}

/* ---------- index: Deleted Setlists trash ---------- */
.setlist-deleted-section {
    margin: 32px 24px 0;
}
.setlist-deleted-summary {
    cursor: pointer;
    font-weight: bold;
    color: var(--music-danger);
    padding: 6px 0;
}
.setlist-deleted-note {
    font-weight: normal;
    font-size: var(--music-text-sm);
    color: var(--music-gray-medium);
}
.setlist-deleted-msg {
    color: var(--music-danger);
    margin: 6px 0;
}
.setlist-deleted-list {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
}
.setlist-deleted-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 18px;
    padding: 8px 14px;
}
.setlist-deleted-list li:nth-child(even) {
    background: var(--music-alt-row);
}
.setlist-deleted-title { color: var(--music-gray-medium); }
.setlist-deleted-when {
    font-size: var(--music-text-sm);
    color: var(--music-gray-medium);
}
.setlist-deleted-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-left: auto;
}
.setlist-purge-btn {
    color: var(--music-danger);
    border-color: var(--music-danger);
    background: var(--music-white);
}
.setlist-purge-btn:hover:not(:disabled) {
    background: var(--music-error-bg);
    color: var(--music-danger);
}

/* ---------- published setlist (block style, step 9) ---------- */
/* A break between song blocks: a full-width yellow band, the custom
   column's text size, with room above and below (09/07/2026). No side
   margin: .song-list already pads 10px, so the band lines up with the
   song blocks (fixed 09/11/2026). */
.song-list .setlist-break {
    margin: 30px 0;
    padding: 8px 12px;
    text-align: center;
    font-size: var(--music-text-xl);
    color: var(--music-gray-dark);
    background: var(--music-break-bg);
    border-top: 4px solid var(--music-blue);
}
/* A chosen sheet that is no longer available: named under the buttons. */
.song-block .sheet-problem-msg {
    list-style: none;
    margin: 8px 10px 0;
    padding: 0;
    color: var(--music-danger);
    font-size: var(--music-text-base);
    line-height: 1.3;
    text-align: center;
}

@media (max-width: 639px) {
    .setlist-title-edit,
    .setlist-options-row,
    .setlist-toolbar,
    .setlist-panels {
        margin-left: 12px;
        margin-right: 12px;
    }
    /* The logo is static above the bands here; no overhang to clear. */
    .setlist-page-head {
        padding: 16px 12px 0;
    }
    .setlist-deleted-section {
        margin-left: 12px;
        margin-right: 12px;
    }
    /* The logo is static above the bands here; no overhang to clear. */
    #setlistHeader {
        padding-top: 12px;
    }
}


/* ============================================================
   PROFILE (/profile/)
   Edit Profile and Change Password are tabs in the green band
   (menuTop's $page_menu). Below: a left-aligned heading, then
   either the facts list (view) or the Save button and form (edit),
   reusing the auth-page form rules (.formGroup, .message-*). #contentWrap has no side
   padding under the fixed-header layout, so the column pads
   itself.
   ============================================================ */

/* 100px each side on wide screens: the round logo overhangs the
   bands by about half its width, so a 24px gutter put the heading
   against it (measured 09/02/2026). Narrow screens give the logo
   its own row, so the gutter drops back. */
.profile {
    margin: 0;
    padding: 20px 100px 0;
}

/* The form column; the heading and messages above it run full width */
#profileForm {
    max-width: 480px;
}

.profile h1 {
    margin: 0 0 16px;
    text-align: left;
}

/* The Save button: top center, above the form fields */
.profile-save {
    text-align: center;
    margin: 0 0 16px;
}

.profile-facts {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 16px;
    row-gap: 8px;
    margin: 0 0 20px;
}

.profile-facts dt {
    color: var(--music-gray-medium);
    font-size: var(--music-text-sm);
    align-self: baseline;
}

.profile-facts dd {
    margin: 0;
    font-size: var(--music-text-base);
    color: var(--music-gray-dark);
    overflow-wrap: anywhere;
}

/* The one-line description under the member's role */
.profile-role-note {
    display: block;
    margin-top: 2px;
    font-size: var(--music-text-sm);
    color: var(--music-gray-medium);
}

.profile-help {
    margin: 0 0 12px;
    color: var(--music-gray-medium);
    font-size: var(--music-text-base);
}

@media (max-width: 639px) {
    .profile {
        padding: 16px 12px 0;
    }
}


/* ============================================================
   ADMIN PAGES (/site-admin/)
   Tabs SONG MANAGER | USER MANAGER in the green band (via
   SSI/admin_tabs_include.php). The column pads itself like
   .profile does: the logo overhangs the bands on wide screens.
   ============================================================ */

.admin {
    margin: 0;
    /* 20px gutters, String Therapy's (changed 09/09/2026 from 100px:
       the seven-column user table needs the width). The logo's 26px
       overhang is cleared by the top padding instead of a gutter: it
       sits at the far left, so only the first line was ever at risk. */
    padding: 36px 20px 24px;
    /* Above the logo (#siteLogo is z-index 2): the logo's white ring
       is what meets the first button, so the button wins the overlap
       (decided 09/02/2026). */
    position: relative;
    z-index: 3;
}

/* ---------- Add New User (a <details>, no script) ---------- */

.um-add {
    margin: 0 0 20px;
}

.um-add > summary {
    list-style: none;
    display: inline-flex;
}

.um-add > summary::-webkit-details-marker {
    display: none;
}

.um-note {
    margin: 12px 0 8px;
    color: var(--music-gray-medium);
    font-size: var(--music-text-base);
}

/* The create and edit forms share the auth-page field rules */
.um-form {
    max-width: 480px;
}

/* Role: two radio buttons in a row */
.um-roles {
    border: none;
    padding: 0;
    margin: 0 0 20px;
    min-width: 0;
}

.um-roles legend {
    padding: 0;
    margin-bottom: 4px;
    color: var(--music-gray-medium);
    font-size: var(--music-text-sm);
}

.um-roles label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-right: 24px;
    cursor: pointer;
}

.um-roles input[type="radio"] {
    width: auto;
    margin: 0;
    accent-color: var(--music-blue);
}

.formGroup select {
    width: 100%;
    padding: 10px;
    font-size: var(--music-text-base);
    font-family: inherit;
    border: 1px solid var(--music-border);
    border-radius: var(--music-radius-sm);
    background: var(--music-white);
}

.um-form-actions {
    display: flex;
    gap: 12px;
    align-items: center;
}

/* ---------- The list: a table at 800px and up, cards below ----------
   String Therapy's sortable table (restored 09/09/2026): the <thead>
   holds the sort links. Below 800px the same markup becomes a stack of
   cards -- the seven columns wrapped their email column on a tablet --
   with each cell labelled by its data-label. .card-table is the same
   rule set for any admin list that wants it; .user-table is the user
   manager's name for it. */

.user-table,
.card-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--music-text-base);
}

.user-table th,
.card-table th {
    text-align: left;
    padding: 6px 10px;
    color: var(--music-gray-medium);
    font-size: var(--music-text-sm);
    font-weight: normal;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    white-space: nowrap;
    border-bottom: 2px solid var(--music-divider);
}

.user-table th .sort-link,
.card-table th .sort-link {
    color: inherit;
    text-decoration: none;
}

.user-table th .sort-link:hover,
.user-table th .sort-link.active,
.card-table th .sort-link:hover,
.card-table th .sort-link.active {
    color: var(--music-link);
}

.user-table tbody tr,
.card-table tbody tr {
    border-bottom: 1px solid var(--music-divider);
}

.user-table tbody tr:nth-child(even),
.card-table tbody tr:nth-child(even) {
    background: var(--music-alt-row);
}

.user-table td,
.card-table td {
    padding: 6px 10px;
    vertical-align: top;
    /* break-word, not anywhere: anywhere shrinks every column's
       minimum to one character, so the auto layout squeezed ID to a
       digit a line and wrapped emails while Status sat wide
       (09/09/2026). break-word keeps each column's natural minimum
       and only breaks a value that cannot fit at all. */
    overflow-wrap: break-word;
}

/* Short, fixed-shape values never wrap; Status and Name take the
   squeeze instead of Email. */
.user-table td[data-label="ID"],
.user-table td[data-label="Created"],
.user-table td[data-label="Actions"] {
    white-space: nowrap;
}

@media (max-width: 799px) {
    .user-table thead,
    .card-table thead {
        display: none;
    }

    .user-table,
    .user-table tbody,
    .user-table tr,
    .user-table td,
    .card-table,
    .card-table tbody,
    .card-table tr,
    .card-table td {
        display: block;
    }

    .user-table tbody tr,
    .card-table tbody tr {
        padding: 6px 0;
        border-bottom: 2px solid var(--music-divider);
    }

    .user-table td,
    .card-table td {
        padding: 3px 10px;
        white-space: normal;
        overflow-wrap: anywhere;
    }

    .user-table td::before,
    .card-table td::before {
        content: attr(data-label) ": ";
        color: var(--music-gray-medium);
        font-size: var(--music-text-sm);
    }

    /* Cells whose label would say the obvious: a card's title line, and
       the edit / failures row under a card */
    .card-table td.card-title::before,
    .user-table .um-edit-row td::before {
        content: none;
    }
}

.card-table td.card-title {
    font-weight: bold;
    color: var(--music-gray-dark);
}

.status-active   { color: var(--music-success); }
.status-pending  { color: var(--music-gray-medium); font-style: italic; }
.status-disabled { color: var(--music-danger); }
/* .status-locked is with the song manager rules below. */

/* The upload-failures viewer under a card (09/03/2026) */
.um-failures-title {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 0 8px;
    font-weight: bold;
}

.um-failures {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--music-text-sm);
}

.um-failures th,
.um-failures td {
    text-align: left;
    padding: 4px 8px;
    border-bottom: 1px solid var(--music-divider);
    vertical-align: top;
    overflow-wrap: anywhere;
}

.um-failures th {
    color: var(--music-gray-medium);
    font-weight: normal;
}

.um-muted {
    color: var(--music-gray-medium);
}

/* Action buttons: small, one row per card, wrapping when narrow */
.um-actions form,
.card-actions form {
    display: inline;
    margin: 0;
}

.um-actions .btn-secondary,
.um-actions .btn-primary,
.card-actions .btn-secondary,
.card-actions .btn-primary {
    padding: 4px 10px;
    min-width: 0;
    font-size: var(--music-text-sm);
    margin: 2px 2px 2px 0;
}

.um-danger {
    color: var(--music-danger);
}

.btn-primary.um-danger {
    background: var(--music-danger);
    border-color: var(--music-danger);
    color: var(--music-white);
}

/* ---------- Deleted Users trash (09/09/2026) ----------
   The setlists index's Deleted Setlists recipe (.setlist-deleted-*)
   with um- names: a <details> under the table, red bold summary, one
   flex line per account. Candidate for one shared trash rule set when
   the two are next touched together. */
.um-deleted-section {
    margin: 32px 0 0;
}
.um-deleted-summary {
    cursor: pointer;
    font-weight: bold;
    color: var(--music-danger);
    padding: 6px 0;
}
.um-deleted-note {
    font-weight: normal;
    font-size: var(--music-text-sm);
    color: var(--music-gray-medium);
}
.um-deleted-list {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
}
.um-deleted-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 18px;
    padding: 8px 14px;
}
.um-deleted-list li:nth-child(even) {
    background: var(--music-alt-row);
}
.um-deleted-name  { color: var(--music-gray-medium); }
.um-deleted-login,
.um-deleted-when {
    font-size: var(--music-text-sm);
    color: var(--music-gray-medium);
}
.um-deleted-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-left: auto;
}
.um-deleted-actions form {
    margin: 0;
}

/* The card being edited, and the form card under it */
.user-table tr.um-editing {
    background: var(--music-blue-pale);
}

.user-table .um-edit-row {
    padding: 16px 10px 8px;
    background: var(--music-blue-pale);
}

/* The confirm dialogs on this page (?disable= / ?delete=) are opened
   modally by script; without script a <noscript> rule displays them,
   and this pins them to the viewport, since a non-modal dialog sits
   at its static position -- under the fixed-height page, out of
   sight. */
.admin-dialog {
    position: fixed;
    inset: 0;
    margin: auto;
    z-index: 20;
}

/* The delete dialog's form fills the dialog */
.um-delete-form {
    margin: 0;
}

.um-delete-form .formGroup {
    text-align: left;
    max-width: 420px;
    margin: 0 auto 20px;
}

/* The gutter holds until the logo moves to its own row (639px), the
   same point the header itself changes. */
@media (max-width: 639px) {
    .admin {
        padding: 16px 12px 24px;
    }
}

/* ---------- Song manager (09/02/2026) ---------- */

/* The strip between the alphabet bar and the listing: flash message
   and the two buttons. A fixed row like the search bar (see
   body.fixed-header above), so the buttons never scroll away. Shares
   .admin's gutters (the logo overhang) but not its depth. */
.sm-toolbar {
    padding-top: 12px;
    padding-bottom: 12px;
    background: var(--music-white);
    /* A rule and a shadow below, so the strip reads as fixed and the
       listing as scrolling under it. The clip keeps the shadow off
       the alphabet bar above (decided 09/03/2026). */
    border-bottom: 1px solid var(--music-brand);
    box-shadow: 0 2px 32px var(--music-gray-shadow);
    clip-path: inset(0 0 -40px 0);
}

.sm-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

/* Trash (N) sits at the far right of the toolbar (09/12/2026). */
.sm-trash-link {
    margin-left: auto;
}

/* Under a song's title bar: Edit Song and + Add Sheet, as buttons. */
.song-admin-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 12px;
    margin: 12px 10px 0;
}

/* A link cannot be :disabled; the span version carries the class. */
.btn-primary.disabled {
    background: var(--music-gray-medium);
    border-color: var(--music-gray-medium);
    cursor: not-allowed;
}

/* Missing-file message under the title bar (both views): centered,
   at the block's own size (lg), not italic (decided 09/09/2026). */
.song-no-sheets {
    margin: 12px 10px 0;
    text-align: center;
    color: var(--music-gray-medium);
}

/* Admin view: the sheets AND the audio chips stack one per line,
   left-aligned: the actions slot, then the button, then owner and
   date. The slot has a fixed width (empty when the member may not
   act on that file) so every button starts at the same x, sheets and
   audio alike. The button itself is the public one, untouched. The
   controls in the slot are the user manager's small text buttons
   (.card-actions), at the listing's size (decided 09/12/2026). */
.sheetLinks.sheet-admin-list,
.playLinks.sheet-admin-list {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 10px;
}

.sheet-admin-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 16px;
}

.sheet-actions {
    flex: 0 0 11rem;
    white-space: nowrap;
}

/* Two half-width columns inside the slot: Update on the left, Delete
   on the right, so every Delete lines up. An audio row has no Update
   and packs its Delete to the right; a trashed row's Restore form
   takes the left column. */
.sheet-actions.card-actions {
    display: flex;
    gap: 6px;
}

.sheet-actions.card-actions .btn-secondary,
.sheet-actions.card-actions .sheet-restore-form {
    flex: 0 0 calc(50% - 3px);
    box-sizing: border-box;
    margin: 0;
    text-align: center;
    font-size: var(--music-text-lg);
}

.sheet-actions.card-actions .sheet-restore-form .btn-secondary {
    width: 100%;
}

.audio-admin-list .sheet-actions.card-actions {
    justify-content: flex-end;
}

.sheet-restore-form {
    display: inline;
}

.sheet-meta {
    color: var(--music-gray-medium);
}

/* ---------- The trash in the song manager (09/12/2026) ----------
   A trashed file dims as a whole (its Restore button included); its
   button opens the copy in user_deleted/. An archived song shows the
   flag and a hint where Edit Song / + Add Sheet would be. */
.file-trashed {
    opacity: 0.55;
}

.file-trashed:hover,
.file-trashed:focus-within {
    opacity: 1;
}

.audio-link-trashed.disabled,
.sheet-btn-link.disabled {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
}

.song-block.song-archived .song-title-bar {
    opacity: 0.55;
}

.song-archived-flag {
    font-weight: bold;
    color: var(--music-danger);
}

.song-archived-hint {
    color: var(--music-gray-medium);
}

/* The Delete Song dialog's file lists (09/12/2026): left-aligned
   inside the centered dialog, headings above each list. */
.confirm-dialog .sm-delete-files {
    text-align: left;
    margin: 0 0 18px;
}

.confirm-dialog .sm-delete-files p {
    margin: 0 0 6px;
}

.confirm-dialog .sm-delete-files-head {
    font-weight: bold;
}

.confirm-dialog .sm-delete-files ul {
    margin: 0 0 12px;
    padding-left: 28px;
}

/* ---------- Trash page (09/12/2026) ----------
   trash.php: the heading and its sentence on the left, Empty Trash on
   the right, the .card-table below. */
.trash-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px 24px;
    margin: 0 0 16px;
}

.trash-head h1 {
    margin: 0 0 8px;
    text-align: left;
}

.trash-note {
    margin: 0;
    max-width: 60ch;
    color: var(--music-gray-medium);
}

.trash-empty-btn {
    white-space: nowrap;
}

/* ---------- Song manager write pages (09/03/2026) ----------
   add.php, edit-song.php, edit-sheet.php, delete.php, archives.php.
   Ported from String Therapy's adminStyle.css and stringTherapyStyle.css
   onto Salt's variables. The page heading, the notes box, the stage
   forms, the radio chips (sheet type, key, capo), the tag chips, the
   owner picker, the confirm table, the file
   chooser and the busy spinner. */

.sm-page h1 {
    margin: 0 0 12px;
    text-align: left;
}

.sm-page .message-error {
    margin-bottom: 16px;
}

.small-note {
    color: var(--music-gray-medium);
    font-size: var(--music-text-sm);
    margin: 0 0 8px;
}

/* Notes from validation or the upload (a heading and a list) */
.sm-notes {
    background: var(--music-blue-pale);
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius-sm);
    padding: 10px 14px;
    margin: 0 0 16px;
}

.sm-notes ul {
    margin: 6px 0 0;
    padding-left: 20px;
}

/* A caution before a destructive action (delete dialog, archives.php) */
.sm-warning {
    background: #fff4e0;
    border: 1px solid #f0c674;
    border-radius: var(--music-radius-sm);
    padding: 10px 14px;
    margin: 0 0 16px;
    color: #8a5a00;
}

.sm-warning ul {
    margin: 6px 0 0;
    padding-left: 20px;
}

.confirm-dialog .sm-warning {
    text-align: left;
    font-size: var(--music-text-base);
}

.upload-stage {
    margin: 12px 0 20px;
    max-width: 900px;
}

.upload-stage p {
    line-height: 1.5;
}

.form-actions {
    margin-top: 20px;
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
}

.btn-small {
    padding: 4px 10px;
    min-width: 0;
    font-size: var(--music-text-sm);
}

/* Save / Close row under the heading on the edit pages */
.sm-save-row {
    margin: 0 0 16px;
}

/* ----- File chooser (stage 1): filename field + Browse button ----- */
.file-chooser {
    display: flex;
    align-items: stretch;
    gap: 10px;
    margin: 10px 0;
    flex-wrap: wrap;
    max-width: 600px;
}

.file-chooser-name {
    flex: 1 1 200px;
    min-width: 0;
    padding: 8px 14px;
    font-size: var(--music-text-base);
    font-family: inherit;
    line-height: 1.2;
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius-sm);
    background: var(--music-white);
    color: var(--music-blue);
    box-sizing: border-box;
}

.file-chooser-name::placeholder {
    color: var(--music-gray-medium);
    font-style: italic;
    font-family: inherit;
}

.file-chooser .btn-secondary {
    flex: 0 0 auto;
}

.file-chooser-help {
    color: var(--music-gray-medium);
    font-size: var(--music-text-sm);
    margin: 4px 0 12px;
}

.drop-zone-error {
    color: var(--music-danger);
    font-weight: bold;
    margin: 8px 0 0;
}

/* ----- Form fields ----- */
.form-field {
    margin-bottom: 20px;
}

.form-field > label {
    display: block;
    font-weight: bold;
    color: var(--music-gray-dark);
    margin-bottom: 6px;
}

.form-field input[type="text"] {
    width: 100%;
    max-width: 500px;
    padding: 10px;
    font-size: var(--music-text-base);
    font-family: inherit;
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius-sm);
    box-sizing: border-box;
}

.form-field input[type="text"]:disabled,
.custom-input:disabled {
    background: var(--music-off-white);
    color: var(--music-gray-icon);
    cursor: not-allowed;
}

.form-field input[type="text"].field-error,
.custom-input.field-error {
    background: var(--music-error-bg);
    border-color: var(--music-danger);
    color: var(--music-danger);
}

/* Radio chips: sheet type, capo, and the key picker rows */
.radio-group {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.radio-option {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius-sm);
    background: var(--music-off-white);
    cursor: pointer;
    user-select: none;
    min-width: 50px;
    justify-content: center;
}

.radio-option input[type="radio"] {
    margin: 0;
    accent-color: var(--music-blue);
}

.radio-option:has(input[type="radio"]:checked) {
    background: var(--music-blue-pale);
    border-color: var(--music-blue);
    font-weight: bold;
}

/* Key picker (music_key_render_picker in key_lib.php): a None chip,
   then Major and Minor rows, parallel-aligned. Every chip is the same
   width so Cm sits under C; the None row carries an empty label to hold
   the same indent. */
.key-picker {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.key-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

.key-row-label {
    flex: 0 0 4em;
    padding-top: 10px;
    font-size: var(--music-text-sm);
    color: var(--music-gray-medium);
    text-align: right;
}

.key-chips .radio-option {
    width: 5.5em;
    min-width: 0;
    padding-left: 0;
    padding-right: 0;
}

@media (max-width: 800px) {
    .key-row-label {
        flex-basis: 3.2em;
    }
    .key-chips .radio-option {
        width: 4.6em;
        gap: 4px;
    }
}

/* Custom sheet type input, inline after the Custom radio */
input[type="text"].custom-input {
    padding: 8px 10px;
    font-size: var(--music-text-base);
    font-family: inherit;
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius-sm);
    width: 12em;
    max-width: 12em;
    box-sizing: border-box;
}

.field-error-msg {
    margin-left: 10px;
    color: var(--music-danger);
    font-size: var(--music-text-sm);
    font-weight: normal;
}

.field-error-msg.below {
    display: block;
    margin-left: 0;
    margin-top: 3px;
}

/* Custom Suffix narrower so its error message fits to the right */
input#custom_suffix {
    width: 8em;
    max-width: 8em;
    display: inline-block;
}

/* ----- Tag chips (add.php, edit-song.php) ----- */
.tag-add-row {
    display: flex;
    gap: 8px;
    align-items: center;
    max-width: 420px;
}

.tag-add-row input[type="text"] {
    flex: 1;
}

.tag-chip-list {
    list-style: none;
    margin: 6px 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.tag-chip-label {
    margin: 14px 0 0;
    font-weight: bold;
}

.tag-chip-label.is-empty {
    display: none;
}

.tag-section-divider {
    margin: 18px 0 0;
    border: 0;
    border-top: 1px solid var(--music-divider);
}

.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px;
    border: 1px solid var(--music-brand);
    border-radius: 14px;
    background: var(--music-alt-row);
    font-size: var(--music-text-base);
}

.tag-chip-text {
    font-weight: bold;
}

.tag-chip-action {
    font-size: var(--music-text-sm);
    text-decoration: underline;
    cursor: pointer;
}

.tag-chip-edit-input {
    width: 12em;
    font-size: var(--music-text-base);
    padding: 2px 4px;
}

/* ----- Confirm table (stage 3 / stage 4) ----- */
.confirm-table {
    border-collapse: collapse;
    margin: 20px 0;
    width: auto;
}

.confirm-table th,
.confirm-table td {
    text-align: left;
    padding: 8px 16px 8px 0;
    vertical-align: top;
}

/* Filenames and ids are marked up as <code> for meaning, not for a
   different face: they read at the same size and font as every other
   value (decided 09/03/2026). */
.upload-stage code {
    font-family: inherit;
    font-size: inherit;
}

.confirm-table th {
    color: var(--music-gray-medium);
    font-weight: bold;
    white-space: nowrap;
}

/* ----- Owner picker (owner_picker_lib.php) ----- */
.owner-picker {
    background: var(--music-alt-row);
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius-sm);
    padding: 15px;
}

.owner-picker-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.owner-picker-header label {
    font-weight: bold;
    color: var(--music-gray-dark);
    margin: 0;
}

.owner-picker-actions {
    display: flex;
    gap: 8px;
}

#changeOwnerBtn .arrow {
    margin-left: 4px;
    font-size: var(--music-text-sm);
}

.owner-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 10px 0;
}

.owner-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    border: 1px solid transparent;
    border-radius: 3px;
    cursor: pointer;
}

.owner-option:hover {
    background: var(--music-white);
    border-color: var(--music-divider);
}

.owner-option:has(input[type="radio"]:checked) {
    background: var(--music-blue-pale);
    border-color: var(--music-blue);
}

.owner-option input[type="radio"] {
    margin: 0;
    accent-color: var(--music-blue);
}

.owner-name {
    font-weight: bold;
    color: var(--music-gray-dark);
}

.owner-email {
    color: var(--music-gray-medium);
    font-size: var(--music-text-sm);
}

.owner-tags {
    margin-left: auto;
    font-size: var(--music-text-sm);
}

.owner-tags .status-locked,
.owner-tags .status-disabled,
.owner-tags .status-pending {
    margin-left: 4px;
}

.status-locked {
    color: var(--music-danger);
    font-weight: bold;
}

/* The files a basename change would rename (edit-song.php) */
.sm-file-list {
    margin: 5px 0;
    padding-left: 20px;
}

.sm-file-list li {
    margin: 4px 0;
}

/* Delete Song, in the Save row: an outlined red button */
.btn-secondary.sm-delete-song {
    color: var(--music-danger);
    border-color: var(--music-danger);
    margin-left: auto;
}

.btn-secondary.sm-delete-song:hover,
.btn-secondary.sm-delete-song:focus {
    background: var(--music-error-bg);
    color: var(--music-danger);
    border-color: var(--music-danger);
}

/* ----- A choice panel (archives.php restore conflict) ----- */
.sm-choice {
    background: var(--music-alt-row);
    border: 1px solid var(--music-divider);
    border-radius: var(--music-radius-sm);
    padding: 15px;
    margin: 15px 0;
}

.sm-choice h4 {
    margin: 0 0 10px;
}

.sm-choice input[type="text"] {
    padding: 8px;
    font-size: var(--music-text-base);
    font-family: inherit;
    border: 1px solid var(--music-gray-light);
    border-radius: var(--music-radius-sm);
}

/* ----- Duplicate title / filename messages (stage 2) -----
   Under the field that causes them. Red while unresolved, with the
   one link that resolves it; blue once taken, with an Undo. */
.dup-msg {
    margin: 6px 0 0;
    font-size: var(--music-text-base);
    line-height: 1.4;
}

.dup-msg.dup-error {
    color: var(--music-danger);
}

.dup-msg.dup-resolved {
    color: var(--music-blue);
}

.dup-msg a {
    font-weight: bold;
}

#filenamePreview.field-error {
    color: var(--music-danger);
}

/* ----- Busy spinner on the primary button during a submit ----- */
.spinner {
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: var(--music-white);
    border-radius: 50%;
    animation: music-spin 0.7s linear infinite;
    vertical-align: -2px;
    margin-right: 8px;
}

@keyframes music-spin {
    to { transform: rotate(360deg); }
}

.btn-primary.is-busy,
.btn-primary.is-busy:disabled {
    background: var(--music-blue);
    border-color: var(--music-blue);
    color: var(--music-white);
    opacity: 0.85;
    cursor: progress;
}


/* ============================================================
   MAINTENANCE COUNTDOWN BANNER
   Rendered by SSI/maintenance_check_include.php on /maintenance.php
   (and injectable by js/maintenance_banner.js). Amber while the
   flag is up, green (.is-done) once the poll sees it lifted. Same
   palette as .sm-warning; fresh rules on --music-* variables rather
   than String Therapy's (decided 09/03/2026).
   ============================================================ */

.maintenance-countdown {
    background: #fff4e0;
    border: 1px solid #f0c674;
    border-radius: var(--music-radius);
    padding: 14px 18px;
    margin: 0 0 20px;
    color: #8a5a00;
}

.maintenance-countdown-message {
    margin: 0 0 10px;
    line-height: 1.5;
}

.maintenance-countdown-bar-wrap {
    height: 8px;
    background: var(--music-white);
    border: 1px solid #f0c674;
    border-radius: var(--music-radius-sm);
    overflow: hidden;
}

.maintenance-countdown-bar {
    height: 100%;
    width: 0;
    background: #f0c674;
    transition: width 1s linear;
}

.maintenance-countdown-bar.no-transition {
    transition: none;
}

.maintenance-countdown-actions {
    margin-top: 12px;
}

.maintenance-countdown.is-done {
    background: #e9f5e9;
    border-color: var(--music-success);
    color: var(--music-success);
}

.maintenance-countdown.is-done .maintenance-countdown-bar-wrap,
.maintenance-countdown.is-done .maintenance-countdown-actions {
    display: none;
}

#maintenanceReturn {
    margin: 0 0 20px;
}

/* ============================================================
   AUTH PAGES
   /login/, and later /user-auth/set_password.php and
   /user-auth/forgot_password.php.

   String Therapy kept these rules in a <style> block on each of
   the three pages, which meant three near-identical copies that
   drifted. They live here from the start.
   ============================================================ */

body#loginPage {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: 4vh 16px;
}

#loginContainer {
    width: 100%;
    max-width: 380px;
    background-color: var(--music-card-bg);
    border-radius: var(--music-radius);
    box-shadow: var(--music-shadow-card);

    /* The logo is a background image rather than an <img> so the
       login page's markup stays free of decoration. The top
       padding reserves the header band's height.

       Height is set on the width only ("220px auto"), so the
       artwork scales proportionally and its native 640x292
       never has to match anything here.

       REQUIRES: /images/salt_logo_light.webp -- artwork drawn
       for a light background, so the card stays white behind it. */
    padding: 140px 20px 24px 20px;

    background-image: url("/images/salt_logo_light.webp");
    background-repeat: no-repeat;
    background-position: center 20px;
    background-size: 220px auto;
}

#loginContainer > p:first-of-type {
    margin-top: 0;
    text-align: center;
    color: var(--music-heading);
}

/* ---------- Fields ---------- */

.formGroup {
    margin-bottom: 20px;
}

.formGroup label {
    display: block;
    margin-bottom: 4px;
    color: var(--music-gray-medium);
    font-size: var(--music-text-sm);
}

.formGroup input {
    width: 100%;
    padding: 10px;
    font-size: var(--music-text-base);
    font-family: inherit;
    border: 1px solid var(--music-border);
    border-radius: var(--music-radius-sm);
}

.formGroup input:focus {
    border-color: var(--music-blue);
}

/* Field-level error state, applied by script on blur. */
.formGroup input.field-error {
    border-color: var(--music-danger);
    background-color: var(--music-error-bg);
    color: var(--music-danger);
}

/* ---------- Show-password checkbox ---------- */

.checkboxContainer {
    margin-top: 10px;
    display: flex;
    align-items: center;
}

.checkboxContainer input[type="checkbox"] {
    margin-right: 6px;
    width: auto;
}

/* The label is bound to the checkbox with for=, so the whole
   control is clickable and both halves show the pointer. */
.checkboxContainer input[type="checkbox"],
.checkboxContainer label {
    cursor: pointer;
    margin-bottom: 0;
}

/* ---------- Forgot password ---------- */

.forgot-link {
    text-align: right;
    font-size: var(--music-text-sm);
    margin-top: 6px;
}

.forgot-link a {
    color: var(--music-gray-medium);
}

/* ---------- Submit ---------- */

.buttonContainer {
    text-align: center;
    margin-top: 20px;
}

.buttonContainer input[type="submit"] {
    background-color: var(--music-navy-dark);
    color: var(--music-white);
    padding: 12px 60px;
    border: none;
    border-radius: var(--music-radius-sm);
    cursor: pointer;
    font-size: var(--music-text-base);
    font-family: inherit;
    transition: background-color 0.1s ease;
}

.buttonContainer input[type="submit"]:hover {
    background-color: var(--music-navy-logo);
}

/* A disabled submit must LOOK disabled. Without this it silently
   swallows the click and the visitor cannot tell why nothing
   happened. */
.buttonContainer input[type="submit"]:disabled,
.buttonContainer input[type="submit"][disabled],
.buttonContainer input[type="submit"]:disabled:hover,
.buttonContainer input[type="submit"][disabled]:hover {
    background-color: var(--music-gray-medium);
    cursor: not-allowed;
}

/* ---------- Password requirement hints ----------
   For set_password.php. Neutral until the field is touched, so
   the form never greets anyone in red. */

.password-rules {
    font-size: var(--music-text-sm);
    margin: 4px 0 6px;
}

.rule {
    display: block;
    color: var(--music-gray-medium);
}

.rule.ok  { color: var(--music-success); }
.rule.bad { color: var(--music-danger); }

/* ---------- Page-level status lines ----------
   Not auth-only: every page that reports an outcome uses these. */

.message-success {
    color: var(--music-flash-ok);
    text-align: center;
    background-color: var(--music-flash-ok-bg);
    padding: 12px 0;
    font-size: var(--music-text-md);
}

.message-error {
    color: var(--music-flash-bad);
    text-align: center;
    background-color: var(--music-flash-bad-bg);
    padding: 12px 0;
    font-size: var(--music-text-md);
}

.message-notice {
    text-align: center;
}


/* ============================================================
   AUTH PAGES - narrow screens
   Phones in portrait. The card gives up its shadow and rounded
   corners and runs edge to edge; the logo shrinks so the form
   still starts above the fold on a short screen.
   ============================================================ */
@media (max-width: 419px) {

    body#loginPage {
        padding: 0;
    }

    #loginContainer {
        max-width: none;
        min-height: 100vh;
        min-height: 100dvh;
        border-radius: 0;
        box-shadow: none;
        padding: 120px 20px 24px 20px;
        background-position: center 18px;
        background-size: 185px auto;
    }
}
