/**
 * Shared UI kit — surface tokens, filter-select shells, and the card tile.
 *
 * Extracted from the 2026-08-26 browse redesign when the card detail page
 * needed the same tiles and the same form controls. Load this BEFORE any page
 * stylesheet that uses it (see $page_styles in app/controllers/cards.php).
 *
 * Nothing here is page-scoped. The tile rules key off `.tile-grid` on the grid
 * container, so a page opts in by adding that class to its `.card-grid`.
 *
 * Theme handling follows the project convention: dark values on :root, and
 * html[data-theme="light"] re-points the same tokens. Accent tokens are
 * inherited from design.css, never redefined here.
 */

:root,
html[data-theme="dark"] {
    --surf-surface:        #14141d;
    --surf-surface-2:      #1b1b26;
    --surf-input:          rgba(255, 255, 255, 0.045);
    --surf-border:         rgba(255, 255, 255, 0.08);
    --surf-border-strong:  rgba(255, 255, 255, 0.14);
    --surf-accent-ink:     var(--accent-bright);
    --surf-accent-border:  rgba(139, 124, 255, 0.55);
    --surf-accent-tint:    rgba(139, 124, 255, 0.13);
    --surf-accent-ring:    rgba(139, 124, 255, 0.22);
    --surf-img-bg:         #ffffff;
    --surf-img-shadow:     none;
    --surf-shadow:         0 1px 2px rgba(0, 0, 0, 0.5), 0 10px 26px -14px rgba(0, 0, 0, 0.85);
    --surf-shadow-hover:   0 26px 52px -20px rgba(0, 0, 0, 0.9), 0 0 34px -12px rgba(139, 124, 255, 0.4);
    --surf-shadow-panel:   0 2px 4px rgba(0, 0, 0, 0.35), 0 18px 46px -22px rgba(0, 0, 0, 0.9);
    --surf-head-wash:      radial-gradient(ellipse 90% 120% at 12% 0%, rgba(124, 106, 255, 0.20), transparent 62%);
}

html[data-theme="light"] {
    --surf-surface:        #ffffff;
    --surf-surface-2:      #ffffff;
    --surf-input:          #f5f5fb;
    --surf-border:         #e5e5ef;
    --surf-border-strong:  #d8d8e6;
    --surf-accent-ink:     var(--accent);
    --surf-accent-border:  rgba(106, 88, 232, 0.55);
    --surf-accent-tint:    rgba(106, 88, 232, 0.08);
    --surf-accent-ring:    rgba(106, 88, 232, 0.16);
    --surf-img-bg:         #ffffff;
    --surf-img-shadow:     none;
    /* Light theme needs layered, tighter shadows to read as elevation at all —
       a single soft shadow on a near-white page just looks like a smudge. */
    --surf-shadow:         0 1px 1px rgba(20, 20, 45, 0.05), 0 2px 4px rgba(20, 20, 45, 0.05), 0 10px 22px -12px rgba(20, 20, 45, 0.22);
    --surf-shadow-hover:   0 2px 4px rgba(20, 20, 45, 0.06), 0 20px 40px -16px rgba(20, 20, 45, 0.28), 0 0 0 1px rgba(106, 88, 232, 0.18);
    --surf-shadow-panel:   0 1px 2px rgba(20, 20, 45, 0.05), 0 14px 34px -18px rgba(20, 20, 45, 0.28);
    --surf-head-wash:      radial-gradient(ellipse 90% 120% at 12% 0%, rgba(106, 88, 232, 0.13), transparent 62%);
}

/* ============================================================
   Labelled filter/form select shells
   A bare select reads as a text box: "All sets" gives no clue it is the set
   filter, and "Near Mint" none that it is the condition. Each control carries
   its own caption inside the shell and turns accent-coloured when it holds a
   non-default value.
   ============================================================ */
.fselect,
.fprice,
.ffield {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
    flex: 1 1 148px;
    min-width: 0;
    max-width: 210px;
    min-height: 52px;
    padding: 7px 32px 8px 13px;
    background: var(--surf-input);
    border: 1px solid var(--surf-border);
    border-radius: 13px;
    cursor: pointer;
    transition: border-color 0.18s, background 0.18s, box-shadow 0.18s;
}
.fprice {
    cursor: default;
    flex: 1 1 190px;
    max-width: 240px;
    padding-right: 13px;
}
/* Same shell around a free input rather than a select: no chevron, no reserved
   room for one, and a caret instead of the pointer. */
.ffield {
    cursor: text;
    flex: 1 1 auto;
    max-width: none;
    padding-right: 13px;
}
.ffield:hover { border-color: var(--surf-border-strong); }
.ffield:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--surf-accent-ring);
}
input.fselect__input {
    cursor: text;
    /* Firefox/WebKit spinners crowd a shell this tight; the caption already
       says what the field is and the arrows add nothing at this size. */
    -moz-appearance: textfield;
}
input.fselect__input::-webkit-outer-spin-button,
input.fselect__input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input.fselect__input::placeholder { color: var(--text-muted); font-weight: 500; }
.fselect--set  { flex: 2 1 200px; max-width: 300px; }
.fselect--sort { flex: 1 1 168px; max-width: 210px; }

.fselect::after {
    content: '';
    position: absolute;
    right: 14px;
    top: calc(50% - 3px);
    width: 7px;
    height: 7px;
    border-right: 1.8px solid var(--text-muted);
    border-bottom: 1.8px solid var(--text-muted);
    transform: translateY(-50%) rotate(45deg);
    pointer-events: none;
    transition: border-color 0.18s;
}
.fselect__label {
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    line-height: 1.35;
    color: var(--text-muted);
    pointer-events: none;
    transition: color 0.18s;
}
.fselect__input {
    appearance: none;
    -webkit-appearance: none;
    width: 100%;
    min-width: 0;
    margin: 0;
    padding: 0;
    background: transparent;
    border: 0;
    outline: none;
    font: inherit;
    font-size: 0.87rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--text-primary);
    cursor: pointer;
    text-overflow: ellipsis;
}
/* The shell is transparent, so the native dropdown list needs its own colours
   or dark-theme options render as light-on-light. */
.fselect__input option,
.fselect__input optgroup {
    background: var(--surf-surface-2);
    color: var(--text-primary);
}
.fselect:hover { border-color: var(--surf-border-strong); }
.fselect:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--surf-accent-ring);
}
.fselect.is-active {
    background: var(--surf-accent-tint);
    border-color: var(--surf-accent-border);
}
.fselect.is-active .fselect__label { color: var(--surf-accent-ink); }
.fselect.is-active::after {
    border-right-color: var(--surf-accent-ink);
    border-bottom-color: var(--surf-accent-ink);
}

/* —— Price range —— */
.fprice__inputs {
    display: flex;
    align-items: center;
    gap: 6px;
}
.tile-grid .input-sm--price {
    flex: 1;
    width: 100%;
    min-width: 0;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
    color: var(--text-primary);
    font-size: 0.87rem;
    font-weight: 600;
    line-height: 1.35;
}
.tile-grid .input-sm--price:focus { outline: none; }
.tile-grid .input-sm--price::placeholder { color: var(--text-muted); font-weight: 500; }
.fprice__dash { color: var(--text-muted); font-size: 0.8rem; }
.fprice:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--surf-accent-ring);
}


/* ============================================================
   Search field — text input with a leading icon
   ============================================================ */
.search-field {
    position: relative;
    flex: 1;
    min-width: 0;
}
.search-field__icon {
    position: absolute;
    left: 16px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    font-size: 0.88rem;
    pointer-events: none;
}
.search-field .search-input {
    width: 100%;
    padding: 14px 16px 14px 42px;
    background: var(--surf-input);
    border: 1px solid var(--surf-border);
    border-radius: 13px;
    color: var(--text-primary);
    font-size: 0.95rem;
    font-weight: 500;
    transition: border-color 0.18s, box-shadow 0.18s, background 0.18s;
}
.search-field .search-input::placeholder { color: var(--text-muted); }
.search-field .search-input:focus {
    outline: none;
    background: var(--surf-surface);
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--surf-accent-ring);
}

/* ============================================================
   Card tile
   Anatomy: artwork -> language + rarity -> name -> number + price
   -> action footer. Price sits in the info block rather than the
   footer bar, so the footer is a single uncrowded row instead of
   two cramped ones.
   ============================================================ */
/* ============================================================
   4. Card grid & tiles
      Anatomy: artwork → language + rarity → name → number + price
      → action footer. Price left the footer bar so the footer is a
      single uncrowded row instead of two cramped ones.
   ============================================================ */
.card-grid.tile-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(clamp(156px, 15vw, 196px), 100%), 1fr));
    gap: clamp(12px, 1.2vw, 20px);
    margin-top: 0;
}
.tile-grid .card-item {
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--surf-surface);
    border: 1px solid var(--surf-border);
    border-radius: 16px;
    box-shadow: var(--surf-shadow);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    transition:
        transform 0.28s var(--ease-out),
        box-shadow 0.28s var(--ease-out),
        border-color 0.2s;
}
.tile-grid .card-item:hover {
    transform: translateY(-4px);
    border-color: var(--surf-accent-border);
    box-shadow: var(--surf-shadow-hover);
}
.tile-grid .card-item__link {
    flex: 1;
    display: flex;
    flex-direction: column;
    border-radius: 0;
    color: inherit;
    text-decoration: none;
}

/* Artwork plate — thumbnails stay clean (no overlays, badges or text). */
.tile-grid .card-img {
    aspect-ratio: 63 / 88;
    padding: 9px 9px 5px;
    background: var(--surf-img-bg);
    border-bottom: 1px solid var(--surf-border);
}
.tile-grid .card-img img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    filter: var(--surf-img-shadow);
}

/* —— Info block —— */
.tile-grid .card-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 11px 12px 10px;
    border-top: 0;
}
.tile-grid .card-info__meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 19px;
    margin-bottom: 0;
}
.tile-grid .card-info__meta .badge {
    font-size: 0.58rem;
    font-weight: 800;
    letter-spacing: 0.08em;
    padding: 3px 8px;
    border-radius: 6px;
}
.tile-grid .card-name {
    margin: 0;
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1.32;
    letter-spacing: -0.012em;
    color: var(--text-primary);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: 2.32em;
}
.tile-grid .card-print {
    color: var(--text-muted);
    font-weight: 600;
}
.tile-grid .card-info__foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-top: auto;
    padding-top: 9px;
}
.tile-grid .card-num {
    margin: 0;
    font-size: 0.64rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Accent-coloured in BOTH themes. theme-light.css already paints .card-price
   with the accent at (0,2,1), so the dark rule has to match that weight or the
   two themes disagree on the single most important number on the tile. */
html .tile-grid .card-price {
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--surf-accent-ink);
    font-variant-numeric: tabular-nums;
}
html .tile-grid .card-price--none {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--text-muted);
}

/* —— Action footer —— */
.tile-grid .cc-bar {
    margin-top: 0;
    padding: 0 10px 10px;
    border-top: 0;
    gap: 0;
}
.tile-grid .cc-bar__row { gap: 8px; }
.tile-grid .cc-stepper {
    flex: 1;
    justify-content: space-between;
    gap: 0;
    padding: 2px;
    background: var(--surf-input);
    border: 1px solid var(--surf-border);
    border-radius: 11px;
    transition: border-color 0.18s, background 0.18s;
}
.tile-grid .cc-step {
    flex: 0 0 30px;
    height: 30px;
    border-radius: 9px;
    color: var(--text-secondary);
    font-size: 1.1rem;
}
.tile-grid .cc-step:hover:not(:disabled) {
    background: var(--accent);
    color: #fff;
}
.tile-grid .cc-qty {
    flex: 1;
    min-width: 0;
    text-align: center;
    font-size: 0.92rem;
    font-weight: 800;
    color: var(--text-primary);
}
.tile-grid .cc-options {
    width: 34px;
    height: 34px;
    flex: 0 0 34px;
    border-radius: 11px;
    background: var(--surf-input);
    border: 1px solid var(--surf-border);
    color: var(--text-muted);
    font-size: 0.85rem;
    transition: border-color 0.18s, background 0.18s, color 0.18s, transform 0.18s;
}
.tile-grid .cc-options:hover {
    background: var(--surf-accent-tint);
    border-color: var(--surf-accent-border);
    color: var(--surf-accent-ink);
    transform: none;
}

/* Owned state — the tile reads as "in your collection" at a glance. */
.tile-grid .card-item.is-owned {
    border-color: var(--surf-accent-border);
}
.tile-grid .card-item.is-owned .cc-stepper {
    background: var(--surf-accent-tint);
    border-color: var(--surf-accent-border);
}
.tile-grid .card-item.is-owned .cc-qty { color: var(--surf-accent-ink); }

.tile-grid[aria-busy="true"] { opacity: 0.45; }

/* ---------- Tile responsive ----------
   Phone tiles measured 398px tall on a 380px viewport — 1.8 rows per screen,
   with ~40px of that being dead air: .card-name reserved two lines whether or
   not it used them, and .card-info__foot's `margin-top: auto` pushed the price
   away from the name to fill the slack.

   The fix re-lays the info block as a 2-column grid instead of a stack, so the
   card number rides on the free right-hand end of the language/rarity row and
   stops costing a row of its own. `display: contents` on .card-info__foot lifts
   .card-num and .card-price out of their wrapper and into that grid — the
   wrapper only exists to make the desktop baseline row, which phones do not use.

   Grid rows 1–2 (meta, name) are min-content; everything after is 1fr, so the
   slack a short name leaves in a 2-up row collects on the price row and
   `align-self: end` keeps the two prices in a row on the same line — the one
   thing `margin-top: auto` was doing that is worth keeping. */
@media (max-width: 700.98px) {
    .card-grid.tile-grid { gap: 10px; }

    .tile-grid .card-info {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-rows: min-content min-content;
        grid-auto-rows: 1fr;
        column-gap: 8px;
        row-gap: 3px;
        padding: 8px 10px 6px;
    }
    /* Badge + rarity read as one left-hand cluster now that the number shares
       the row; space-between would fling the rarity into the number. */
    .tile-grid .card-info__meta {
        grid-column: 1;
        grid-row: 1;
        justify-content: flex-start;
        gap: 6px;
        min-height: 18px;
    }
    .tile-grid .card-name,
    .tile-grid .card-name-cn { grid-column: 1 / -1; }
    .tile-grid .card-name { min-height: 0; }

    .tile-grid .card-info__foot { display: contents; }
    .tile-grid .card-num {
        grid-column: 2;
        grid-row: 1;
        align-self: center;
        font-size: 0.6rem;
    }
    /* .card-price is set at (0,2,1) with an `html` prefix further up, so the
       phone override has to carry the same prefix or it silently loses. */
    html .tile-grid .card-price,
    html .tile-grid .card-price--none {
        grid-column: 1 / -1;
        align-self: end;
        font-size: 1rem;
        line-height: 1.1;
    }

    /* .cc-options points at the same URL as the tile link wrapping the artwork
       and the name, so on a phone it is a third tap target for a destination
       the user already has twice. Dropping it hands its 44px to the stepper,
       taking each of −/+ from 34px to ~52px. */
    .tile-grid .cc-bar { padding: 0 8px 8px; }
    .tile-grid .cc-bar__row { gap: 6px; }
    .tile-grid .cc-options { display: none; }
    .tile-grid .cc-stepper { padding: 3px; }
    .tile-grid .cc-step { flex: 0 0 46px; height: 32px; }
}

@media (pointer: coarse) {
    .tile-grid .cc-step { min-width: 34px; min-height: 34px; }
    .tile-grid .cc-options { width: 38px; height: 38px; flex-basis: 38px; }
}

@media (prefers-reduced-motion: reduce) {
    .tile-grid .card-item,
    .tile-grid .card-item:hover { transform: none; transition: none; }
}

/* ============================================================
   Rarity glyphs
   The low-rarity marks ship as monochrome #1a1a1a SVGs, which are
   effectively invisible on a dark surface. Invert only those — the
   higher rarities are full-colour PNG artwork and must be left alone,
   and .pk-rarity--overlay sits on its own white pill.
   ============================================================ */
:root:not([data-theme="light"]) .tile-grid .pk-rarity:not(.pk-rarity--overlay) .pk-rarity__icon[src$=".svg"],
:root:not([data-theme="light"]) .card-page .pk-rarity:not(.pk-rarity--overlay) .pk-rarity__icon[src$=".svg"] {
    filter: invert(1);
}

/* ============================================================
   Utilities
   ============================================================ */
/* Accessible name for a control or column whose visible label is an icon or
   is carried by the column's contents. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ============================================================
   Enhanced select — trigger + popup listbox
   The native option list is an OS widget: unstyleable, wrong font, wrong
   colours, and blind to the dark theme. js/fselect.js hides it and drives the
   panel below instead, keeping the <select> as the form value. Everything here
   is inert until that script adds .is-enhanced, so the no-JS path is untouched.
   ============================================================ */
:root,
html[data-theme="dark"] {
    --fsmenu-bg:        rgba(24, 24, 35, 0.94);
    --fsmenu-border:    rgba(255, 255, 255, 0.11);
    --fsmenu-shadow:    0 1px 2px rgba(0, 0, 0, 0.5), 0 24px 60px -18px rgba(0, 0, 0, 0.95);
    --fsmenu-hover:     rgba(255, 255, 255, 0.06);
    --fsmenu-scroll:    rgba(255, 255, 255, 0.16);
    --fsmenu-backdrop:  rgba(6, 6, 12, 0.55);
}
html[data-theme="light"] {
    --fsmenu-bg:        rgba(255, 255, 255, 0.97);
    --fsmenu-border:    #e2e2ee;
    --fsmenu-shadow:    0 1px 2px rgba(20, 20, 45, 0.06), 0 8px 18px -8px rgba(20, 20, 45, 0.14), 0 28px 56px -24px rgba(20, 20, 45, 0.3);
    --fsmenu-hover:     rgba(20, 20, 45, 0.05);
    --fsmenu-scroll:    rgba(20, 20, 45, 0.18);
    --fsmenu-backdrop:  rgba(20, 20, 45, 0.35);
}

.fselect.is-enhanced > select { display: none; }
.fselect__trigger {
    appearance: none;
    -webkit-appearance: none;
    display: block;
    width: 100%;
    min-width: 0;
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    outline: none;
    text-align: left;
    font: inherit;
    font-size: 0.87rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--text-primary);
    cursor: pointer;
}
/* polish.css rings every button; here that would draw a second ring inside the
   shell's own :focus-within ring. One indicator, on the shell. */
.fselect__trigger:focus-visible { outline: none; box-shadow: none; }
.fselect__value {
    display: block;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
/* "All sets", "All games" — a default, not a choice. Reads one step back. */
.fselect__value.is-empty { color: var(--text-secondary); font-weight: 500; }
.fselect.is-open {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--surf-accent-ring);
}
.fselect.is-open::after {
    top: calc(50% + 1px);
    transform: translateY(-50%) rotate(-135deg);
    border-right-color: var(--surf-accent-ink);
    border-bottom-color: var(--surf-accent-ink);
}
.fselect.is-enhanced::after { transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.18s, top 0.18s; }

/* —— The panel —— */
.fsmenu {
    position: fixed;
    z-index: 400;                      /* over the navbar (340), under modals (500) */
    display: flex;
    flex-direction: column;
    min-width: 180px;
    padding: 6px;
    background: var(--fsmenu-bg);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
    border: 1px solid var(--fsmenu-border);
    border-radius: 15px;
    box-shadow: var(--fsmenu-shadow);
    opacity: 0;
    transform: translateY(-6px) scale(0.97);
    transform-origin: top center;
    transition: opacity 0.16s ease, transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}
.fsmenu--up { transform: translateY(6px) scale(0.97); transform-origin: bottom center; }
.fsmenu.is-in { opacity: 1; transform: none; }
.fsmenu--instant { transition: none; }

.fsmenu__list {
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--fsmenu-scroll) transparent;
}
.fsmenu__list::-webkit-scrollbar { width: 8px; }
.fsmenu__list::-webkit-scrollbar-thumb {
    background: var(--fsmenu-scroll);
    border: 2px solid transparent;
    background-clip: content-box;
    border-radius: 8px;
}
.fsmenu__opt {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px 8px 10px;
    border-radius: 10px;
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.3;
    color: var(--text-primary);
    cursor: pointer;
    user-select: none;
}
.fsmenu__text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The tick keeps its box whether or not it is showing, so labels never shift. */
.fsmenu__tick {
    flex: none;
    width: 15px;
    height: 15px;
    background: var(--surf-accent-ink);
    opacity: 0;
    transform: scale(0.6);
    transition: opacity 0.14s, transform 0.14s cubic-bezier(0.16, 1, 0.3, 1);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.3 5.7a1 1 0 0 1 0 1.4l-9.9 9.9a1 1 0 0 1-1.4 0l-5.3-5.3a1 1 0 1 1 1.4-1.4l4.6 4.6 9.2-9.2a1 1 0 0 1 1.4 0z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.3 5.7a1 1 0 0 1 0 1.4l-9.9 9.9a1 1 0 0 1-1.4 0l-5.3-5.3a1 1 0 1 1 1.4-1.4l4.6 4.6 9.2-9.2a1 1 0 0 1 1.4 0z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.fsmenu__opt.is-active { background: var(--fsmenu-hover); }
.fsmenu__opt.is-selected { color: var(--surf-accent-ink); font-weight: 700; }
.fsmenu__opt.is-selected .fsmenu__tick { opacity: 1; transform: none; }
.fsmenu__opt.is-selected.is-active { background: var(--surf-accent-tint); }
.fsmenu__opt.is-disabled { opacity: 0.4; cursor: default; }
.fsmenu__group {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 10px 10px 5px;
    background: var(--fsmenu-bg);
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.fsmenu__group:first-child { padding-top: 4px; }
.fsmenu__empty {
    padding: 14px 10px;
    text-align: center;
    font-size: 0.83rem;
    color: var(--text-muted);
}

/* —— In-menu filter (only built for long lists, e.g. the 700-set picker) —— */
.fsmenu__search {
    position: relative;
    flex: none;
    margin-bottom: 4px;
}
.fsmenu__search::before {
    content: '';
    position: absolute;
    left: 11px;
    top: 50%;
    width: 13px;
    height: 13px;
    margin-top: -6.5px;
    background: var(--text-muted);
    pointer-events: none;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M10 2a8 8 0 1 0 4.9 14.3l5.4 5.4a1 1 0 0 0 1.4-1.4l-5.4-5.4A8 8 0 0 0 10 2zm0 2a6 6 0 1 1 0 12 6 6 0 0 1 0-12z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M10 2a8 8 0 1 0 4.9 14.3l5.4 5.4a1 1 0 0 0 1.4-1.4l-5.4-5.4A8 8 0 0 0 10 2zm0 2a6 6 0 1 1 0 12 6 6 0 0 1 0-12z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.fsmenu__search-input {
    width: 100%;
    padding: 9px 10px 9px 31px;
    background: var(--surf-input);
    border: 1px solid var(--surf-border);
    border-radius: 10px;
    outline: none;
    font: inherit;
    font-size: 0.84rem;
    font-weight: 500;
    color: var(--text-primary);
}
.fsmenu__search-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--surf-accent-ring);
}
.fsmenu__search-input::placeholder { color: var(--text-muted); }

/* —— Phone: a bottom sheet beats a 240px panel pinned to a tiny control —— */
.fsmenu__backdrop {
    position: fixed;
    inset: 0;
    z-index: 399;
    background: var(--fsmenu-backdrop);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}
.fsmenu--sheet {
    left: 0 !important;
    right: 0;
    top: auto !important;
    bottom: 0;
    width: auto !important;
    max-height: 74vh !important;
    padding: 8px 10px calc(10px + env(safe-area-inset-bottom));
    border-width: 1px 0 0;
    border-radius: 20px 20px 0 0;
    transform: translateY(100%);
    transform-origin: bottom center;
}
.fsmenu--sheet::before {
    content: '';
    flex: none;
    width: 38px;
    height: 4px;
    margin: 2px auto 8px;
    background: var(--fsmenu-scroll);
    border-radius: 4px;
}
.fsmenu--sheet .fsmenu__opt { padding: 12px; font-size: 0.94rem; }
