/* ------------------------------------------------------------------ *
 * File Convert — custom-element UI controls
 *
 * Skins for <fc-toggle>, <fc-select>, <fc-quality> and <fc-progress>.
 *
 * Everything here is keyed off classes that ui-controls.js adds at upgrade
 * time (.is-ready, .fc-select__native, .fc-visually-hidden, …). Nothing hides
 * or repositions a native control on the strength of the custom tag alone, so
 * with JavaScript off the browser's own checkbox and select render untouched
 * and the form still submits.
 *
 * Colours come from theme.css tokens only — no literal surface or text hex —
 * because dark mode is a real runtime state (data-theme on <html>, with a
 * prefers-color-scheme fallback) and a literal silently breaks one of them.
 * ------------------------------------------------------------------ */

:root {
    /* Local aliases so a future retheme is one edit, not thirty. All of these
       resolve through theme.css tokens that already flip for dark mode. */
    --fc-ctl-accent: var(--primary-500);
    --fc-ctl-accent-hover: var(--primary-600);
    --fc-ctl-tint: var(--brand-tint);
    --fc-ctl-ring: var(--brand-ring);

    /* The thumb of a switch in its OFF position sits on --t-accented. In light
       mode the surface colour reads as a bright cap; in dark it would be
       near-black on dark grey, so it is overridden below. */
    --fc-toggle-thumb: var(--t-surface);
    /* ON, the thumb sits on brand red in both themes, so it stays constant.
       --primary-100 is theme-invariant and near-white. */
    --fc-toggle-thumb-on: var(--primary-100);
    /* A white thumb on the OFF track is 1.17:1 in light mode — the position of
       the thumb IS the state, so SC 1.4.11 wants 3:1 and it was invisible.
       A hairline in --t-muted puts it at 4.05:1 against the track without
       changing the thumb's size (box-shadow, not border). */
    --fc-toggle-thumb-edge: var(--t-muted);

    --fc-ctl-height: 2.5rem;
}

:root[data-theme="dark"] {
    --fc-toggle-thumb: var(--t-highlighted);
    /* Dark already gets 12.8:1 thumb-on-track, so the hairline is redundant;
       collapsing it into the thumb colour keeps the cap clean. */
    --fc-toggle-thumb-edge: var(--t-highlighted);
}

/* Written twice on purpose: data-theme is only present once the user has
   chosen, so an OS-dark visitor who never touched the toggle needs the media
   query half. Unpaired overrides have shipped broken here before. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --fc-toggle-thumb: var(--t-highlighted);
        --fc-toggle-thumb-edge: var(--t-highlighted);
    }
}

/* Same recipe as form.css's .visually-hidden, re-declared under our own name
   so this file never depends on another stylesheet's class surviving a
   refactor. Applied to the native <select>, which must stay submittable — so
   never display:none — while the listbox stands in for it visually. */
.fc-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ------------------------------------------------------------------ *
 * 1. fc-toggle
 * ------------------------------------------------------------------ */

fc-toggle {
    display: inline-block;
    position: relative;
    vertical-align: middle;
    line-height: 0;
}

/* Until JS has run the checkbox is a plain checkbox and is left alone. */
fc-toggle.is-ready {
    width: 2.75rem;
    height: 1.5rem;
}

/* The real input is stretched invisibly over the track: it keeps the click,
   the Space key, the <label for> association, :disabled and the native change
   event, and we never have to reimplement any of them. */
fc-toggle.is-ready .fc-toggle__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    opacity: 0;
    cursor: pointer;
    z-index: 2;
}

fc-toggle.is-ready .fc-toggle__input:disabled {
    cursor: not-allowed;
}

.fc-toggle__track {
    position: absolute;
    inset: 0;
    display: block;
    /* --t-border-strong is only 1.50:1 on the page surface, and an OFF track is
       --t-accented on --t-surface (1.17:1), so the border is the only thing
       that says "there is a switch here". --t-muted gets it to 4.74:1. */
    border: 1px solid var(--t-muted);
    border-radius: 999px;
    background: var(--t-accented);
    /* 0.15s ease is the site's transition speed (form.css, chrome.css and the
       pricing switch all use it); this file had invented 160ms. */
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.fc-toggle__thumb {
    position: absolute;
    top: 50%;
    left: 3px;
    width: 1.05rem;
    height: 1.05rem;
    border-radius: 50%;
    background: var(--fc-toggle-thumb);
    box-shadow: 0 0 0 1px var(--fc-toggle-thumb-edge), var(--shadow-sm);
    transform: translateY(-50%);
    transition: transform 0.15s ease, background-color 0.15s ease;
}

/* Same on/off palette as .pricing-switch: --primary-500 fill with a
   --primary-400 rim, so the two switches on the site read as one control. */
fc-toggle.is-checked .fc-toggle__track {
    background: var(--fc-ctl-accent);
    border-color: var(--primary-400);
}

fc-toggle.is-checked .fc-toggle__thumb {
    background: var(--fc-toggle-thumb-on);
    /* ON the thumb is 3.62:1 on the red track unaided, so the hairline is
       dropped rather than left as a grey rim on brand red. */
    box-shadow: var(--shadow-sm);
    transform: translate(1.2rem, -50%);
}

/* Disabled is exempt from the contrast minimums, but it still has to read as
   unavailable: dimmed *and* not-allowed, not one or the other. */
fc-toggle.is-disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* Focus lives on the invisible input, so the ring is drawn on the track. */
fc-toggle.is-ready .fc-toggle__input:focus-visible + .fc-toggle__track,
fc-toggle.is-ready .fc-toggle__input:focus-visible ~ .fc-toggle__track {
    outline: 2px solid var(--fc-ctl-accent);
    outline-offset: 2px;
}

fc-toggle.is-ready:hover:not(.is-disabled) .fc-toggle__track {
    border-color: var(--fc-ctl-accent);
}

/* ------------------------------------------------------------------ *
 * 2. fc-select
 * ------------------------------------------------------------------ */

fc-select {
    display: block;
    position: relative;
    /* A grid/flex item defaults to min-width:auto, which a long format name
       would push past — that is a horizontal page scrollbar at 360px. */
    min-width: 0;
}

fc-select.is-ready {
    /* The popup is absolutely positioned against this box, and is never wider
       than it — that is what keeps a long format list from pushing the page
       sideways at 360px. */
    isolation: isolate;
}

.fc-select__button {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    min-height: var(--fc-ctl-height);
    padding: 0.45rem 0.75rem;
    border: 1px solid var(--t-border-strong);
    border-radius: var(--radius);
    background: var(--t-surface);
    color: var(--t-default);
    font: inherit;
    text-align: left;
    cursor: pointer;
    /* Matches .btn / .text in form.css: same 1px --t-border-strong box, same
       --radius, same 0.15s ease, so an fc-select sits in a form beside a native
       .select without looking like a different widget kit. */
    transition: border-color 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
}

.fc-select__button:hover:not(:disabled) {
    border-color: var(--fc-ctl-accent);
}

.fc-select__button:focus-visible {
    outline: 2px solid var(--fc-ctl-accent);
    outline-offset: 2px;
}

/* Open state reuses form.css's own :focus treatment for .select/.text — brand
   border plus a 3px --brand-ring halo — rather than inventing a second one. */
fc-select.is-open .fc-select__button {
    border-color: var(--fc-ctl-accent);
    box-shadow: 0 0 0 3px var(--fc-ctl-ring);
}

/* opacity: 0.55 put the label at 4.06:1 and the placeholder below 3:1. Explicit
   tokens instead: --t-dimmed on --t-elevated is 4.68:1 light / 4.99:1 dark and
   still reads unmistakably as "off". */
.fc-select__button:disabled {
    cursor: not-allowed;
    background: var(--t-elevated);
    border-color: var(--t-border);
    color: var(--t-dimmed);
}

/* The value and arrow set their own colours, so they have to be told too. */
.fc-select__button:disabled .fc-select__value,
.fc-select__button:disabled .fc-select__value.is-placeholder {
    color: var(--t-dimmed);
}

.fc-select__button:disabled .fc-select__arrow {
    border-right-color: var(--t-dimmed);
    border-bottom-color: var(--t-dimmed);
}

.fc-select__value {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--t-highlighted);
}

.fc-select__value.is-placeholder {
    color: var(--t-muted);
}

/* Drawn rather than shipped as an icon font, so the control has no dependency
   on Font Awesome having loaded. */
.fc-select__arrow {
    flex: 0 0 auto;
    width: 0.5rem;
    height: 0.5rem;
    margin-left: auto;
    border-right: 2px solid var(--t-muted);
    border-bottom: 2px solid var(--t-muted);
    transform: translateY(-2px) rotate(45deg);
    transition: transform 0.15s ease, border-color 0.15s ease;
}

fc-select.is-open .fc-select__arrow {
    transform: translateY(1px) rotate(-135deg);
}

/* Deliberately the same sheet as form.css's .source-menu and the header .mega:
   --t-surface (not --t-elevated), a 1px --t-border edge, --radius-lg and
   --shadow-menu, hanging 8px off the trigger. Two consequences beyond looking
   right: options sit on the page surface, where --t-muted group labels are
   4.74:1 instead of 4.35:1 on --t-elevated; and the popup carries the same
   elevation cue the rest of the site uses for "this floats". */
.fc-select__popup {
    position: absolute;
    z-index: 60;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    /* Never wider than the trigger: no horizontal page scroll on a phone. */
    max-width: 100%;
    border: 1px solid var(--t-border);
    border-radius: var(--radius-lg);
    background: var(--t-surface);
    box-shadow: var(--shadow-menu);
    overflow: hidden;
}

/* Flipped by JS when there is not enough room below the trigger. */
fc-select.is-above .fc-select__popup {
    top: auto;
    bottom: calc(100% + 8px);
}

.fc-select__popup[hidden] {
    display: none;
}

.fc-select__list {
    max-height: 15rem;
    /* Long option lists scroll inside the popup, not the page. */
    overflow-y: auto;
    overscroll-behavior: contain;
    margin: 0;
    /* 6px inset, same as .source-menu's tray padding. */
    padding: 6px;
    list-style: none;
}

.fc-select__list:focus {
    /* DOM focus is on the list while the popup is open, but the visible focus
       is already carried twice — the trigger keeps the .is-open brand border
       plus --brand-ring halo, and the active option keeps its inset ring — so a
       third box around the whole tray would only add noise. */
    outline: none;
}

/* Row geometry, radius and 0.15s hover copied from .source-menu__item.
   One deliberate departure: .source-menu__item turns its label brand red on
   hover, which is 3.99:1 on --t-elevated — under 4.5:1 for body text. A listbox
   row is long-form text, so the hover darkens the ink to --t-highlighted
   instead and the fill carries the colour change. */
.fc-select__option {
    display: block;
    padding: 10px 12px;
    border-radius: var(--radius);
    color: var(--t-default);
    cursor: pointer;
    white-space: normal;
    overflow-wrap: anywhere;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.fc-select__option:hover:not(.is-disabled) {
    background: var(--t-accented);
    color: var(--t-highlighted);
}

/* The active-descendant marker: this is the keyboard user's "cursor", so it
   needs a border as well as a fill for anyone who cannot separate the two. */
.fc-select__option.is-active {
    background: var(--fc-ctl-tint);
    box-shadow: inset 0 0 0 2px var(--fc-ctl-accent);
    color: var(--t-highlighted);
}

.fc-select__option[aria-selected="true"] {
    font-weight: 600;
    color: var(--t-highlighted);
}

.fc-select__option[aria-selected="true"]::after {
    content: "✓";
    float: right;
    margin-left: 0.5rem;
    color: var(--fc-ctl-accent);
}

/* opacity: 0.7 dragged --t-dimmed down to an effective #969696 — 2.71:1, below
   even the value --t-dimmed was raised from. The token already encodes "quiet
   but legible" (5.10:1 light / 5.91:1 dark here), so nothing is stacked on it. */
.fc-select__option.is-disabled {
    color: var(--t-dimmed);
    cursor: not-allowed;
}

.fc-select__group {
    display: block;
    padding: 0;
    list-style: none;
}

.fc-select__group > ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

.fc-select__grouplabel {
    display: block;
    padding: 0.5rem 0.7rem 0.25rem;
    color: var(--t-muted);
    font-size: 0.8125rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* ------------------------------------------------------------------ *
 * 3. fc-quality
 * ------------------------------------------------------------------ */

fc-quality {
    display: block;
}

fc-quality.is-ready {
    display: grid;
    gap: 0.5rem;
}

.fc-quality__picker {
    min-width: 0;
}

/* The bound number input is only revealed for the Custom preset; JS toggles
   `hidden`, and this simply gives it a matching box when it is on screen. */
.fc-quality__input {
    width: 100%;
    min-height: var(--fc-ctl-height);
    padding: 0.45rem 0.75rem;
    border: 1px solid var(--t-border-strong);
    border-radius: var(--radius);
    background: var(--t-surface);
    color: var(--t-highlighted);
    font: inherit;
}

.fc-quality__input:focus-visible {
    outline: 2px solid var(--fc-ctl-accent);
    outline-offset: 2px;
}

/* Same disabled treatment as the select trigger, for the same reason. */
.fc-quality__input:disabled {
    cursor: not-allowed;
    background: var(--t-elevated);
    border-color: var(--t-border);
    color: var(--t-dimmed);
}

/* `hidden` is easily beaten by a display rule from another stylesheet, and a
   quality box that will not hide is a confusing bug rather than a cosmetic
   one, so it is asserted here. */
.fc-quality__input[hidden] {
    display: none;
}

/* ------------------------------------------------------------------ *
 * 4. fc-progress
 * ------------------------------------------------------------------ */

fc-progress {
    display: block;
}

/* form.css's .progress__bar exactly: 8px tall, --t-border trough, brand fill,
   0.2s width. The trough was --t-accented here, which is a different grey from
   the progress bar the conversion form already shows on the same page. */
.fc-progress__bar {
    position: relative;
    height: 8px;
    border-radius: 999px;
    background: var(--t-border);
    overflow: hidden;
}

.fc-progress__fill {
    display: block;
    width: 0;
    height: 100%;
    border-radius: inherit;
    background: var(--fc-ctl-accent);
    transition: width 0.2s ease;
}

.fc-progress__readout {
    margin-top: 9px;
    color: var(--t-muted);
    font-size: 0.875rem;
}

/* Unknown-length work: a sliver that sweeps, with no aria-valuenow (JS removes
   it) so assistive tech reports "busy" rather than a fabricated percentage. */
fc-progress.is-indeterminate .fc-progress__fill {
    width: 40%;
    transition: none;
    animation: fc-progress-sweep 1.2s ease-in-out infinite;
}

@keyframes fc-progress-sweep {
    0% {
        transform: translateX(-100%);
    }
    100% {
        transform: translateX(250%);
    }
}

/* ------------------------------------------------------------------ *
 * Motion and viewport
 * ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    /* Every transition declared in this file is listed here. If you add a
       `transition` above, add its selector here too. */
    .fc-toggle__track,
    .fc-toggle__thumb,
    .fc-select__button,
    .fc-select__arrow,
    .fc-select__option,
    .fc-progress__fill {
        transition: none;
    }

    /* The sweep is dropped rather than slowed. It is replaced with a static
       barber-pole rather than a flat 100% bar, because a full brand-red bar is
       exactly what "finished" looks like. Both stripe colours are tokens, so
       the fill keeps its 3.57:1 (light) / 3.12:1 (dark) against the trough. */
    fc-progress.is-indeterminate .fc-progress__fill {
        animation: none;
        width: 100%;
        background-image: repeating-linear-gradient(
            135deg,
            var(--fc-ctl-accent) 0,
            var(--fc-ctl-accent) 6px,
            var(--fc-ctl-accent-hover) 6px,
            var(--fc-ctl-accent-hover) 12px
        );
    }
}

@media (max-width: 480px) {
    .fc-select__list {
        max-height: 12rem;
    }

    .fc-select__option {
        /* Comfortable target size on a phone. */
        padding: 13px 12px;
    }
}

@media (max-width: 360px) {
    .fc-select__button {
        padding: 0.45rem 0.55rem;
    }

    /* The popup already tracks the trigger's width (left/right: 0 plus
       max-width: 100%), so the only thing that can widen the page here is an
       unbreakable option label; --t-* geometry stays put. */
    .fc-select__option {
        padding: 13px 10px;
    }
}

/* Windows high-contrast: system colours replace ours, and the ones that are
   drawn purely with background need an explicit border to survive. */
@media (forced-colors: active) {
    .fc-toggle__track,
    .fc-progress__bar,
    .fc-select__popup {
        border: 1px solid CanvasText;
    }

    fc-toggle.is-checked .fc-toggle__thumb {
        background: Highlight;
    }

    .fc-select__option.is-active {
        outline: 2px solid Highlight;
    }
}
