/* =====================================================================
   Design tokens.

   The look is 1970s/80s British broadcast. The eight saturated teletext
   colours are used as ACCENTS AND BLOCKS ONLY — they are far too bright to
   put text in. Anything carrying text uses the ink/surface tokens, which are
   chosen to clear WCAG AA (4.5:1). See the Accessibility notes in AGENTS.md.
   ===================================================================== */
:root {
    /* Teletext palette — decorative blocks and rules, never text on white. */
    --tt-red: #d90000;
    --tt-green: #00c000;
    --tt-yellow: #f0d000;
    --tt-blue: #0026ca;
    --tt-magenta: #d900d9;
    --tt-cyan: #00c8d9;
    --tt-white: #ffffff;
    --tt-black: #101010;

    /* Text and surfaces — warm off-whites to sit with the wood cabinet. */
    --ink: #1c1a17;
    --ink-muted: #5c554c;
    --surface: #ffffff;
    --surface-alt: #f6f3ee;
    --surface-sunken: #ebe6dd;
    --border: #d8d1c5;
    --border-strong: #b3aa9a;

    /* Interactive — all verified >= 4.5:1 on white. */
    --accent: #0b4f9e;
    --accent-hover: #093f7e;
    --accent-ink: #ffffff;
    --danger: #b3261e;
    --ok: #1a7f37;

    /* Cabinet / CRT, shared with the stream viewer. */
    --wood-light: #8a5c34;
    --wood-mid: #6f4a29;
    --wood-dark: #533619;
    --bezel: #131315;

    /* Spacing scale */
    --sp-1: 0.25rem;
    --sp-2: 0.5rem;
    --sp-3: 0.75rem;
    --sp-4: 1rem;
    --sp-5: 1.5rem;
    --sp-6: 2rem;

    --radius-sm: 3px;
    --radius: 6px;
    --radius-lg: 12px;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.12);
    --shadow: 0 2px 6px rgba(0, 0, 0, 0.16);
    --shadow-lg: 0 10px 24px rgba(0, 0, 0, 0.28);

    /* Width of the chat column beside the stream, and the horizontal space the
       television has to leave for it. They are declared together because they move
       together: narrow the chat without widening the picture's allowance and the
       screen simply stays small, which is the opposite of what narrowing is for. */
    --chat-width: 340px;
    --chat-reserve: 220px;   /* nav padding, gaps and the cabinet's own chrome */

    /* Chunky, slightly condensed headings echo broadcast captions; body stays
       a plain grotesque so it is easy to read. */
    --font-body: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --font-display: 'Haettenschweiler', 'Arial Narrow Bold', 'Helvetica Neue', Arial, sans-serif;
    --font-mono: ui-monospace, Consolas, 'Courier New', monospace;
}

html, body {
    font-family: var(--font-body);
    color: var(--ink);
    background: var(--surface-alt);
}

h1, h2, h3 {
    letter-spacing: 0.01em;
}

/* =====================================================================
   Accessibility helpers.
   ===================================================================== */

/* Available to screen readers, invisible on screen. Used for spoken labels
   and live-region announcements. */
.visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Hidden until focused, then pinned to the top-left. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 200;
    padding: 0.6rem 1rem;
    background: #fff;
    color: #1b6ec2;
    border: 2px solid #1b6ec2;
    border-radius: 0 0 4px 0;
}

.skip-link:focus {
    left: 0;
}

/* A clearly visible focus ring everywhere, for keyboard users. */
:focus-visible {
    outline: 3px solid #1b6ec2;
    outline-offset: 2px;
}

/* Respect a user's reduced-motion preference. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* =====================================================================
   Owncast1 structural layout (minimal — full styling to follow later).
   ===================================================================== */

/* ---- Home: stream + chat. Desktop: side by side. Mobile: stacked. ---- */
.home-layout {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}

/* Both fill the height the window actually leaves them.
   --vph and --chrome are measured in the browser (see App.razor) because neither can be
   known from CSS: vh over-reports on some mobile browsers, and the nav, footer and padding
   come to about 132px rather than the flat 6rem this used to assume. Being 36px out meant
   the page always had a strip of nothing below the footer to scroll to — invisible until
   the chat filled up and scrolling it carried on into the page. */
.home-stream {
    flex: 1 1 auto;
    min-width: 0;
    max-height: calc(var(--vph, 100vh) - var(--chrome, 6rem));
    overflow: hidden;
}

.home-chat {
    flex: 0 0 var(--chat-width);
    max-width: var(--chat-width);
    height: calc(var(--vph, 100vh) - var(--chrome, 6rem));
    position: sticky;
    top: 1rem;
}

/* ---- Mobile: fill the screen, don't scroll the page ----
   The chat composer used to sit at the bottom of a tall panel below the fold, so
   you had to scroll to type. Instead the whole layout fits the viewport: the
   stream takes the height it needs, the chat takes what's left and scrolls
   internally, and the composer ends up at the bottom of the *screen* — where a
   thumb expects it, and without breaking the convention that the input sits
   beneath the newest messages.

   dvh rather than vh so the browser's collapsing address bar and the on-screen
   keyboard don't push the composer out of view.

   Orientation keeps this to portrait. A small phone on its side is 667px across, which
   is inside this width — and stacked on a 375px-tall screen the picture and the chat
   have nowhere to go. Landscape is handled by the block below instead.

   Up to 1024px, so tablets in portrait stack too. An iPad is 810px across upright: put a
   340px chat column beside the picture and the picture is left with 250, squeezed by
   width on a device with height to spare. Stacked it gets the full width. 1024 is the
   widest iPad in portrait, and a desktop window that narrow and that tall is better
   stacked as well. */
@media (max-width: 1024px) and (orientation: portrait) {
    .page {
        height: 100vh;
        height: 100svh;
        height: var(--vph, 100svh);   /* measured innerHeight; see the note in App.razor */
        overflow: hidden;
    }

    .main-content {
        flex: 1 1 auto;
        min-height: 0;      /* lets the flex child actually shrink */
        overflow: hidden;
        padding: var(--sp-2);
    }

    .home-layout {
        flex-direction: column;
        /* The base rule uses flex-start so the sticky chat aligns to the top on
           desktop. Stacked, that would make children shrink to their content
           width — which collapses the stripped-back mobile TV to a few pixels. */
        align-items: stretch;
        height: 100%;
        min-height: 0;
        gap: var(--sp-2);
    }

    .home-stream {
        flex: 0 0 auto;     /* the picture keeps its size */
        max-height: none;
        overflow: visible;
    }

    .home-chat {
        flex: 1 1 auto;     /* chat absorbs whatever is left */
        min-height: 0;
        flex-basis: auto;
        max-width: none;
        width: 100%;
        height: auto;
        position: static;
    }

    /* NB the chat panel's own mobile overrides live at the end of this file — they
       have to come after the base .chat-panel rules to win. */
}

/* ---- Landscape phones, and anything else short ----
   Portrait stacks the picture above the chat. Turned on its side a phone has no
   vertical room to stack anything, so the side-by-side layout is kept and height
   becomes the constraint instead of width.

   Keyed on height, because width alone gets this wrong: a phone in landscape is
   around 850px across, comfortably past the 800px mobile breakpoint, so it used to
   be served the full desktop treatment — wood cabinet and all — on a screen 390px
   tall. That is the bug, and no width threshold fixes it without also catching
   small desktop windows that are perfectly fine as they are.

   No lower width bound: a small phone on its side is 667px across and needs this
   layout every bit as much as a large one at 926px. */
@media (max-height: 600px) {
    /* The narrower chat comes from .plain-screen below, which is always on at this size.
       Setting a second width here only looked like it worked: the class selector is more
       specific than :root, so it won anyway and this value was never the one applied. */
    .page {
        height: 100vh;
        height: 100svh;
        height: var(--vph, 100svh);   /* measured innerHeight; see the note in App.razor */
        overflow: hidden;
    }

    .main-content {
        flex: 1 1 auto;
        min-height: 0;
        overflow: hidden;
        padding: var(--sp-2);
    }

    .home-layout {
        align-items: stretch;
        height: 100%;
        min-height: 0;
        gap: var(--sp-2);
    }

    .home-stream {
        max-height: none;
    }

    .home-chat {
        min-height: 0;
        height: auto;
        position: static;
    }
}

/* The plain-screen preference narrows the chat too. On a television the picture is
   what the room is watching; 340px of chat on a 10-foot display is a lot of column
   for a conversation nobody is reading up close. */
:root.plain-screen {
    --chat-width: 280px;
    --chat-reserve: 150px;
}

/* Narrower again on a short screen, where the chat is competing with the picture for
   width rather than sitting beside a large one. A small phone on its side is 667px
   across: at 280px the chat took nearly half of it and squeezed the picture down to
   237px wide, when the height would happily have allowed 280.

   After the rule above, not inside the landscape block higher up the file. That block
   sets these on :root, which the .plain-screen selector outranks — so the value there
   was written, overridden, and never once applied. */
@media (max-height: 600px) {
    /* Both selectors, because the cabinet is now the default on a phone in landscape and
       the class is only present if someone asked for the plain screen. Two rules rather
       than one on :root, since .plain-screen outranks :root and would otherwise put the
       chat back to 280px for exactly the people who chose the narrower look. */
    :root,
    :root.plain-screen {
        --chat-width: 220px;
    }

    /* The cabinet needs more width than a bare picture: its padding, the bezel, and the
       column of controls beside the screen. Stripped back, that space is the picture's. */
    :root {
        --chat-reserve: 190px;
    }

    :root.plain-screen {
        --chat-reserve: 120px;
    }
}

/* ---- Stream viewer ---- */
.stream-frame {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #000;
}

.stream-frame iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.stream-live-badge {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-weight: 700;
    color: #c0392b;
    margin-bottom: 0.4rem;
}

.live-dot {
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    background: #e74c3c;
    display: inline-block;
}

.stream-title {
    color: #333;
    font-weight: 400;
}

.stream-offline {
    padding: 2rem;
    background: #f1f3f5;
    border: 1px solid #dee2e6;
    border-radius: 6px;
    text-align: center;
    min-height: 200px;
}

/* ---- Chat panel ---- */
.chat-panel, .chat-locked {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    height: 100%;
    min-height: 320px;
    overflow: hidden;
}

.chat-locked {
    height: auto;
    padding: var(--sp-4);
    gap: var(--sp-2);
    align-items: flex-start;
}

/* Header styled like an on-screen caption bar. */
.chat-header {
    padding: 0.5rem 0.75rem;
    background: var(--tt-black);
    color: #fff;
    border-bottom: 3px solid var(--tt-cyan);
}

.chat-header h2 {
    margin: 0;
    font-size: 0.95rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-family: var(--font-display);
    font-weight: 700;
}

.chat-locked h2 {
    font-family: var(--font-display);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin: 0;
}

.chat-messages {
    flex: 1 1 auto;
    /* Without this a flex item won't shrink below its content, so on a short screen
       the message list pushes the composer off the bottom instead of scrolling. */
    min-height: 0;
    overflow-y: auto;
    /* Scrolling to the end of the messages stops there instead of carrying on into the
       page behind. Reading back through a busy chat should never move the page. */
    overscroll-behavior: contain;

    /* Holds the absolutely positioned .visually-hidden spans inside this box.
       Every message carries one — the word "at" before the time, for screen readers — and
       .visually-hidden is position:absolute, as that technique requires. Without a
       positioned ancestor here their containing block was .home-chat, the sticky column,
       which does not clip; so each span was laid out at its flow position deep inside the
       scrolled list, hundreds or thousands of pixels down, and the document grew to reach
       the last one. Measured with 40 messages: the page was 4,516px tall against an 848px
       window, all of it empty below the footer. With this, 848 exactly.

       The list still scrolls internally — the spans move with its content instead of
       escaping to the page. */
    position: relative;
    padding: 0.5rem 0.75rem;
}

.chat-empty {
    color: #868e96;
    font-style: italic;
}

/* Smaller and tighter than body copy, because chat is scanned rather than read — 16px is
   what you set an article in, and every message was spending 15px of its 55 on the gap and
   the rule beneath it. Measured on a 390x844 phone: 55px a message before, 47 after, which
   is five messages visible against not quite six.

   In rem rather than px, so somebody who has set a larger default font in their browser
   still gets larger chat text. */
.chat-message {
    font-size: 0.9rem;
    margin-bottom: 0.35rem;
    padding-bottom: 0.3rem;
    border-bottom: 1px solid var(--surface-sunken);
}

.chat-message:last-child {
    border-bottom: none;
}

.chat-message .chat-name {
    font-weight: 700;
    margin-right: 0.35rem;
}

.chat-message .chat-time {
    color: #adb5bd;
    font-size: 0.75rem;
}

.chat-message .chat-text {
    white-space: pre-wrap;
    word-break: break-word;
    /* Tighter than the 1.5 body copy uses. Chat lines are short, so the extra leading buys
       nothing and costs a message off the bottom of the list. */
    line-height: 1.4;
}

.chat-you {
    color: #868e96;
    font-weight: 400;
    font-size: 0.75rem;
    margin-left: 0.25rem;
}

.chat-composer {
    display: flex;
    gap: 0.4rem;
    padding: 0.5rem;
    border-top: 1px solid #dee2e6;
}

.chat-input {
    flex: 1;
    padding: 0.4rem;
}

/* Emoji picker */
.chat-emojis {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem;
    padding: 0.35rem 0.5rem;
    border-top: 1px solid var(--border);
    background: var(--surface-alt);
}

.chat-emoji {
    border: none;
    background: none;
    cursor: pointer;
    font-size: 1.15rem;
    line-height: 1;
    padding: 0.15rem 0.2rem;
    border-radius: 4px;
}

.chat-emoji:hover:not(:disabled) {
    background: #f1f3f5;
}

.chat-emoji:disabled {
    opacity: 0.4;
    cursor: default;
}

/* Destructive actions, set apart so they aren't clicked by accident */
.btn-danger {
    display: inline-block;
    padding: 0.45rem 0.9rem;
    border: 1px solid #8f1d17;
    border-radius: var(--radius);
    background: var(--danger);
    color: #fff;
    text-decoration: none;
    cursor: pointer;
}

.btn-danger:hover {
    background: #8f1d17;
}

.danger-zone {
    margin-top: var(--sp-6);
    padding: var(--sp-4);
    border: 1px solid #f0c9c6;
    border-left: 4px solid var(--danger);
    border-radius: var(--radius);
    background: #fdf6f5;
}

.danger-zone h2 {
    margin-top: 0;
    font-size: 1.05rem;
    color: var(--danger);
}

.confirm-delete {
    display: inline-flex;
    gap: var(--sp-2);
    align-items: center;
    font-size: 0.8rem;
}

/* Neutral notice — neither success nor failure (e.g. a sign-in the user cancelled) */
.alert-info {
    background: #eef4fb;
    border: 1px solid #c4d8ef;
    color: #123a63;
    padding: 0.6rem 0.9rem;
    border-radius: var(--radius-sm);
}

/* Moderator actions on a message — quiet until the row is hovered or focused,
   so they don't clutter the conversation. */
.chat-mod {
    margin-left: var(--sp-2);
    opacity: 0;
    font-size: 0.7rem;
}

.chat-message:hover .chat-mod,
.chat-mod:focus-within {
    opacity: 1;
}

.chat-mod .btn-link {
    background: none;
    border: 0;
    padding: 0 0.2rem;
    font-size: 0.7rem;
    cursor: pointer;
    color: var(--accent);
}

.chat-mod .btn-link.danger {
    color: var(--danger);
}

/* Why the composer is unavailable (muted, or posting too fast) */
.chat-blocked {
    margin: 0;
    padding: var(--sp-2) var(--sp-3);
    background: #fdecea;
    border-top: 1px solid #f5c6cb;
    color: var(--danger);
    font-size: 0.8rem;
}

/* Remaining-characters counter under the composer */
.chat-remaining {
    margin: 0;
    padding: 0 var(--sp-3) var(--sp-2);
    font-size: 0.75rem;
    color: var(--ink-muted);
    text-align: right;
}

.chat-remaining.low {
    color: var(--danger);
    font-weight: 700;
}

/* Blocked-content notice (URL / email) */
.chat-error {
    color: #c0392b;
    background: #fdecea;
    padding: 0.35rem 0.6rem;
    font-size: 0.85rem;
    border-top: 1px solid #f5c6cb;
}

/* ---- Content / account / admin pages ---- */
.content-page, .account-page {
    max-width: 960px;
    margin: 0 auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: var(--sp-5);
}

/* Broadcast-caption headings: chunky, uppercase, with a coloured rule beneath. */
.content-page h1, .account-page h1 {
    font-family: var(--font-display);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 1.9rem;
    margin: 0 0 var(--sp-2);
    padding-bottom: var(--sp-2);
    border-bottom: 3px solid var(--tt-yellow);
}

.content-page h2, .account-page h2 {
    font-size: 1.1rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-muted);
}

/* Text inputs */
.form-control {
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
}

.form-control:focus {
    border-color: var(--accent);
    outline: 3px solid rgba(11, 79, 158, 0.35);
    outline-offset: 0;
}

.account-columns {
    display: flex;
    gap: 2rem;
    flex-wrap: wrap;
}

.account-form, .account-external {
    flex: 1 1 280px;
}

.form-field {
    margin-bottom: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.form-field.checkbox {
    flex-direction: row;
    align-items: center;
}

/* A checkbox that carries an explanation under it: the box and its wording stay on one
   line, and the note sits beneath rather than beside — which is what .form-field.checkbox
   above would have given, since it lays the whole field out as a row.

   The whole label is the hit target, so the wording is clickable and not just the box. */
.checkbox-label {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    cursor: pointer;
}

.checkbox-label input[type="checkbox"] {
    flex: 0 0 auto;
}

.field-hint {
    margin: 0;
    font-size: 0.85rem;
    /* Muted, but still measured against the surface for AA — a hint that explains what
       consent means is not decoration and has to be readable. */
    color: var(--ink-muted, #495057);
}

.external-login-buttons {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* =====================================================================
   Third-party sign-in buttons.

   These deliberately follow Google's and Meta's own branding rather than the
   site's vintage styling: an unfamiliar-looking sign-in button makes people
   hesitate, and familiarity matters more here than visual consistency. Sizes,
   colours and wording follow their published guidance.
   ===================================================================== */
.btn-provider {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    width: 100%;
    min-height: 40px;          /* both sets of guidelines ask for >= 40px */
    padding: 0.5rem 0.9rem;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    transition: background-color 0.12s ease, box-shadow 0.12s ease;
}

.provider-mark {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}

/* Google: white button, their neutral grey border, near-black label. */
.btn-provider-google {
    background: #ffffff;
    border: 1px solid #747775;
    color: #1f1f1f;
}

.btn-provider-google:hover {
    background: #f7f8f8;
    box-shadow: var(--shadow-sm);
}

/* Meta: their blue with a white mark and label.
   Uses #166fe0 — Meta's own darker shade — rather than their primary #1877f2,
   because white on the primary measures 4.23:1 and so misses WCAG AA for normal
   text (4.5:1). This is visually near-identical, still one of their colours, and
   measures 4.79:1. Everything else on the site clears AA; this shouldn't be the
   one exception. */
.btn-provider-facebook {
    background: #166fe0;
    border: 1px solid #166fe0;
    color: #ffffff;
}

.btn-provider-facebook:hover {
    background: #1461c7;
    border-color: #1461c7;
}

.btn-external {
    padding: 0.5rem 0.75rem;
    border: 1px solid #ced4da;
    background: #fff;
    cursor: pointer;
    border-radius: 4px;
    text-align: left;
}

/* Chunky, slightly tactile buttons — pressed states echo the set's push-buttons. */
.btn-primary, .btn-secondary {
    display: inline-block;
    padding: 0.5rem 1rem;
    border-radius: var(--radius-sm);
    text-decoration: none;
    cursor: pointer;
    font: inherit;
    font-weight: 600;
    border: 1px solid transparent;
    box-shadow: var(--shadow-sm);
}

.btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
}

.btn-primary:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
}

.btn-secondary {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--ink);
}

/* Spacing between buttons belongs to whatever is laying them out, not to the button.
   .btn-secondary used to carry a left margin for the case where it follows a primary
   button on one line — but most of its uses are on their own, where the margin pushed
   it 6px out of line with the text above it, and in the signed-out chat panel the two
   are stacked, so it offset the lower one from the upper. That was the visible symptom.

   Wrap buttons that share a line in this instead. It works in either direction, adds
   nothing when there is only one button, and cannot misfire when the container turns
   out to be a column. */
.button-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
}

.btn-secondary:hover {
    background: var(--surface-sunken);
}

.btn-primary:active, .btn-secondary:active {
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25);
}

.btn-primary:disabled, .btn-secondary:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.admin-grid {
    display: flex;
    gap: 2rem;
    flex-wrap: wrap;
    align-items: flex-start;
}

.admin-list, .admin-edit, .admin-preview {
    flex: 1 1 320px;
}

/* Tables scroll rather than pushing the page sideways on narrow screens. */
.admin-list, .admin-edit, .admin-preview {
    overflow-x: auto;
}

.admin-table {
    width: 100%;
    border-collapse: collapse;
}

.admin-table thead th {
    background: var(--tt-black);
    color: #fff;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 0.78rem;
}

.admin-table th, .admin-table td {
    border-bottom: 1px solid var(--border);
    padding: 0.5rem 0.6rem;
    text-align: left;
    font-size: 0.9rem;
    vertical-align: middle;
}

.admin-table tbody tr:nth-child(even) {
    background: var(--surface-alt);
}

.admin-table tbody th[scope="row"] {
    font-weight: 600;
}

.admin-table .url-cell {
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.btn-link.danger {
    color: #c0392b;
}

.admin-table tr.expired td {
    opacity: 0.55;
}

.role-cell input[disabled] {
    cursor: not-allowed;
}

.status-ok {
    color: #26884d;
}

.muted, .validation-summary {
    color: #868e96;
}

.form-control.code {
    font-family: ui-monospace, Consolas, monospace;
    width: 100%;
}

/* Long-form text pages (privacy policy and similar) */
.prose {
    max-width: 46rem;
    line-height: 1.65;
}

.prose h2 {
    margin-top: var(--sp-6);
    padding-top: var(--sp-2);
    border-top: 2px solid var(--border);
}

.prose h3 {
    margin-top: var(--sp-5);
    font-size: 1.05rem;
}

.prose ul {
    padding-left: 1.25rem;
}

.prose li {
    margin-bottom: var(--sp-2);
}

/* Profile page */
.profile-summary {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.35rem 1rem;
    margin-bottom: 1rem;
}

.profile-summary dt {
    font-weight: 700;
    color: #555;
}

.profile-summary dd {
    margin: 0;
}

.badge-ok, .badge-warn {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.05rem 0.4rem;
    border-radius: 3px;
    font-size: 0.75rem;
}

.badge-ok {
    background: #d3f2df;
    color: #1c7a44;
}

.badge-warn {
    background: #fdecea;
    color: #c0392b;
}

.member-links {
    line-height: 1.8;
}

.member-link-desc {
    color: #868e96;
}

/* =====================================================================
   Short-screen chat panel overrides.

   Placed last deliberately: they have the same specificity as the base
   .chat-panel rules above, so they must come after them to take effect.

   Height is in the condition as well as width because every rule here is really
   about a screen without much of it, and a phone held sideways has less height
   than one held upright while being twice as wide. Keyed on width alone, the
   320px minimum below survived into landscape and clipped the panel.

   The portrait arm reaches 1024px so a stacked tablet gets these too: there the
   chat sits under the picture with whatever height is left, which is exactly the
   situation the 320px minimum was wrong for.
   ===================================================================== */
@media (max-width: 1024px) and (orientation: portrait), (max-height: 600px) {
    /* The desktop 320px minimum would push the composer off a short screen. */
    .chat-panel {
        min-height: 0;
        height: 100%;
    }

    .chat-locked {
        min-height: 0;
        /* Fills its column and scrolls inside it, rather than growing to fit its text and
           standing proud of the bottom. On a phone in landscape, height:auto put the panel
           26px past the column and cut the sign-in button in half — the one control on it
           that anybody needs. */
        height: 100%;
        overflow-y: auto;
        /* Trimmed to suit the space. At full size the panel needed 42px of scrolling on a
           phone in landscape, which put "sign in to chat" below the fold — an invitation
           nobody sees is not much of an invitation. */
        padding: var(--sp-2);
        gap: var(--sp-1);
        font-size: 0.9rem;
    }

    /* Everything except the message list keeps its size; the list absorbs the
       shrink, so the composer stays put at the bottom of the screen. */
    .chat-header, .chat-emojis, .chat-composer {
        flex: 0 0 auto;
    }

    /* One scrollable row rather than three wrapped ones — a grid of 18 buttons
       costs too much height on a phone. */
    .chat-emojis {
        flex-wrap: nowrap;
        overflow-x: auto;
        gap: var(--sp-1);
    }

    .chat-emoji {
        flex: 0 0 auto;
    }
}
