/*
 * alexiuz shared controls — buttons, range sliders, credits pill
 * --------------------------------------------------------------
 * Source of truth: /home/internetieruser/alexiuz-shared/styles/controls.css
 * Symlinked into each site at public/styles/shared/.
 *
 * Extracted from generor.com, which is the reference implementation. Like
 * ui.css this file contains no literal colours — every value is a token, so a
 * site re-skins by overriding tokens in its own theme.css.
 *
 * Load order:
 *     tokens.css -> [base.css] -> [ui.css] -> controls.css
 *                -> <site>/theme.css -> <site> app styles
 *
 * THE ONE RULE THIS FILE ENCODES
 * ------------------------------
 * generor never fills a control with the accent. Buttons, active toggles and
 * slider thumbs all sit on a dark panel and carry the accent as *text*, as a
 * *border*, or as the *thumb* — never as a background with light text on top.
 *
 * That is not a stylistic preference, it is a legibility constraint. The
 * network accent (#6afcff) against white text is 1.24:1; against the dark
 * panel ramp it is 12-15:1. Every site that has independently reached for
 * "cyan button, white label" has produced an unreadable control.
 *
 * The press affordance is therefore motion, not colour: the control sinks one
 * pixel (translateY(1px)) and its text brightens to --text.
 *
 * RELATIONSHIP TO ui.css
 * ----------------------
 * ui.css already styles `a.button` / `button.default-btn` and is loaded first
 * where a site takes both. Two deliberate differences here:
 *
 *   - ui.css's `.primary` and `.danger` also set `border-color`. generor sets
 *     only `color`, so that is what this file does. A site loading both gets
 *     ui.css's tinted border plus this file's text colour; harmless, but if
 *     you want generor's exact look, load controls.css without ui.css.
 *   - ui.css has no slider styling at all. That half of this file is new.
 */

/* ======================================================================
   Buttons
   ====================================================================== */

/* `.btn` is imaig's and vidaye's name for the same thing; both are listed so
   those sites adopt this file by deleting their local copies rather than
   renaming markup.

   THIS RULE NOW OWNS PROPORTIONS AND TYPE TOO.
   It used to own only colour and the press affordance, leaving padding,
   font-size and text-transform to each site. That policy produced a 2-2 split:
   imaig and vidaye each carried a byte-identical local `.btn` override (2px
   radius, 12px/22px, 700, uppercase) while generor and muuic took the defaults
   here (6px, 8px/16px, 600, sentence case) — so a button looked like two
   different controls depending on which site you were on, and the agreeing
   half of the network expressed that agreement as copy-paste in two app.css
   files. The imaig/vidaye values won and moved here; their local copies are
   deleted — EXCEPT the uppercase, which was dropped when the convention was
   revisited (see text-transform below). Sites still own *placement* (a card's
   action row, a toolbar's compact strip) via the .btn-sm / .btn-icon
   modifiers below. */
.button,
a.button,
.btn,
a.btn,
button.default-btn,
.generate-btn,
.choose-sub {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    width: fit-content;
    padding: var(--space-6) 22px;
    background: var(--surface-4);
    border: 1px solid var(--border-3);
    /* Near-square. --radius-xs is exactly imaig's and vidaye's local
       `--radius: 2px`, which is why that token and not a new one. */
    border-radius: var(--radius-xs);
    /* generor's signature hard offset shadow — not a blur, a solid drop. */
    box-shadow: 2px 2px 2px var(--black);
    color: var(--text-link);
    font-family: inherit;
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
    letter-spacing: 0.5px;
    /* Explicitly none, not merely absent: imaig and vidaye used to uppercase
       every button locally, and when the network consolidated on their button
       the uppercase came with it. It is off by decision — a label reads faster
       in its normal case, and "Generate" / "Download" are words, not headings.
       Stated rather than omitted so a site that still carries a stray
       `text-transform: uppercase` on a button ancestor cannot leak it back in
       through inheritance. */
    text-transform: none;
    text-decoration: none;
    cursor: pointer;
    transition: var(--transition);
}

/* The whole system's press affordance: the control sinks by a pixel and its
   label brightens. No background change, so the contrast never degrades. */
.button:hover,
.button:active,
a.button:hover,
a.button:active,
.btn:hover,
.btn:active,
button.default-btn:hover,
button.default-btn:active,
.generate-btn:hover,
.generate-btn:active,
.choose-sub:hover,
.choose-sub:active {
    color: var(--text);
    transform: translateY(1px);
}

.button:focus-visible,
a.button:focus-visible,
.btn:focus-visible,
button.default-btn:focus-visible,
.generate-btn:focus-visible {
    outline: none;
    box-shadow: 2px 2px 2px var(--black), var(--shadow-focus);
}

.button:disabled,
.button.disabled,
.btn:disabled,
.btn.disabled,
button.default-btn:disabled,
button.default-btn.disabled,
a.button.disabled,
.generate-btn:disabled,
.generate-btn.disabled {
    color: var(--text-dim);
    border-color: var(--border-2);
    cursor: not-allowed;
    opacity: 0.7;
    /* Cancel the press affordance so a dead control does not feel clickable. */
    transform: none;
}

/* ---- Semantic variants -------------------------------------------------
   Text colour only. The panel background is identical across every variant,
   which is what keeps them legible and what makes them read as one family.

   generor's live mapping, for reference:
     .generate-btn, #accept-all-cookies-btn, .login-btn  -> accent-bright
     #decline-all-cookies-btn                            -> danger
     .reset-btn, .reset-toggle-btn                       -> accent-orange
     .register-btn, #affiliate-dashboard                 -> accent-pink

   generor spells the last three as literals (#ff6a6a, #f6b485) that are one
   or two digits off the tokens they clearly mean. Tokenised here. */

.button.cta,
.button.primary,
.btn-primary,
.btn.cta,
button.default-btn.cta,
button.default-btn.primary,
.generate-btn {
    color: var(--accent-bright);
}

.button.danger,
.btn-danger,
button.default-btn.danger,
.danger-btn {
    color: var(--danger);
}

.button.warn,
button.default-btn.warn {
    color: var(--accent-orange);
}

.button.success,
button.default-btn.success {
    color: var(--success-2);
}

.button.alt,
button.default-btn.alt {
    color: var(--accent-pink);
}

/* Chromeless variant — no panel, no shadow. For inline/tertiary actions.
   NOTE: `.btn-ghost` is deliberately NOT hooked in here. imaig and vidaye
   already use that name for a *panelled* secondary button, which is what the
   base rule above now gives them for free — claiming the name here would strip
   the panel off every secondary button on both sites. */
.button.ghost,
button.link-btn {
    background: none;
    border-color: transparent;
    box-shadow: none;
    color: var(--text-link);
}

.button.ghost:focus-visible,
button.link-btn:focus-visible {
    box-shadow: var(--shadow-focus);
}

/* ======================================================================
   Icon buttons
   ======================================================================
   A button whose whole label is a glyph. Pair with data-tooltip for the
   sighted hover label and aria-label for the accessible name — the icon alone
   is a name to neither audience. */

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* display:block kills the inline baseline gap that would otherwise make an
   icon button a couple of pixels taller than a text button beside it. */
.icon-btn svg {
    display: block;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* ---- Size and icon modifiers -------------------------------------------
   Promoted from imaig's app.css, where `.btn-sm` and `.btn-icon` were local
   and `.btn-icon` was only ever defined scoped (`.row-buttons .btn-icon`,
   `.section-h.btn-icon`). imaig's own canvas toolbar spells its buttons
   `.btn .btn-ghost .btn-sm .btn-icon`, so the .btn-icon there matched nothing
   and the layout only held because the base .btn is already inline-flex.
   Centralising them makes that combination mean the same thing everywhere
   rather than only inside two of imaig's containers.

   `.btn-ghost` deliberately has no rule — see the note above the .button.ghost
   block. On imaig and vidaye it means "the secondary button", which the base
   panel already is, so it is consolidated by having nothing to consolidate. */

.btn-sm {
    padding: var(--space-4) var(--space-6);
    font-size: var(--text-sm);
}

/* The labelled sibling of .icon-btn: glyph AND text. 14px rather than
   .icon-btn's 16px, because here the icon sits next to a cap height rather
   than carrying the control on its own. */
.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
}

.btn-icon svg {
    display: block;
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}

/* ======================================================================
   Scrollbars
   ======================================================================
   The network default, lifted from generor.

   THE IDEA: a scrollbar thumb is a control surface, not a browser artifact.
   generor's styles.css proves it by listing `::-webkit-scrollbar-thumb` in the
   very same selector groups as `.neu-btn`, `.custom-dropdown-option` and
   `#account-menu a` — the thumb wears the same panel ground, the same hairline
   and the same radius as every other thing you can push. Nothing here is a new
   value; it is the button treatment applied to one more surface.

   Because those are all tokens, a site inherits this on its own ramp for free:
   vidaye's theme.css already maps --surface-4 and --border-3 onto its violet
   ink, so its bars come out violet without a line of site CSS.

   FAITHFUL, NOT LITERAL. generor arrives at this over three separate rule
   blocks that overwrite each other, and the winner is what is written below.
   Two deliberate departures:

     - The track is written `transparent` rather than generor's
       `background: var(--black); opacity: 0; width: 0` — the same invisible
       track, spelled as the leftovers of an earlier design.
     - `height` is set alongside `width`. generor sets only `width`, so its
       horizontal bars are still browser-default height. Anything with a
       horizontal scroller — vidaye's timeline, a filmstrip — wants the match.

   FIREFOX gets colour but not width: `scrollbar-width: thin` would undercut
   the 12px this sets for everyone else, and Firefox's default width is nearer
   to it than `thin` is. Colour alone is the closer read. It is set on :root
   and inherits, so it reaches every scroller without naming one.

   TO OPT OUT, a site overrides these on its own containers in its app.css,
   which loads later — imaig does exactly that for its sidebar and canvas. */

:root {
    scrollbar-color: var(--surface-4) transparent;
}

::-webkit-scrollbar {
    width: 12px;
    height: 12px;
}

::-webkit-scrollbar-track {
    background: transparent;
    /* Keeps the thumb off the very ends of the trough, so it reads as a handle
       travelling in a groove rather than a bar filling a slot. */
    margin: 6px 0;
}

/* The same three declarations as `.btn` above. That is the whole point — if
   the button ground ever moves, the scrollbar moves with it. */
::-webkit-scrollbar-thumb {
    background: var(--surface-4);
    border: 1px solid var(--border-3);
    border-radius: var(--radius-md);
}

::-webkit-scrollbar-thumb:hover,
::-webkit-scrollbar-thumb:active {
    background: var(--surface-hover);
}

/* Where two bars meet. Transparent for the same reason the track is: the page
   ground shows through, whatever ramp the site is on. */
::-webkit-scrollbar-corner {
    background: transparent;
}

/* ======================================================================
   Tooltips
   ======================================================================
   CSS-only. No script, no initialisation, no per-site wiring: put
   data-tooltip="…" on any element and it works.

   USE INSTEAD OF title=, NOT ALONGSIDE IT. The browser draws its own tooltip
   from title, so an element carrying both shows two.

   NOT AN ACCESSIBILITY FEATURE. data-tooltip is invisible to assistive tech —
   generated content is not reliably exposed and this has no aria wiring. An
   icon-only control still needs aria-label for its accessible name; the
   tooltip sits on top of that. It is reachable on :focus-visible so keyboard
   users get it too.

   Gated on (hover: hover): on touch, :hover latches after a tap and would
   strand a bubble on screen with no way to dismiss it.

   CAVEAT: an ancestor with overflow:hidden clips the bubble. This is
   positioned, not popover/anchor-based, so there is no escaping a clipping
   container. */

@media (hover: hover) {
    [data-tooltip] {
        position: relative;
    }

    [data-tooltip]::after {
        content: attr(data-tooltip);
        position: absolute;
        /* Below by default: most tooltips here hang off header and toolbar
           controls, where an upward bubble runs off the top of the viewport. */
        top: calc(100% + var(--space-3));
        left: 50%;
        transform: translateX(-50%);
        z-index: 300;
        padding: var(--space-2) var(--space-5);
        background: var(--surface-4);
        border: 1px solid var(--border-3);
        border-radius: var(--radius-sm);
        box-shadow: var(--shadow-md);
        color: var(--text);
        /* Reset inherited type. Buttons on imaig and vidaye are uppercase,
           bold and letter-spaced; a tooltip inheriting that reads as shouting. */
        font-family: var(--font-sans);
        font-size: var(--text-xs);
        font-weight: var(--weight-normal);
        font-style: normal;
        letter-spacing: normal;
        text-transform: none;
        line-height: 1.4;
        white-space: nowrap;
        opacity: 0;
        /* Never intercept the pointer, or the bubble under the cursor would
           flicker between shown and hidden. */
        pointer-events: none;
        transition: opacity 0.12s ease;
    }

    [data-tooltip]:hover::after,
    [data-tooltip]:focus-visible::after {
        opacity: 1;
        /* Dwell before appearing, so sweeping across a row of controls does not
           strobe a bubble at every one. Inbound only; hiding stays immediate. */
        transition-delay: 0.35s;
    }

    /* For controls near the bottom of the viewport. */
    [data-tooltip][data-tooltip-pos="above"]::after {
        top: auto;
        bottom: calc(100% + var(--space-3));
    }
}

/* ======================================================================
   Chips — segmented choice
   ======================================================================
   A row of mutually exclusive options too small to deserve a select: export
   format, size preset, quality. imaig already uses this pattern (.chip /
   .chip-group with .is-on); centralised here so muuic and vidaye get the same
   control instead of each inventing one.

   Selected state is the accent as label and border, never a fill — the rule
   every other control in this file follows. */

.chip-group {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.chip {
    padding: var(--space-3) var(--space-6);
    background: var(--surface-4);
    border: 1px solid var(--border-3);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-family: inherit;
    font-size: var(--text-base);
    cursor: pointer;
    transition: var(--transition);
}

.chip:hover:not(:disabled) {
    color: var(--text);
}

.chip.is-on,
.chip[aria-pressed="true"] {
    border-color: var(--accent);
    color: var(--accent-bright);
}

.chip:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}

.chip:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* ======================================================================
   Toggle buttons
   ======================================================================
   A toggle is a button whose *state* is expressed by the accent, again as
   text rather than as a fill. Supports both a .active class and the correct
   ARIA attribute, so markup that is already accessible needs no extra class. */

.toggle-btn,
.mode-toggle-btn {
    padding: var(--space-2) var(--space-5);
    background: rgba(var(--surface-raised-rgb), 0.1);
    border: 1px solid rgba(var(--border-4-rgb), 0.4);
    border-radius: var(--radius-sm);
    color: var(--text-link);
    font-family: inherit;
    font-size: var(--text-base);
    cursor: pointer;
    transition: var(--transition);
}

.toggle-btn:hover,
.mode-toggle-btn:hover {
    color: var(--text);
}

.toggle-btn.active,
.toggle-btn[aria-pressed="true"],
.mode-toggle-btn.active {
    color: var(--accent-bright);
    border-color: var(--accent);
}

/* ======================================================================
   Range sliders
   ======================================================================
   generor carries four near-identical range styles (.range-slider,
   .duration-slider, .voice-control-slider, and .temperature-slider layered on
   top of .duration-slider) that differ only in track colour and thumb size —
   including a webkit thumb of 19px against a moz thumb of 14px on the same
   control. They are collapsed into one here; the legacy names are kept in the
   selector lists so generor's migration onto this file is a deletion.

   .range-input is the canonical name for new markup. */

.range-wrap,
.duration-slider-wrap,
.range-slider-wrap {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-4) 0;
}

.range-input,
.duration-slider,
.range-slider,
.voice-control-slider {
    -webkit-appearance: none;
    appearance: none;
    flex: 1;
    width: 100%;
    height: 6px;
    background: var(--surface-3);
    border-radius: var(--radius-xs);
    outline: none;
    cursor: pointer;
}

/* Thumb. Vendor pseudo-elements cannot be combined into one selector list —
   an unrecognised pseudo-element invalidates the whole rule — so the webkit
   and moz blocks are duplicated on purpose. Keep them in sync. */
.range-input::-webkit-slider-thumb,
.duration-slider::-webkit-slider-thumb,
.range-slider::-webkit-slider-thumb,
.voice-control-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 18px;
    height: 18px;
    background: var(--accent-bright);
    border: none;
    border-radius: var(--radius-circle);
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.range-input::-moz-range-thumb,
.duration-slider::-moz-range-thumb,
.range-slider::-moz-range-thumb,
.voice-control-slider::-moz-range-thumb {
    width: 18px;
    height: 18px;
    background: var(--accent-bright);
    border: none;
    border-radius: var(--radius-circle);
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.range-input::-webkit-slider-thumb:hover,
.duration-slider::-webkit-slider-thumb:hover,
.range-slider::-webkit-slider-thumb:hover,
.voice-control-slider::-webkit-slider-thumb:hover {
    /* Scale only. The thumb used to also throw a
       `box-shadow: 0 0 8px rgba(accent,.5)` glow, which on a 6px track read as
       a smudge around the handle rather than a highlight — and on a dense strip
       of sliders the glows overlapped each other. Growth alone is the
       affordance. */
    transform: scale(1.2);
}

.range-input::-moz-range-thumb:hover,
.duration-slider::-moz-range-thumb:hover,
.range-slider::-moz-range-thumb:hover,
.voice-control-slider::-moz-range-thumb:hover {
    /* Scale only. The thumb used to also throw a
       `box-shadow: 0 0 8px rgba(accent,.5)` glow, which on a 6px track read as
       a smudge around the handle rather than a highlight — and on a dense strip
       of sliders the glows overlapped each other. Growth alone is the
       affordance. */
    transform: scale(1.2);
}

.range-input:focus-visible,
.duration-slider:focus-visible,
.voice-control-slider:focus-visible {
    box-shadow: var(--shadow-focus);
}

/* Numeric readout next to the track. Tabular figures stop the row from
   shifting as the value changes. */
.range-value,
.duration-value,
.temperature-value {
    min-width: 40px;
    text-align: center;
    color: var(--accent-bright);
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    font-variant-numeric: tabular-nums;
}

/* ---- Gradient track variant -------------------------------------------
   For scales where the two ends mean opposing things rather than less/more —
   generor uses it for LLM temperature (focused <-> creative).

   NOTE: this is fixed here. generor defines --track-gradient only inside a
   commented-out block, so `background: var(--track-gradient)` resolves to
   nothing and the track silently falls back to the browser default. The
   gradient has not actually rendered on generor for as long as that comment
   has been there. */

.range-input.gradient,
.temperature-slider {
    --track-gradient: linear-gradient(
        90deg,
        var(--accent-blue) 0%,
        var(--accent-bright) 50%,
        var(--accent-orange) 100%
    );
}

.range-input.gradient::-webkit-slider-runnable-track,
.temperature-slider::-webkit-slider-runnable-track {
    height: 6px;
    background: var(--track-gradient);
    border-radius: var(--radius-xs);
}

.range-input.gradient::-moz-range-track,
.temperature-slider::-moz-range-track {
    height: 6px;
    background: var(--track-gradient);
    border-radius: var(--radius-xs);
}

/* A styled webkit track resets the thumb's vertical centring, so it has to be
   nudged back by (track height - thumb height) / 2. */
.range-input.gradient::-webkit-slider-thumb,
.temperature-slider::-webkit-slider-thumb {
    margin-top: -6px;
}

/* ======================================================================
   Range sliders — imaig recipe
   ======================================================================
   A second slider, extracted from imaig's app.css where it was written as
   `input[type="range"].slider, .edit-section input[type="range"]`.

   It differs from .range-input above in ways that are a deliberate design
   choice rather than drift, which is why both are here:

     .range-input   6px well, 18px flat accent disc, no outline.
     .slider        4px pill rail, 14px accent disc cut out of the ground by a
                    2px ring and lit by a soft accent glow.

   The second reads as a precision control — imaig uses it for brightness,
   contrast and brush size, where the thumb has to stay findable against a
   photograph. The first reads as a chunky value picker. Collapsing them into
   one is the same kind of follow-up as consolidating the near-duplicate
   surface steps in tokens.css: worth doing, WILL change pixels on generor, and
   so not something to fold in silently.

   The `input[type="range"]` qualifier is load-bearing, not decoration.
   `.slider` on its own is a name sites already use for other things — muuic
   has `.toggle .slider` on the track of a checkbox switch — and an unqualified
   rule here would repaint it.

   Markup:
     <input type="range" class="slider" id="brightness" min="0" max="200" value="100">
     <label class="lbl-sm" for="brightness">Brightness <span>134%</span></label>

   The caption sits BELOW the track and carries the live value in a trailing
   <span>; see .lbl-sm at the end of this section. */

input[type="range"].slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    /* Taller than the rail on purpose: the input box is the pointer target, so
       a 4px rail with a 4px hit area would be unusable. */
    height: 24px;
    margin: var(--space-2) 0;
    background: transparent;
    cursor: pointer;
    /* The track and thumb are drawn by hand below, so drop any native tint the
       site may have set with accent-color. */
    accent-color: auto;
}

/* ---- Rail ----
   imaig hard-codes #2a2a40 here. Tokenised as --border-3, which is that exact
   value on the muuic/imaig/vidaye ramp: a 4px pill reads as a hairline rule
   rather than a surface, and picking a --surface-N step instead would make the
   rail vanish into the panel it sits on. */
input[type="range"].slider::-webkit-slider-runnable-track {
    height: 4px;
    background: var(--border-3);
    border-radius: var(--radius-pill);
    border: none;
}

input[type="range"].slider::-moz-range-track {
    height: 4px;
    background: var(--border-3);
    border-radius: var(--radius-pill);
    border: none;
}

/* ---- Thumb ----
   Vendor pseudo-elements cannot share a selector list — one unrecognised
   pseudo-element invalidates the whole rule — so webkit and moz are written
   out separately. Keep them in sync.

   The dark border cuts the disc out of whatever it sits on and the 1px accent
   ring puts the colour back at the very edge, so the thumb stays legible over
   a light photo as well as a dark panel. That is the whole point of this
   variant over .range-input's bare disc. */
input[type="range"].slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 14px;
    background: var(--accent);
    border: 2px solid var(--page-bg);
    border-radius: var(--radius-circle);
    /* (rail height - thumb height) / 2, less the border. A styled webkit track
       drops the thumb's automatic centring. */
    margin-top: -5px;
    box-shadow: 0 0 0 1px var(--accent), 0 0 8px rgba(var(--accent-bright-rgb), 0.4);
    transition: transform 0.12s ease, box-shadow 0.12s ease;
    cursor: pointer;
}

/* Firefox centres the thumb on the track itself, so no margin-top here —
   adding one would push it off the rail. */
input[type="range"].slider::-moz-range-thumb {
    width: 14px;
    height: 14px;
    background: var(--accent);
    border: 2px solid var(--page-bg);
    border-radius: var(--radius-circle);
    box-shadow: 0 0 0 1px var(--accent), 0 0 8px rgba(var(--accent-bright-rgb), 0.4);
    transition: transform 0.12s ease, box-shadow 0.12s ease;
    cursor: pointer;
}

input[type="range"].slider::-webkit-slider-thumb:hover {
    transform: scale(1.2);
    box-shadow: 0 0 0 1px var(--accent), 0 0 14px rgba(var(--accent-bright-rgb), 0.7);
}

input[type="range"].slider::-moz-range-thumb:hover {
    transform: scale(1.2);
    box-shadow: 0 0 0 1px var(--accent), 0 0 14px rgba(var(--accent-bright-rgb), 0.7);
}

/* The focus ring goes on the thumb, not the input: the input box is 24px of
   mostly transparent padding, so a ring on it would float well clear of the
   control the user is actually driving. */
input[type="range"].slider:focus-visible {
    outline: none;
}

input[type="range"].slider:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 1px var(--accent), 0 0 0 4px rgba(var(--accent-rgb), 0.25);
}

input[type="range"].slider:focus-visible::-moz-range-thumb {
    box-shadow: 0 0 0 1px var(--accent), 0 0 0 4px rgba(var(--accent-rgb), 0.25);
}

/* ---- Vertical ----
   For faders — muuic's mixer is the first user. Add alongside .slider:

     <input type="range" class="slider slider-vertical" orient="vertical" …>

   `writing-mode: vertical-lr` is what actually turns the control (Chrome 121+,
   Firefox 129+). The legacy `orient="vertical"` attribute is harmless next to
   it and is what older Firefox reads, so markup that already carries it should
   keep it. `direction: rtl` puts the high end at the top, which is the only
   direction a fader is ever read in.

   Only the geometry is restated here — colours, glow and hover come from
   .slider above. A 4px rail is `height` when horizontal and `width` when
   vertical, and the thumb's centring offset has to move axis with it, so none
   of it can be inherited. */
input[type="range"].slider.slider-vertical {
    writing-mode: vertical-lr;
    direction: rtl;
    width: 24px;
    height: 100%;
    margin: 0 var(--space-2);
}

input[type="range"].slider.slider-vertical::-webkit-slider-runnable-track {
    width: 4px;
    height: 100%;
}

input[type="range"].slider.slider-vertical::-moz-range-track {
    width: 4px;
    height: 100%;
}

/* (rail width - thumb width) / 2, less the border: the vertical twin of the
   margin-top the horizontal thumb carries. That margin-top has to be cleared
   or it offsets the thumb along the rail instead of across it. */
input[type="range"].slider.slider-vertical::-webkit-slider-thumb {
    margin-top: 0;
    margin-left: -5px;
}

/* ---- Caption row ----
   The label that goes under a .slider. Name on the left, live value on the
   right, pushed apart rather than centred so a value that changes width does
   not shift the name. Also usable standalone as a small field label. */
.lbl-sm {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin: var(--space-4) 0 var(--space-2);
    color: var(--text-muted);
    font-size: var(--text-xs);
    line-height: 1.4;
}

/* Anything after the label text is a value, so it goes right. Written as a
   margin rather than relying on justify-content alone, so the rule still holds
   when the label is `Brightness <span>134</span><span>%</span>`. */
.lbl-sm > span:not(:first-child) {
    margin-left: auto;
}

/* Tabular figures keep the row from twitching as digits change width. */
.lbl-sm > span {
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* ======================================================================
   Credits pill
   ======================================================================
   The network's balance indicator, top-right of the header. Extracted from
   imaig and vidaye, which had byte-identical copies of it.

   It reads as a quiet accent outline rather than a filled control, so it sits
   in the nav without competing with the primary CTA next to it — the same
   reason nothing else here is filled with the accent.

   Markup:
     <a class="credits-pill" id="credits-pill" href="…/credits" target="_blank">
       <span class="credits-icon" aria-hidden="true"></span>
       <span class="credits-amount">…</span>
     </a>

   The amount is filled in by the site's own script. Keep `credits-amount` as
   the hook — every site's balance JS targets that class. */

.credits-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-6);
    /* Same panel as every button in the system — the pill differs by silhouette
       (a capsule) rather than by colour. An accent-tinted ground made it read as
       a foreign element in generor's header, where the nav links are all
       --surface-4 panels. */
    background: var(--surface-4);
    border: 1px solid var(--border-3);
    border-radius: var(--radius-pill);
    box-shadow: 2px 2px 2px var(--black);
    color: var(--accent-bright);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    letter-spacing: 0.4px;
    text-decoration: none;
    transition: var(--transition);
}

/* The pill takes the HEADER hover, not the button one: it lifts a pixel and
   its ground lightens, exactly like generor's nav links sitting beside it.
   Deliberately leaves the border and the label alone — an accent border on
   hover made the pill the loudest thing in the row, and the label is already
   the accent so brightening it has nowhere to go. */
.credits-pill:hover {
    background-color: var(--surface-hover);
    transform: translateY(-1px);
    text-decoration: none;
}

.credits-pill:focus-visible {
    outline: none;
    box-shadow: 2px 2px 2px var(--black), var(--shadow-focus);
}

/* The bolt is drawn as a mask rather than an <img> so it takes its colour from
   the pill — one asset, no per-theme variants, and it inherits any accent
   override a site's theme.css makes. currentColor makes it track the label,
   so it brightens with the text on hover instead of staying cyan. */
.credits-pill .credits-icon {
    display: inline-block;
    width: 12px;
    height: 12px;
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><polygon points='23,1 11,1 9,19 13,19 11,31 23,11 19,11'/></svg>") center/contain no-repeat;
            mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><polygon points='23,1 11,1 9,19 13,19 11,31 23,11 19,11'/></svg>") center/contain no-repeat;
}

/* Tabular figures so the pill does not change width as the balance ticks. */
.credits-pill .credits-amount {
    font-variant-numeric: tabular-nums;
}

/* Prompt variant — for signed-out or unlinked users, where the pill carries a
   call to action ("Claim 25/week free") rather than a number. Wider text, so
   the letter-spacing is dropped. */
.credits-pill.prompt {
    letter-spacing: normal;
    font-weight: var(--weight-semibold);
}

/* ---- Credits panel ------------------------------------------------------
   The popover the pill opens (alexiuz-shared/js/credits-panel.js). Anchored to
   the pill rather than centred as a modal: it is a glance at a number, not a
   task, so it should not take the page over.

   Deliberately not built on ui.css's .modal — generor and alexiuz load ui.css
   but muuic, imaig and vidaye do not, and this has to look the same on all
   five. Everything it needs is here. */

.credits-panel {
    position: absolute;
    top: calc(100% + var(--space-4));
    right: 0;
    z-index: 200;
    width: 290px;
    max-width: calc(100vw - var(--space-9));
    padding: var(--space-4) var(--space-6) var(--space-6);
    background: var(--surface-2);
    border: 1px solid var(--border-3);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-modal);
    color: var(--text);
    font-size: var(--text-base);
    font-weight: var(--weight-normal);
    letter-spacing: normal;
    text-align: left;
    cursor: default;
    animation: fadeIn 0.15s linear forwards;
}

.credits-panel[hidden] {
    display: none;
}

.credits-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: var(--space-4);
}

.credits-panel-title {
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.credits-panel-x {
    padding: 0 var(--space-2);
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--text-2xl);
    line-height: 1;
    cursor: pointer;
    transition: var(--transition);
}

.credits-panel-x:hover {
    color: var(--text);
}

/* ---- Balance ---- */
.credits-panel-total {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    color: var(--accent-bright);
}

.credits-panel-total .credits-icon {
    display: inline-block;
    width: 16px;
    height: 16px;
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><polygon points='23,1 11,1 9,19 13,19 11,31 23,11 19,11'/></svg>") center/contain no-repeat;
            mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><polygon points='23,1 11,1 9,19 13,19 11,31 23,11 19,11'/></svg>") center/contain no-repeat;
}

.credits-panel-total-value {
    font-size: 24px;
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
}

/* free / earned / paid, always all three. Equal columns so the row reads as a
   fixed set of buckets rather than a variable list. */
.credits-panel-split {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
    margin-top: var(--space-5);
}

.credits-panel-cell {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: var(--space-3) var(--space-4);
    background: rgba(var(--surface-0-rgb), 0.5);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.credits-panel-cell-value {
    color: var(--text);
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    font-variant-numeric: tabular-nums;
}

.credits-panel-cell-label {
    color: var(--text-dim);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
}

/* An empty bucket stays visible but recedes — present, clearly zero, not
   competing with the buckets that hold something. */
.credits-panel-cell.is-zero .credits-panel-cell-value {
    color: var(--text-dim);
}

/* ---- Card verification offer ----
   Shown only to accounts without a verified card, which get neither the
   one-off bonus nor the weekly refill. Accent-tinted rather than panelled, so
   it reads as an offer instead of another row of data. */
.credits-panel-verify {
    margin-top: var(--space-6);
    padding: var(--space-5);
    background: rgba(var(--accent-rgb), 0.07);
    border: 1px solid rgba(var(--accent-rgb), 0.28);
    border-radius: var(--radius-md);
}

.credits-panel-verify-text {
    margin: 0 0 var(--space-4);
    color: var(--text-soft);
    font-size: var(--text-sm);
}

.credits-panel-verify-btn {
    width: 100%;
    font-size: var(--text-base);
}

.credits-panel-verify-note {
    margin: var(--space-3) 0 0;
    color: var(--text-dim);
    font-size: var(--text-xs);
    text-align: center;
}

/* ---- Log ---- */
.credits-panel-label {
    margin-top: var(--space-6);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.credits-panel-log {
    margin: var(--space-3) 0 0;
    padding: 0;
    list-style: none;
}

.credits-panel-log li {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid rgba(var(--border-rgb), 0.5);
    font-size: var(--text-sm);
}

.credits-panel-log li:last-child {
    border-bottom: none;
}

.credits-panel-amt {
    flex-shrink: 0;
    min-width: 42px;
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-semibold);
    text-align: right;
}

.credits-panel-amt.is-spend {
    color: var(--text-muted);
}

.credits-panel-amt.is-gain {
    color: var(--success-2);
}

/* The description is the flexible column: it truncates so the amount, the
   service tag and the timestamp always stay readable. min-width:0 is required
   or a long unbroken description refuses to shrink inside the flex row. */
.credits-panel-desc {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-soft);
}

/* Which site the charge came from. Rendered only when it differs from the site
   being viewed, since tagging every row "imaig" while on imaig is noise. */
.credits-panel-tag {
    flex-shrink: 0;
    padding: 1px var(--space-3);
    background: rgba(var(--accent-blue-rgb), 0.12);
    border-radius: var(--radius-pill);
    color: var(--accent-blue);
    font-size: var(--text-xs);
}

.credits-panel-when {
    flex-shrink: 0;
    color: var(--text-dim);
    font-size: var(--text-xs);
    white-space: nowrap;
}

/* ---- States and footer ---- */
.credits-panel-msg,
.credits-panel-empty {
    margin: var(--space-4) 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.credits-panel-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-5);
    margin-top: var(--space-6);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border);
}

/* .credits-panel-buy also carries `button cta`, so the panel's primary action
   is literally the same control as every other CTA in the network. */
.credits-panel-buy {
    font-size: var(--text-base);
}

.credits-panel-all {
    color: var(--text-link);
    font-size: var(--text-sm);
    text-decoration: none;
    transition: var(--transition);
}

.credits-panel-all:hover {
    color: var(--accent-bright);
}

/* The panel is absolutely positioned against the pill, so the pill has to be a
   containing block. Applied to the pill's parent instead of the pill itself —
   an inline-flex anchor is a fragile thing to hang a popover off. */
.credits-pill-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
}

/* ---- Mobile: centre it -------------------------------------------------
   Anchoring under the pill stops working on a phone. The pill sits at the
   right edge of the header, so a 290px popover hanging off it either clips at
   the viewport edge or gets pushed flush against it, and on a short screen the
   activity list runs past the fold with nothing to scroll.

   Below the 768px breakpoint it becomes a centred dialog instead: fixed rather
   than absolute, so it escapes the header's bounds entirely. */
@media (max-width: 768px) {
    .credits-panel {
        position: fixed;
        top: 50%;
        left: 50%;
        right: auto;
        /* Both axes, so it stays centred regardless of its own height. */
        transform: translate(-50%, -50%);
        width: min(340px, calc(100vw - var(--space-11)));
        max-width: none;
        max-height: 80vh;
        overflow-y: auto;
    }

    /* A centred dialog floating over live content needs something behind it.
       Drawn as a pseudo-element so no markup or JS changes are needed —
       .credits-panel is positioned with a z-index, so it establishes a
       stacking context and a z-index:-1 child paints behind the panel's own
       background while still covering the viewport.

       pointer-events:none is required, not decorative. Pseudo-elements DO take
       part in hit testing, and the event target for one resolves to its
       originating element — so without this, a tap on the backdrop would arrive
       as a tap on .credits-panel itself. The script's outside-click test is
       `!panel.contains(e.target)`, which would then be false and the panel
       would refuse to close. Making it transparent to pointers lets the tap
       fall through to the page, where it correctly reads as outside. */
    .credits-panel::before {
        content: "";
        position: fixed;
        inset: 0;
        z-index: -1;
        background: rgba(var(--black-rgb), 0.6);
        pointer-events: none;
    }
}

/* Optional companion link (generor's "Log" — the credit history page). Sits
   beside the pill as plain text so it stays clearly secondary to it. */
.credits-log-link {
    margin-left: var(--space-4);
    color: var(--text-muted);
    font-size: var(--text-sm);
    text-decoration: none;
    transition: var(--transition);
}

.credits-log-link:hover {
    color: var(--accent-bright);
}

/* ---- Label row --------------------------------------------------------- */
.range-label {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
    color: var(--text-muted);
    font-size: var(--text-base);
}

/* The small "?" affordance generor puts beside a slider label. */
.range-hint,
.temperature-hint {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    background: rgba(var(--accent-blue-rgb), 0.15);
    border-radius: var(--radius-circle);
    color: var(--accent-blue);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    cursor: help;
    transition: var(--transition);
}

.range-hint:hover,
.temperature-hint:hover {
    background: rgba(var(--accent-blue-rgb), 0.3);
    color: var(--text-link-2);
}
