/*
 * Wallpaper Stream.
 *
 * The art is the interface. Everything else is thin lines, one accent, and a
 * lot of restraint — chrome sits at low opacity until you move the mouse, and
 * gets out of the way entirely after a few idle seconds.
 */

:root {
    --bg:        #05070d;
    --ink:       #e7ecf6;
    --dim:       #8b96ad;
    --faint:     #5a6478;

    --accent:    #5eead4;   /* cyan */
    --accent-2:  #a78bfa;   /* violet */
    --danger:    #fb7185;

    --glass:     rgba(10, 14, 24, .62);
    --edge:      rgba(255, 255, 255, .10);
    --edge-hi:   rgba(255, 255, 255, .20);

    --ui:        ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --mono:      ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;

    --dwell:     20s;
    --ease:      cubic-bezier(.22, 1, .36, 1);
}

* { box-sizing: border-box; }

/* The [hidden] attribute only carries a UA-stylesheet `display: none`, which
   any explicit display in this file silently outranks — .sheet, .hud and
   .boot all set one. Without this the dialogs render on page load. */
[hidden] { display: none !important; }

html, body {
    height: 100%;
    margin: 0;
}

body {
    background: var(--bg);
    color: var(--ink);
    font-family: var(--ui);
    overflow: hidden;
    -webkit-font-smoothing: antialiased;
}

/* Chrome fades out while the stream is left alone. */
body.idle .hud,
body.idle .dock,
body.idle .progress { opacity: 0; pointer-events: none; }

/*
 * Hide the pointer along with the chrome.
 *
 * The descendant selector is not redundant. `cursor` inherits, but any
 * element that declares its own wins over an inherited value — the dock
 * buttons, the settings rows and the drop zone all set `cursor: pointer`, so
 * a pointer left resting over one of them would keep showing a hand long
 * after everything else had faded. The !important is aimed at those same
 * declarations rather than at any inherited value.
 */
body.idle,
body.idle * { cursor: none !important; }

/* --------------------------------------------------------------- stage */

.stage {
    position: fixed;
    inset: 0;
    background: var(--bg);
}

.layer {
    position: absolute;
    inset: 0;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
    opacity: 0;
    transition: opacity 1.4s var(--ease);
    will-change: opacity, transform;
}

.layer.on { opacity: 1; }

/* Slow drift. Disabled wholesale by the settings toggle and by
   prefers-reduced-motion. */
body.drift .layer.on { animation: drift 44s ease-in-out infinite alternate; }

@keyframes drift {
    from { transform: scale(1.04) translate3d(-1.1%, -0.7%, 0); }
    to   { transform: scale(1.11) translate3d(1.1%,  0.8%, 0); }
}

/*
 * Scrim.
 *
 * The bottom ramp is doing real work, not decoration: the HUD sits about 11vh
 * up, and bright wallpapers (daylight key art, snow, white UI) will happily
 * render light text on light pixels. It is deliberately deep and tall enough
 * to still be carrying weight at the HUD's baseline rather than having faded
 * out by then — tuned against the brightest art, since the dark pieces look
 * fine either way.
 */
.vignette {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        radial-gradient(130% 95% at 50% 42%, transparent 42%, rgba(0,0,0,.55) 100%),
        linear-gradient(to top,
            rgba(0,0,0,.88) 0%,
            rgba(0,0,0,.72) 12%,
            rgba(0,0,0,.42) 26%,
            rgba(0,0,0,.16) 38%,
            transparent 52%),
        linear-gradient(to bottom, rgba(0,0,0,.42) 0%, transparent 26%);
}

/* One-pixel scanlines. repeating-linear-gradient rather than an image so it
   stays crisp at any DPR. */
.scan {
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity .4s;
    background: repeating-linear-gradient(
        to bottom,
        rgba(255,255,255,.030) 0px,
        rgba(255,255,255,.030) 1px,
        transparent 1px,
        transparent 3px
    );
    mix-blend-mode: overlay;
}

body.scanlines .scan { opacity: 1; }

/* ---------------------------------------------------------------- boot */

.boot {
    position: fixed;
    inset: 0;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 22px;
    background: var(--bg);
    z-index: 60;
    transition: opacity .6s var(--ease);
}

.boot.gone { opacity: 0; pointer-events: none; }

.boot p {
    margin: 0;
    font: 500 11px/1 var(--mono);
    letter-spacing: .32em;
    text-transform: uppercase;
    color: var(--faint);
}

.boot-mark {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    border: 1px solid rgba(255,255,255,.10);
    border-top-color: var(--accent);
    border-right-color: var(--accent-2);
    animation: spin 1.1s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ----------------------------------------------------------------- hud */

.hud {
    position: fixed;
    left: clamp(20px, 4vw, 56px);
    bottom: clamp(84px, 11vh, 130px);
    z-index: 20;
    display: flex;
    gap: 18px;
    max-width: min(46ch, 74vw);
    transition: opacity .5s var(--ease);
    /* No isolation/filter/opacity here, deliberately: any of them would make
       this element a *backdrop root*, and the ::before below would then blur
       an empty backdrop instead of the wallpaper — silently, with the tint
       still showing so it looks like it works. The z-index above already
       contains the z-index:-1 child without that side effect. */
}

/*
 * Text backdrop.
 *
 * The page-wide vignette handles dark art fine, but mid-tone detail — comic
 * linework, daylight key art — sits right in the same luminance band as the
 * type and no amount of gradient fixes that. This blurs the art directly
 * behind the text instead, which destroys the competing detail rather than
 * just darkening it.
 *
 * Because the blur is doing the legibility work, the tint on top can stay
 * light (peaking around .55) instead of the near-opaque plate you would need
 * from tint alone. Both the tint and the blur are feathered by the same mask,
 * so there is no rectangle edge anywhere — it dissolves into the wallpaper.
 */
.hud::before {
    content: "";
    position: absolute;
    /* Bleeds off the left of the viewport on purpose — no edge to hide on
       that side, and the top gets the most room because that is where the
       falloff has to finish before it reaches the kicker. */
    inset: -56px -110px -46px -80px;
    z-index: -1;
    pointer-events: none;

    /* Flat tint. The mask below does all the shaping, for both this and the
       blur, so the two can never disagree about where the edge is. */
    background: rgba(2, 5, 11, .52);

    backdrop-filter: blur(18px) saturate(105%) brightness(.62);
    -webkit-backdrop-filter: blur(18px) saturate(105%) brightness(.62);

    /* Two linear feathers intersected, rather than one radial: a radial big
       enough to cover a wide title is also bigger than the element, so it
       never reaches transparent before the border and leaves a visible
       rectangle. Per-axis control makes the falloff land where it should. */
    -webkit-mask-image:
        linear-gradient(to right, #000 0%, #000 50%, transparent 100%),
        linear-gradient(to bottom, transparent 0%, #000 24%, #000 86%, transparent 100%);
    mask-image:
        linear-gradient(to right, #000 0%, #000 50%, transparent 100%),
        linear-gradient(to bottom, transparent 0%, #000 24%, #000 86%, transparent 100%);

    -webkit-mask-composite: source-in;   /* legacy WebKit spelling */
    mask-composite: intersect;
}

/* Where backdrop-filter is unavailable the tint is the only thing left, so
   it has to be considerably heavier to do the same job. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .hud::before { background: rgba(2, 5, 11, .82); }
}

/* Tight shadow for edge definition, wide one as a last-resort local scrim.
   With the backdrop behind them these stay subtle rather than smeary. */
.hud-kicker,
.hud-title,
.hud-sub,
.hud-track {
    text-shadow: 0 1px 2px rgba(0,0,0,.9), 0 2px 14px rgba(0,0,0,.65);
}

body.nohud .hud { display: none; }

.hud-rail {
    width: 2px;
    border-radius: 2px;
    background: linear-gradient(to bottom, var(--accent), var(--accent-2));
    opacity: .85;
    flex: none;
}

.hud-body { min-width: 0; }

.hud-kicker {
    margin: 0 0 8px;
    font: 600 10px/1 var(--mono);
    letter-spacing: .30em;
    text-transform: uppercase;
    color: var(--accent);
}

/* The saturated accents lose too much against warm art; these are the same
   hues lifted toward white so they stay legible without going grey. */
.hud-kicker      { color: #8ff5e4; }
.hud-kicker.user { color: #c9b8ff; }

.hud-title {
    margin: 0;
    font-size: clamp(24px, 3.4vw, 42px);
    font-weight: 300;
    letter-spacing: -.015em;
    line-height: 1.1;
    overflow-wrap: anywhere;
}

/* Lighter than --dim: that grey is tuned for panels on a dark background,
   and over artwork it was the first thing to disappear. */
.hud-sub {
    margin: 8px 0 0;
    font-size: 13px;
    color: rgba(231, 236, 246, .84);
    overflow-wrap: anywhere;
}

.hud-track {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 14px 0 0;
    font: 500 11px/1 var(--mono);
    letter-spacing: .06em;
    color: rgba(231, 236, 246, .76);
}

/* Four bars that bounce only while audio is actually playing. */
.eq { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; }
.eq i {
    width: 2px;
    height: 3px;
    background: var(--accent);
    border-radius: 1px;
    transition: height .2s;
}
body.playing .eq i { animation: bounce .9s ease-in-out infinite; }
body.playing .eq i:nth-child(2) { animation-delay: .15s; }
body.playing .eq i:nth-child(3) { animation-delay: .35s; }
body.playing .eq i:nth-child(4) { animation-delay: .5s; }

@keyframes bounce {
    0%, 100% { height: 3px; }
    50%      { height: 12px; }
}

/* ------------------------------------------------------------ progress */

.progress {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    z-index: 25;
    background: rgba(255,255,255,.06);
    transition: opacity .5s var(--ease);
}

.progress span {
    display: block;
    height: 100%;
    width: 0;
    background: linear-gradient(90deg, var(--accent), var(--accent-2));
    box-shadow: 0 0 12px rgba(94,234,212,.5);
}

.progress.run span { animation: fill var(--dwell) linear forwards; }

@keyframes fill { from { width: 0; } to { width: 100%; } }

/* ---------------------------------------------------------------- dock */

.dock {
    position: fixed;
    left: 50%;
    bottom: clamp(18px, 3vh, 30px);
    transform: translateX(-50%);
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 7px;
    border-radius: 16px;
    background: var(--glass);
    border: 1px solid var(--edge);
    backdrop-filter: blur(18px) saturate(150%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    box-shadow: 0 18px 50px rgba(0,0,0,.55);
    transition: opacity .5s var(--ease);
}

.dock-sep {
    width: 1px;
    height: 20px;
    margin: 0 5px;
    background: var(--edge);
}

.dock-btn {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    border-radius: 11px;
    background: transparent;
    color: var(--dim);
    cursor: pointer;
    transition: background .18s, color .18s, transform .18s;
}

.dock-btn:hover  { background: rgba(255,255,255,.07); color: var(--ink); }
.dock-btn:active { transform: scale(.93); }

.dock-btn svg {
    width: 19px;
    height: 19px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.dock-btn.hot { color: var(--accent); }

/* Play/pause and sound each hold two icons; state picks one. */
.i-play, .i-off { display: none; }
body.paused #btnPlay .i-play  { display: block; }
body.paused #btnPlay .i-pause { display: none; }
body.muted  #btnSound .i-off  { display: block; }
body.muted  #btnSound .i-on   { display: none; }
body.muted  #btnSound         { color: var(--faint); }

/* Sound button pulses while a track is still downloading. */
#btnSound.loading { color: var(--accent-2); animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .35; } }

/* --------------------------------------------------------------- sheet */

.sheet {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: grid;
    place-items: center;
    padding: 20px;
    background: rgba(2, 4, 9, .68);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    animation: fade .22s var(--ease);
}

@keyframes fade { from { opacity: 0; } }

.sheet-panel {
    width: min(560px, 100%);
    max-height: min(86vh, 780px);
    display: flex;
    flex-direction: column;
    border-radius: 20px;
    background: linear-gradient(180deg, rgba(18,23,36,.96), rgba(9,12,20,.97));
    border: 1px solid var(--edge);
    box-shadow: 0 40px 100px rgba(0,0,0,.7);
    animation: rise .28s var(--ease);
    overflow: hidden;
}

@keyframes rise { from { transform: translateY(14px) scale(.985); opacity: 0; } }

.sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 20px 14px;
    border-bottom: 1px solid var(--edge);
}

.sheet-head h2 {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--dim);
}

.x {
    width: 30px; height: 30px;
    border: 0; border-radius: 8px;
    background: transparent;
    color: var(--faint);
    font-size: 22px; line-height: 1;
    cursor: pointer;
}
.x:hover { background: rgba(255,255,255,.07); color: var(--ink); }

.sheet-body {
    padding: 6px 20px 20px;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.grp { padding: 16px 0; border-bottom: 1px solid rgba(255,255,255,.055); }
.grp:last-child { border-bottom: 0; }

.grp h3 {
    margin: 0 0 12px;
    font: 600 10px/1 var(--mono);
    letter-spacing: .28em;
    text-transform: uppercase;
    color: var(--faint);
}

/* ---------------------------------------------------------------- rows */

.row {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 14px;
    padding: 11px 0;
    cursor: pointer;
}

/* Song-length pacing is on: the fixed count still applies wherever a track
   is missing or its length is not known, so this is dimmed rather than
   disabled — it is a fallback, not dead UI. */
body.bysong #dwellRow { opacity: .45; }
body.bysong #dwellRow em::after {
    content: " · fallback only";
    color: var(--accent-2);
}

.row > span { display: block; min-width: 0; }
.row b  { display: block; font-size: 14px; font-weight: 500; }
.row em {
    display: block;
    margin-top: 3px;
    font-size: 12px;
    font-style: normal;
    color: var(--faint);
}

/* Switch: the checkbox itself is the a11y surface, .track is the pill. */
.switch input { position: absolute; opacity: 0; pointer-events: none; }

.switch .track {
    position: relative;
    width: 42px; height: 24px;
    border-radius: 12px;
    background: rgba(255,255,255,.10);
    border: 1px solid var(--edge);
    transition: background .2s;
    flex: none;
}

.switch .track::after {
    content: "";
    position: absolute;
    top: 3px; left: 3px;
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--dim);
    transition: transform .22s var(--ease), background .2s;
}

.switch input:checked + .track {
    background: linear-gradient(90deg, rgba(94,234,212,.30), rgba(167,139,250,.30));
    border-color: rgba(94,234,212,.45);
}
.switch input:checked + .track::after {
    transform: translateX(18px);
    background: var(--accent);
}
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 2px; }

/* -------------------------------------------------------------- ranges */

input[type=range] {
    -webkit-appearance: none;
    appearance: none;
    width: 168px;
    height: 22px;
    background: transparent;
    cursor: pointer;
}

input[type=range]::-webkit-slider-runnable-track {
    height: 3px;
    border-radius: 2px;
    background: rgba(255,255,255,.13);
}
input[type=range]::-moz-range-track {
    height: 3px;
    border-radius: 2px;
    background: rgba(255,255,255,.13);
}

input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 14px; height: 14px;
    margin-top: -5.5px;
    border-radius: 50%;
    background: var(--accent);
    border: 0;
    box-shadow: 0 0 10px rgba(94,234,212,.55);
}
input[type=range]::-moz-range-thumb {
    width: 14px; height: 14px;
    border-radius: 50%;
    background: var(--accent);
    border: 0;
    box-shadow: 0 0 10px rgba(94,234,212,.55);
}

/* EQ knobs — three vertical faders. */
.eq-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    margin: 16px 0 6px;
    padding: 16px 10px 12px;
    border-radius: 14px;
    background: rgba(255,255,255,.03);
    border: 1px solid rgba(255,255,255,.055);
}

.eqk {
    display: grid;
    justify-items: center;
    gap: 8px;
    cursor: pointer;
}

.eqk span { font: 500 10px/1 var(--mono); color: var(--accent); }
.eqk b    { font: 500 10px/1 var(--mono); letter-spacing: .18em; text-transform: uppercase; color: var(--faint); }

/* Vertical range inputs still need the WebKit rotation dance. */
.eqk input[type=range] {
    width: 96px;
    transform: rotate(-90deg);
    margin: 41px -41px;
}

/* ------------------------------------------------------ music schedule */

/* Collapsed until the schedule is switched on: an empty rule editor is
   noise, and the switch above it is the only thing that matters first. */
#schedBody { display: none; }
body.sched #schedBody { display: block; }

.pick {
    padding: 8px 11px;
    border-radius: 9px;
    border: 1px solid var(--edge);
    background: rgba(255,255,255,.05);
    color: var(--ink);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
}
.pick:focus { outline: none; border-color: var(--accent); }
.pick option { background: #12172a; color: var(--ink); }

.rule {
    margin: 10px 0;
    padding: 12px 13px;
    border-radius: 13px;
    background: rgba(255,255,255,.035);
    border: 1px solid var(--edge);
}

/* A rule kept but switched off still occupies its place in the order, so it
   fades rather than vanishing — the ordering is the whole semantics. */
.rule.sleeping { opacity: .45; }

.rule-top {
    display: flex;
    align-items: center;
    gap: 9px;
}

.rule-top .pick { flex: 1; min-width: 0; }

/* Same pill as everywhere else, scaled down to sit inside a rule row. */
.rule-live { display: inline-flex; flex: none; cursor: pointer; }
.rule-live .track { width: 36px; height: 20px; }
.rule-live .track::after { width: 13px; height: 13px; top: 2.5px; left: 2.5px; }
.rule-live input:checked + .track::after { transform: translateX(16px); }

.rule-del {
    width: 28px; height: 28px;
    flex: none;
    border: 0; border-radius: 8px;
    background: transparent;
    color: var(--faint);
    font-size: 19px; line-height: 1;
    cursor: pointer;
}
.rule-del:hover { background: rgba(251,113,133,.14); color: var(--danger); }

.rule-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 5px;
    margin: 11px 0;
}

.rule-days button {
    padding: 7px 0;
    border-radius: 8px;
    border: 1px solid var(--edge);
    background: rgba(255,255,255,.03);
    color: var(--faint);
    font: 600 11px/1 var(--mono);
    cursor: pointer;
    transition: .15s;
}
.rule-days button:hover { color: var(--ink); border-color: var(--edge-hi); }
.rule-days button.on {
    background: rgba(94,234,212,.16);
    border-color: rgba(94,234,212,.5);
    color: var(--accent);
}

.rule-times {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.rule-times input[type=time] {
    padding: 7px 9px;
    border-radius: 8px;
    border: 1px solid var(--edge);
    background: rgba(255,255,255,.04);
    color: var(--ink);
    font-family: var(--mono);
    font-size: 12.5px;
}
.rule-times input[type=time]:focus { outline: none; border-color: var(--accent); }
/* The picker glyph defaults to near-black on a dark field. */
.rule-times input[type=time]::-webkit-calendar-picker-indicator {
    filter: invert(1) opacity(.45);
    cursor: pointer;
}

.rule-times .dash { color: var(--faint); font-size: 12px; }

.rule-desc {
    flex: 1 1 100%;
    font: 500 10.5px/1.5 var(--mono);
    font-style: normal;
    color: var(--faint);
    letter-spacing: .04em;
}

.addrule {
    width: 100%;
    margin-top: 4px;
    padding: 10px;
    border-radius: 10px;
    border: 1px dashed rgba(255,255,255,.17);
    background: transparent;
    color: var(--dim);
    font: 500 11px/1 var(--mono);
    letter-spacing: .14em;
    text-transform: uppercase;
    cursor: pointer;
    transition: .18s;
}
.addrule:hover { border-color: var(--accent); color: var(--accent); }

.norules { margin: 10px 0; }

#schedStatus:not(:empty) {
    margin-top: 12px;
    padding: 9px 11px;
    border-radius: 9px;
    background: rgba(167,139,250,.09);
    border: 1px solid rgba(167,139,250,.22);
    color: #ddd6fe;
}

/* Muted by schedule reads the same as muted by hand on the dock. */
body.sched-muted #btnSound .i-off { display: block; }
body.sched-muted #btnSound .i-on  { display: none; }
body.sched-muted #btnSound        { color: var(--faint); }

.presets { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px; }

.presets button {
    padding: 7px 13px;
    border-radius: 9px;
    border: 1px solid var(--edge);
    background: rgba(255,255,255,.03);
    color: var(--dim);
    font: 500 11px/1 var(--mono);
    letter-spacing: .1em;
    text-transform: uppercase;
    cursor: pointer;
    transition: .18s;
}
.presets button:hover { color: var(--ink); border-color: var(--edge-hi); }
.presets button.on {
    color: var(--bg);
    background: var(--accent);
    border-color: var(--accent);
}

/* --------------------------------------------------------------- forms */

.drop {
    display: grid;
    place-items: center;
    gap: 8px;
    padding: 30px 20px;
    margin-top: 14px;
    border-radius: 15px;
    border: 1.5px dashed rgba(255,255,255,.16);
    background: rgba(255,255,255,.02);
    text-align: center;
    cursor: pointer;
    transition: .2s;
}
.drop:hover, .drop:focus-visible, .drop.over {
    border-color: var(--accent);
    background: rgba(94,234,212,.06);
    outline: none;
}
.drop svg {
    width: 26px; height: 26px;
    fill: none; stroke: var(--faint);
    stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.drop.over svg, .drop:hover svg { stroke: var(--accent); }
.drop p  { margin: 0; font-size: 13px; color: var(--dim); }
.drop b  { color: var(--ink); font-weight: 500; }
.drop em { font-size: 11px; font-style: normal; color: var(--faint); font-family: var(--mono); }
.drop.has { border-style: solid; border-color: rgba(94,234,212,.5); }

/* Preview of the chosen file, drawn as a background on .drop */
.drop.has { background-size: cover; background-position: center; }
.drop.has p, .drop.has em {
    padding: 4px 10px;
    border-radius: 7px;
    background: rgba(3,5,10,.72);
    backdrop-filter: blur(5px);
}
.drop.has svg { display: none; }

.or {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 18px 0 4px;
    color: var(--faint);
    font: 500 10px/1 var(--mono);
    letter-spacing: .2em;
    text-transform: uppercase;
}
.or::before, .or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--edge);
}

.field { display: block; margin-top: 14px; }

.field > span {
    display: block;
    margin-bottom: 7px;
    font-size: 12px;
    color: var(--dim);
}
.field i { font-style: normal; color: var(--faint); }

.field input {
    width: 100%;
    padding: 11px 13px;
    border-radius: 10px;
    border: 1px solid var(--edge);
    background: rgba(255,255,255,.035);
    color: var(--ink);
    font-family: inherit;
    font-size: 14px;
    transition: .18s;
}
.field input::placeholder { color: rgba(139,150,173,.5); }
.field input:focus {
    outline: none;
    border-color: var(--accent);
    background: rgba(94,234,212,.05);
    box-shadow: 0 0 0 3px rgba(94,234,212,.10);
}

.field em {
    display: block;
    margin-top: 6px;
    font-size: 11px;
    font-style: normal;
    color: var(--faint);
    line-height: 1.5;
}

.actions { margin-top: 20px; }

.go {
    width: 100%;
    padding: 13px;
    border: 0;
    border-radius: 11px;
    background: linear-gradient(90deg, var(--accent), var(--accent-2));
    color: #04121a;
    font-family: var(--mono);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    cursor: pointer;
    transition: .18s;
}
.go:hover:not(:disabled) { filter: brightness(1.1); }
.go:disabled { opacity: .45; cursor: not-allowed; }

/* --------------------------------------------------------------- meter */

.meter {
    height: 4px;
    margin: 14px 0 8px;
    border-radius: 3px;
    background: rgba(255,255,255,.08);
    overflow: hidden;
}

.meter span {
    display: block;
    height: 100%;
    width: 0;
    border-radius: 3px;
    background: linear-gradient(90deg, var(--accent), var(--accent-2));
    transition: width .4s var(--ease), background .3s;
}

.meter span.full { background: var(--danger); }

.note {
    margin: 0;
    font-size: 11.5px;
    line-height: 1.6;
    color: var(--faint);
}

.msg {
    margin: 16px 0 0;
    padding: 11px 13px;
    border-radius: 10px;
    font-size: 12.5px;
    line-height: 1.5;
    border: 1px solid;
}
.msg.err { background: rgba(251,113,133,.09); border-color: rgba(251,113,133,.32); color: #fecdd3; }
.msg.ok  { background: rgba(94,234,212,.09);  border-color: rgba(94,234,212,.32);  color: #a7f3e4; }
.msg.busy{ background: rgba(167,139,250,.09); border-color: rgba(167,139,250,.32); color: #ddd6fe; }

/* --------------------------------------------------------------- toast */

.toast {
    position: fixed;
    left: 50%;
    bottom: clamp(84px, 12vh, 120px);
    transform: translate(-50%, 10px);
    z-index: 80;
    padding: 11px 18px;
    border-radius: 11px;
    background: var(--glass);
    border: 1px solid var(--edge);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    font-size: 13px;
    color: var(--ink);
    box-shadow: 0 16px 40px rgba(0,0,0,.5);
    opacity: 0;
    transition: opacity .28s, transform .28s var(--ease);
    max-width: min(90vw, 460px);
    text-align: center;
}

.toast.up { opacity: 1; transform: translate(-50%, 0); }

/* --------------------------------------------------------------- small */

@media (max-width: 560px) {
    .hud { bottom: clamp(92px, 14vh, 140px); max-width: 84vw; }
    .dock { gap: 2px; padding: 6px; }
    .dock-btn { width: 38px; height: 38px; }
    input[type=range] { width: 128px; }
    .eqk input[type=range] { width: 84px; margin: 35px -35px; }
}

@media (prefers-reduced-motion: reduce) {
    /* Kill the drift and the crossfade travel; keep opacity changes so the
       stream still reads as a stream. */
    body.drift .layer.on { animation: none; transform: scale(1.02); }
    .boot-mark { animation-duration: 3s; }
    .eq i { animation: none !important; }
    * { scroll-behavior: auto; }
}
