/*  The shared vocabulary.
    ==========================================================================
    Buttons, the badge, the click-to-copy chip, the hint under a form field, and
    the banner that says an admin is signed in as somebody else.

    These lived in admin-table.css and admin-dashboard.css, which meant every
    page in the application loaded four admin stylesheets - about sixty
    kilobytes - so that a member's match page could draw a button. The four are
    loaded for staff now and this is loaded for everybody.

    Admin-only rules that merely mention a button stayed where they were:
    ".sb-queue-row .sb-btn" is a dashboard rule about a queue, not a rule about
    buttons.
*/

/* --- from admin-table.css ----------------------------------------------- */


.sb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Middle, not baseline. An inline-flex box sits on the text baseline of its
       first line by default, and that baseline moves with the icon inside it -
       so a row of buttons with different icons came out as a staircase, each one
       a few pixels lower than the last. Aligning on the middle instead makes
       them level whether or not the thing holding them is a flex row. */
    vertical-align: middle;
    gap: .375rem;
    height: 2.25rem;
    padding: 0 .75rem;
    font-family: inherit;
    font-size: .875rem;
    line-height: 1;
    white-space: nowrap;
    color: var(--bs-body-color);
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: .5rem;
    text-decoration: none;
    cursor: pointer;
}

    .sb-btn:hover {
        background: var(--bs-tertiary-bg);
        border-color: var(--bs-secondary-border-subtle);
        color: var(--bs-body-color);
    }
    .sb-btn:disabled,
    .sb-btn[disabled] {
        opacity: .5;
        cursor: not-allowed;
    }

    .sb-btn.is-on {
        background: var(--sb-brand-soft, rgba(11, 108, 127, .08));
        border-color: var(--sb-brand, #0B6C7F);
        color: var(--sb-brand, #0B6C7F);
    }

.sb-btn-primary {
    color: #fff;
    background: var(--sb-brand, #0B6C7F);
    border-color: var(--sb-brand, #0B6C7F);
}

    .sb-btn-primary:hover {
        color: #fff;
        background: #095866;
        border-color: #095866;
    }

.sb-badge {
    display: inline-block;
    padding: .125rem .5rem;
    font-size: .6875rem;
    font-weight: 600;
    border-radius: 1rem;
    background: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

    .sb-badge.is-good {
        background: var(--sb-green-soft, rgba(0, 149, 42, .1));
        color: var(--sb-green, #007521);
    }

    .sb-badge.is-warn {
        background: var(--sb-amber-soft, rgba(249, 160, 0, .12));
        color: var(--sb-amber, #B36F00);
    }

    .sb-badge.is-bad {
        background: var(--sb-maroon-soft, rgba(149, 0, 0, .08));
        color: var(--sb-maroon, #950000);
    }

    .sb-badge.is-brand {
        background: var(--sb-brand-soft, rgba(11, 108, 127, .08));
        color: var(--sb-brand, #0B6C7F);
    }
.sb-btn-sm {
    height: auto;
    padding: .1875rem .5rem;
    font-size: .75rem;
    gap: .25rem;
}
.sb-btn-danger {
    color: var(--sb-maroon, #950000);
    border-color: var(--sb-maroon-soft, rgba(149, 0, 0, .35));
}

    .sb-btn-danger:hover {
        color: #fff;
        background: var(--sb-maroon, #950000);
        border-color: var(--sb-maroon, #950000);
    }
.sb-btn-primary.sb-btn-danger {
    color: #fff;
    background: var(--sb-maroon, #950000);
    border-color: var(--sb-maroon, #950000);
}

    .sb-btn-primary.sb-btn-danger:hover {
        color: #fff;
        background: #7a0000;
        border-color: #7a0000;
    }
.sb-btn-primary.sb-btn-good {
    color: #fff;
    background: var(--sb-green, #007521);
    border-color: var(--sb-green, #007521);
}

    .sb-btn-primary.sb-btn-good:hover {
        color: #fff;
        background: #005c1a;
        border-color: #005c1a;
    }

.sb-field-hint {
    display: block;
    margin-top: .375rem;
    font-size: .75rem;
    color: var(--bs-secondary-color);
}
/* --- from admin-dashboard.css ------------------------------------------- */

:root {
    --sb-brand: #0B6C7F;
    --sb-brand-soft: rgba(11, 108, 127, .08);
    --sb-amber: #B36F00; /* darkened from #F9A000 for contrast on white text-size type */
    --sb-amber-soft: rgba(249, 160, 0, .12);
    --sb-green: #007521; /* darkened from #00952A for the same reason */
    --sb-green-soft: rgba(0, 149, 42, .1);
    --sb-maroon: #950000;
    --sb-maroon-soft: rgba(149, 0, 0, .08);
    --sb-sand: #f7f4eb;

    /* Male takes the brand teal. Female needed a colour of its own: every other
       hue in the palette already carries a meaning on this page (amber lapsed,
       red overdue, green good), and a gender marker must not read as a status. */
    --sb-male: var(--sb-brand);
    --sb-male-soft: rgba(11, 108, 127, .10);
    --sb-female: #B04A70;
    --sb-female-soft: rgba(176, 74, 112, .12);
}

/* .sb-copy is two things at once: the 20px icon chip in the admin tables and
   dashboard queues, and the click-to-copy behaviour on a labelled button beside
   a referral link. copy-link.js binds on the class, so both need it - but only
   the first wants to look like a chip.

   Without the exclusions below, a button carrying both got the chip's 1.25rem
   width. min-width: auto then floored it at the width of the word inside it, so
   "Copy" sat in a 30px box with 28px of its own padding and spilled out of its
   own border.

   A button that already carries a button class is dressed by that class; this
   one only lends it the behaviour. */
.sb-copy:not(.sb-home-btn):not(.sb-acct-btn) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border: 1px solid var(--bs-border-color);
    border-radius: .3125rem;
    background: var(--bs-body-bg);
    color: var(--sb-brand);
    font-size: .625rem;
    line-height: 1;
    cursor: pointer;
}

.sb-copy:not(.sb-home-btn):not(.sb-acct-btn):hover {
    background: var(--sb-brand-soft);
    border-color: var(--sb-brand);
}

.sb-copy.is-copied {
    background: var(--sb-green-soft);
    border-color: var(--sb-green);
    color: var(--sb-green);
}
@media (prefers-reduced-motion: no-preference) {

    .sb-copy {
        transition: transform .16s ease, box-shadow .16s ease,
                    border-color .16s ease, background-color .16s ease, color .16s ease;
    }

    .sb-copy:active {
        transform: scale(.92);
    }
    @keyframes sbPop {
        0%   { transform: scale(.6); }
        60%  { transform: scale(1.15); }
        100% { transform: scale(1); }
    }

    .sb-copy.is-copied i {
        display: inline-block;
        animation: sbPop .28s ease;
    }
}

/* Sticky, not fixed.

   Fixed took the banner out of the flow, so nothing reserved room for it and
   the page started underneath: the site header sat behind the banner with 44px
   of it hidden. There was a rule meant to hold that space -
   body.is-impersonating { padding-top: 2.25rem } - but it had two problems.
   It lived in admin-dashboard.css, which the layout only sends when the signed
   in user is staff; an impersonated session is signed in as the *member*, so
   that stylesheet is never sent and the rule never applied - the one state it
   existed for was the one state it could not reach. And 2.25rem is 36px against
   a banner that measures 44, so even on the pages that did load it the gap was
   8px short, and shorter still on a narrow screen where the sentence wraps to
   two or three lines.

   Sticky ends the guessing: the banner keeps its place in the flow, so it holds
   exactly its own height whatever that turns out to be, and still pins to the
   top of the viewport as the page scrolls. Verified with the real stylesheet -
   body carries overflow-x: hidden, which computes body's overflow-y to auto and
   is the usual way sticky gets broken, but html's overflow is visible so the
   overflow propagates to the viewport and body is not itself a scroll
   container. Scrolled 400px: the banner stayed at top 0 while the header went
   to -357. */
.sb-impersonating {
    position: sticky;
    top: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: .5rem 1rem;
    font-size: .8125rem;
    color: #fff;
    background: var(--sb-maroon, #950000);
    box-shadow: 0 1px 6px rgba(0, 0, 0, .25);
}

    .sb-impersonating strong {
        font-weight: 700;
    }

    .sb-impersonating form {
        margin: 0 0 0 auto;
    }

.sb-impersonating-stop {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    padding: .25rem .625rem;
    font-size: .8125rem;
    color: var(--sb-maroon, #950000);
    background: #fff;
    border: 0;
    border-radius: .375rem;
    cursor: pointer;
    white-space: nowrap;
}

    .sb-impersonating-stop:hover {
        background: #f2f2f2;
    }

/* --- a two or three answer question ---------------------------------------

   Rendered by <sb-choice>. The wizard asked fourteen of these through a select,
   which hides two options behind a click and looks identical whether it holds
   two of them or fifty. Shown, they are one press instead of two and the answer
   is readable without opening anything.

   The input is the button. appearance: none takes the browser's dot away and
   the ring is drawn back on with a border, so the control keeps its own focus
   handling, its own keyboard behaviour and its own place in the tab order -
   which is what goes wrong when a radio is hidden and a div is styled to look
   like one. */

.sb-choice {
    display: flex;
    flex-wrap: wrap;
    gap: .625rem;
}

.sb-choice-option {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    min-width: 7rem;
    padding: .625rem 1rem;
    border: 1px solid var(--bs-border-color, #e9eaec);
    border-radius: .625rem;
    background: var(--bs-body-bg, #fff);
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    /* The label is the hit area, so nothing selects when you press it twice. */
    user-select: none;
}

    .sb-choice-option:hover {
        border-color: var(--sb-brand, #0B6C7F);
    }

    /* The ring. Sized in ems so it follows the label rather than being pinned to
       whatever the page's font happens to be. */
    .sb-choice-option input[type="radio"] {
        appearance: none;
        -webkit-appearance: none;
        flex: 0 0 auto;
        width: 1.125em;
        height: 1.125em;
        margin: 0;
        border: 2px solid var(--bs-border-color, #ced2d6);
        border-radius: 50%;
        background: transparent;
        cursor: pointer;
    }

    .sb-choice-option input[type="radio"]:checked {
        border-color: var(--sb-brand, #0B6C7F);
        /* The dot, as a ring of background between the border and the fill -
           one element rather than a pseudo one to position inside it. */
        background: var(--sb-brand, #0B6C7F);
        box-shadow: inset 0 0 0 3px var(--bs-body-bg, #fff);
    }

    .sb-choice-option input[type="radio"]:focus-visible {
        outline: 2px solid var(--sb-brand, #0B6C7F);
        outline-offset: 2px;
    }

/* The chosen answer. :has() rather than a class, so nothing has to script the
   selected state - the label follows the input it contains. */
.sb-choice-option:has(input[type="radio"]:checked) {
    border-color: var(--sb-brand, #0B6C7F);
    background: var(--sb-brand-soft, rgba(11, 108, 127, .08));
}

/* A step that cannot be edited disables its fieldset. The buttons should read
   as unavailable rather than merely refusing to move. */
fieldset:disabled .sb-choice-option {
    opacity: .65;
    cursor: default;
}

@media (prefers-reduced-motion: no-preference) {
    .sb-choice-option,
    .sb-choice-option input[type="radio"] {
        transition: border-color .15s ease, background-color .15s ease,
                    box-shadow .15s ease;
    }
}

/* ---------------------------------------------------------------------------
   Powered by 88ninety, and which build is running.

   Sits under the existing footer row on signed-in pages. Deliberately quiet: it
   is a maintainer's note, and it appears on every page, so it has to be readable
   without ever competing with the page above it. */
.sb-poweredby {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    font-size: .6875rem;   /* 11px - smaller than anything else on the page */
    line-height: 1.4;
    color: var(--bs-secondary-color, #6c757d);
    margin-top: -5px;
}

/* The footer row: copyright, this, links. The two sides share what is left over
   equally, so the middle lands on the centre of the row and not on the centre of
   the gap between two items of different widths. */
.footer .copy {
    align-items: center;
    gap: 1rem;
}

/* The copyright is a <p>, which carries Bootstrap's 1rem bottom margin. Centring
   the row then centres the paragraph's box rather than its text, so the line sat
   visibly higher than the other two. */
.footer .copy p {
    margin: 0;
}

.footer .copy .copy__side {
    flex: 1 1 0;
    min-width: 0;
}

.footer .copy .sb-poweredby {
    flex: 0 0 auto;
}

/* .footer .copy a floats right, which stacks floated siblings right-to-left: the
   legal links have therefore always rendered as "Privacy PolicyTerms", in reverse
   and with the separator stranded ahead of them. text-end on the container already
   does the aligning the float was there for, so dropping it puts the pair back in
   the order they are written - and stops it tearing the powered-by line apart. */
.footer .copy a {
    float: none;
}

.sb-poweredby a {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    color: inherit;
    text-decoration: none;
}

.sb-poweredby a:hover,
.sb-poweredby a:focus-visible {
    color: var(--sb-brand, #0B6C7F);
    text-decoration: underline;
}

/* Height-capped rather than width-capped, so the box follows the asset's own
   proportions whatever they are.

   32px because the asset is the stacked lockup - the 88 mark above the word - so
   "ninety" is only about a fifth of the image's height. At the 14px this started
   at that word came out around two pixels tall and read as a smudge; 32 is the
   point it becomes legible without the logo taking over a row whose text is 11px.
   A horizontal version of the wordmark would read at half this height. */
.sb-poweredby__logo {
    height: 32px;
    width: auto;
    vertical-align: middle;
}

/* The stand-in until the real asset is dropped in. Weighted rather than sized,
   so swapping the image in does not change the height of the line. */
.sb-poweredby__mark {
    font-weight: 700;
    letter-spacing: .01em;
}

/* Tabular figures so the timestamp does not jitter between deployments. */
.sb-poweredby__build {
    font-variant-numeric: tabular-nums;
    cursor: help;
}

/* Below this the three will not sit on one line without the timestamp wrapping
   through the middle of itself, so the row becomes three stacked lines.

   Each keeps its own alignment - the copyright left, the links right, this
   centred - rather than being forced to centre: .text-end carries !important and
   .footer .copy p is the more specific selector, so centring the sides would take
   an !important of its own to win an argument not worth having. */
@media (max-width: 700px) {
    .footer .copy {
        flex-wrap: wrap;
        gap: .5rem;
    }

    .footer .copy .copy__side {
        flex: 1 0 100%;
    }

    /* Built last, shown last: the legal links matter more to a member than a
       deployment stamp does. */
    .footer .copy .sb-poweredby {
        order: 3;
    }
}

@media (max-width: 480px) {
    /* Two short lines beat one wrapped mid-timestamp. */
    .sb-poweredby {
        flex-direction: column;
        gap: .125rem;
    }

    .sb-poweredby > span[aria-hidden] {
        display: none;
    }
}
