/* Tokens (base/root.css) are linked once site-wide — see fragments/header.html. */

/* ============================================================================
   FIELD SYSTEM — the single source of truth for how a text field looks.
   ----------------------------------------------------------------------------
   Generalised from the character-create popup (component/popup-character-create.css),
   which was the reference implementation: 1rem padding, the panel border, a 10%
   white fill, and a gold focus ring. That exact look was hand-copied into ~30
   stylesheets (`.form-group input`, `.section input`, `.problem-report-field
   textarea`, …) with small accidental differences — 2px vs 1px borders, `#fff`
   vs `var(--color-text)`, a hardcoded `rgba(255,215,0,.3)` glow vs
   `var(--shadow-gold)`. Those duplicates have been removed; this file is what
   they all resolved to.

   SPECIFICITY IS DELIBERATE. The base selectors are wrapped in `:where()`, so
   they weigh 0-0-1 — a bare element selector. Any existing class-based rule
   (`.search-input`, `.tag-input`, `.filter-panel input`, `.modal--compact
   input`) still wins without needing `!important` or a scope bump. This file
   sets the floor, not the ceiling: it styles controls nobody had styled, and
   gets out of the way wherever a component has an opinion.

   Because it is element-level it must NOT touch controls that are not text
   fields — checkboxes, radios, file pickers, sliders and buttons all carry
   their own native or component styling and would be destroyed by a 1rem
   padding and a full-width box. Hence the type exclusion list.

   Imported by base/common.css and base/popup-common.css, so it reaches every
   page and every popup.
   ========================================================================== */

/* The set of controls that ARE text fields. Kept as one list so the exclusions
   only have to be stated once. */
input:where(:not(
    [type="radio"], [type="checkbox"], [type="file"], [type="range"],
    [type="color"], [type="submit"], [type="button"], [type="reset"],
    [type="image"], [type="hidden"]
)),
select,
textarea {
    width: 100%;
    padding: 1rem;
    border: var(--glass-border);
    background: var(--overlay-sm);
    color: var(--color-text);
    border-radius: var(--radius-md);
    font-size: 1rem;
    font-family: inherit;
    transition: var(--transition);
}

input:where(:not(
    [type="radio"], [type="checkbox"], [type="file"], [type="range"],
    [type="color"], [type="submit"], [type="button"], [type="reset"],
    [type="image"], [type="hidden"]
)):focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--color-gold);
    box-shadow: var(--shadow-gold);
    background: var(--overlay-md);
}

input::placeholder,
textarea::placeholder { color: var(--color-text-subtle); }

textarea {
    min-height: 100px;
    resize: vertical;
}

/* Native date/time pickers render their calendar glyph in black on our dark
   fill, which makes it invisible. */
input:where([type="date"], [type="datetime-local"], [type="time"], [type="month"])::-webkit-calendar-picker-indicator {
    filter: invert(1);
    cursor: pointer;
}

/* ── Range slider ─────────────────────────────────────────────────────
   Sliders are excluded from the text-field block above (a 1rem padding would
   destroy them), which left them completely unstyled: a native ~130px control
   sitting in a full-width form. Three stylesheets (create/commission.css,
   create/commission-req.css, …) each hand-copied the same 8px track + 24px
   thumb to fix it locally, so a slider was full width on the commission forms
   and a small native widget on the build form / progress form.

   This is that rule, once, for every page and popup. Wrapped in `:where()`
   like the rest of the file so any component with an opinion still wins.  */

input:where([type="range"]) {
    -webkit-appearance: none;
    appearance: none;
    display: block;
    width: 100%;
    height: 8px;
    padding: 0;
    background: var(--overlay-lg);
    border: none;
    border-radius: 5px;
    outline: none;
    cursor: pointer;
}

input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 24px;
    height: 24px;
    background: var(--brand-3);
    border: none;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: 0 2px 10px var(--primary-border); /* themed: follows --brand-1 */
}

input[type="range"]::-moz-range-thumb {
    width: 24px;
    height: 24px;
    background: var(--brand-3);
    border: none;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: 0 2px 10px var(--primary-border);
}

input[type="range"]:focus-visible {
    box-shadow: var(--shadow-gold);
}

/* A disabled field should read as "not yours to edit", not as broken. */
input:disabled,
select:disabled,
textarea:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* ============================================================================
   REQUIRED MARKER — the `*` that follows a required field's label.
   ----------------------------------------------------------------------------
   Markup contract:  <label>Field name<span class="required">*</span></label>

   Generalised from the commission offer form (create/of-commission.css), which
   was the reference implementation: gold, set off from the label text by a
   0.3rem left padding so the star never touches the last letter.

   Every form used to redeclare this and they had all drifted apart — a
   hardcoded #ffd700 on the character/event/group/commission forms, red
   (--color-danger-text) on the commission request form and in every popup, a
   ::after star in a fourth colour on the group-join popup, and no gap at all
   anywhere but the offer form. Those copies are gone; this is what they now
   resolve to. The gap comes from the padding alone, so labels must NOT put a
   space before the span.
   ========================================================================== */
.required {
    color: var(--color-gold);
    padding-left: 0.3rem;
}

/* ============================================================================
   CHARACTER COUNTER
   ----------------------------------------------------------------------------
   Generalised from the "Suggest a new idea" page, which was the only place on
   the site that told you how much room was left. js/component/char-counter.js
   now appends one of these to every text field that carries a `maxlength`, so
   the markup below is generated — templates never write it by hand.

   `.suggest-idea-counter` is kept as an alias because that page's markup
   predates the shared component.
   ========================================================================== */

.char-counter,
.suggest-idea-counter {
    display: block;
    margin-top: 0.35rem;
    font-size: 0.85rem;
    line-height: 1.2;
    color: var(--color-text-muted);
    text-align: right;
    /* Fields inside a flex column (the report/suggest panels) would otherwise
       stretch the counter to full width and lose the right alignment. */
    align-self: flex-end;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
    user-select: none;
    /* The counter is injected as the field's next sibling, and some fields sit
       directly in a flex/grid row rather than in a `.form-group` (the timeline
       "step name + duration" pair, the tier feature rows). Without these the
       counter would be laid out as another COLUMN of that row and squash the
       inputs; with them it takes a full line of its own.

       `order` sorts the counter behind every real control on the row, so its
       position in the DOM stops mattering: sitting between the ticket name and
       its price box, it would otherwise wrap the price box and the ✕ button
       down onto the counter's line with it.

       All three are inert in normal block flow, so they cost nothing on the
       fields that sit in an ordinary `.form-group`. */
    flex-basis: 100%;
    grid-column: 1 / -1;
    order: 99;
}

/* Set by char-counter.js on a single-line flex row it has put a counter in —
   the row has to allow wrapping for `flex-basis: 100%` above to reach a line of
   its own, and only the row itself can carry that. The row's own `gap` already
   separates the two lines, so the counter drops its margin here. */
.has-char-counter > .char-counter { margin-top: 0; }

/* Approaching the limit — a warning, not an error. */
.char-counter.is-near { color: var(--color-gold-text); }

/* Over the limit. `maxlength` normally makes this unreachable, but a value can
   still arrive over-length from a draft restore or a server round-trip. */
.char-counter.is-over,
.suggest-idea-counter.is-over { color: var(--color-danger-text); }

/* The counter sits under the control, so a field that lives in a grid/flex row
   must not have it counted as a second column. */
.form-group > .char-counter { margin-bottom: 0; }

/* When a field carries help text as well, the counter goes above it and the
   two share a line's worth of space rather than stacking two full margins. */
.char-counter + .help-text { margin-top: 0.25rem; }

@media (max-width: 480px) {
    .char-counter,
    .suggest-idea-counter { font-size: 0.8rem; }
}
