:root {
    --bg: oklch(0.98 0 247.73);
    --surface: oklch(1 0 0);
    --text: oklch(0.26 0.01 248.18);
    --text-muted: oklch(0.56 0.02 244.88);
    --border: oklch(0.91 0.01 247.87);
    --primary: oklch(0.55 0.22 262.88);
    --primary-hover: oklch(0.49 0.22 264.38);
    --secondary: oklch(0.56 0.02 244.88);
    --success: oklch(0.63 0.17 149.21);
    --warning: oklch(0.68 0.14 75.83);
    --danger: oklch(0.58 0.22 27.33);
    --radius: 8px;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.6;
}

header .top-nav {
    display: flex;
    align-items: center;
    gap: 2rem;
    padding: 1rem 2rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

header .top-nav > a {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--text);
    text-decoration: none;
}

.nav-links {
    display: flex;
    gap: 1rem;
    flex: 1;
}

.nav-links a {
    color: var(--text-muted);
    text-decoration: none;
}

.nav-links a:hover {
    color: var(--primary);
}

.nav-right {
    margin-left: auto;
    position: relative;
}

/* User menu */
.user-menu-button {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.4rem 0.8rem;
    cursor: pointer;
    font-size: 0.875rem;
    color: var(--text);
}

.user-menu-button:hover {
    background: var(--bg);
}

.user-menu-button {
    anchor-name: --user-menu-anchor;
}

.popover-menu {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 4px 12px oklch(0 0 0 / 0.1);
    padding: 0.5rem 0;
    min-width: 160px;
    /* Anchor to the user menu button */
    position-anchor: --user-menu-anchor;
    inset: unset;
    top: anchor(bottom);
    right: anchor(right);
    margin-top: 0.5rem;
}

.popover-menu a,
.popover-menu .menu-link {
    display: block;
    padding: 0.5rem 1rem;
    color: var(--text);
    text-decoration: none;
    font-size: 0.875rem;
    background: none;
    border: none;
    width: 100%;
    text-align: left;
    cursor: pointer;
}

.popover-menu a:hover,
.popover-menu .menu-link:hover {
    background: var(--bg);
}

.menu-info {
    padding: 0.5rem 1rem;
    font-size: 0.8rem;
    color: var(--warning);
    font-weight: 500;
}

.popover-menu hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 0.25rem 0;
}

/* Flash messages */
#flash-messages {
    max-width: 960px;
    margin: 1rem auto 0;
    padding: 0 1rem;
}

.flash-message {
    padding: 0.75rem 1rem;
    border-radius: var(--radius);
    margin-bottom: 0.5rem;
    font-size: 0.875rem;
}

.flash-success { background: oklch(0.96 0.04 156.70); color: var(--success); }
.flash-error { background: oklch(0.94 0.03 17.77); color: var(--danger); }
.flash-warning { background: oklch(0.97 0.07 103.18); color: var(--warning); }
.flash-info { background: oklch(0.93 0.03 255.58); color: var(--primary); }

main {
    max-width: 960px;
    margin: 2rem auto;
    padding: 0 1rem;
}

footer {
    text-align: center;
    padding: 2rem;
    color: var(--text-muted);
    font-size: 0.875rem;
}

h1 { margin-bottom: 1rem; }
h3 { margin-bottom: 0.5rem; }

.subtitle {
    color: var(--text-muted);
    margin-bottom: 1rem;
}

.help-text {
    color: var(--text-muted);
    font-size: 0.875rem;
    margin-bottom: 1rem;
}

.empty-state {
    text-align: center;
    padding: 3rem;
    color: var(--text-muted);
}

/* Buttons */
.button {
    display: inline-block;
    padding: 0.5rem 1rem;
    border-radius: var(--radius);
    text-decoration: none;
    font-size: 0.875rem;
    font-weight: 500;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color 0.15s;
}

.button.primary,
button[type="submit"] {
    background: var(--primary);
    color: oklch(1 0 0);
}

.button.primary:hover,
button[type="submit"]:hover {
    background: var(--primary-hover);
}

.button.secondary {
    background: var(--surface);
    color: var(--text-muted);
    border-color: var(--border);
}

.button.secondary:hover {
    background: var(--bg);
}

.button.danger {
    background: var(--danger);
    color: oklch(1 0 0);
}

/* Pattern Grid */
.pattern-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 1rem;
}

.pattern-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--goal-color, var(--border));
    border-radius: var(--radius);
    padding: 1.5rem;
}

.pattern-card .location {
    color: var(--text-muted);
    font-size: 0.875rem;
    margin-bottom: 0.75rem;
}

.constraint-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

.badge {
    display: inline-block;
    padding: 0.25rem 0.5rem;
    background: var(--bg);
    border-radius: 4px;
    font-size: 0.8rem;
    color: var(--text-muted);
}

.badge strong {
    color: var(--text);
}

.card-actions {
    display: flex;
    gap: 0.5rem;
}

/* Forms */
fieldset {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem;
    margin-bottom: 1rem;
}

legend {
    font-weight: 600;
    padding: 0 0.5rem;
}

.form-field {
    margin-bottom: 1rem;
}

.form-field label {
    display: block;
    font-size: 0.875rem;
    font-weight: 500;
    margin-bottom: 0.25rem;
}

.form-field input,
.form-field select {
    width: 100%;
    padding: 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 1rem;
}

.form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
}

.checkbox-field {
    margin-bottom: 1rem;
}

.checkbox-field label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
}

.form-actions {
    margin-top: 1.5rem;
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

button[type="submit"] {
    padding: 0.75rem 2rem;
    font-size: 1rem;
    border: none;
    border-radius: var(--radius);
    cursor: pointer;
}

/* Forecast Results */
.results-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 1rem;
}

.result-count {
    font-weight: 500;
}

.sort-controls {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.sort-label {
    color: var(--text-muted);
    font-size: 0.875rem;
}

.sort-option {
    font-size: 0.875rem;
    padding: 0.25rem 0.75rem;
    border-radius: 4px;
    text-decoration: none;
    color: var(--text-muted);
    border: 1px solid var(--border);
}

.sort-option:hover {
    background: var(--bg);
    color: var(--text);
}

.sort-option.active {
    background: var(--primary);
    color: oklch(1 0 0);
    border-color: var(--primary);
}

.window-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-bottom: 2rem;
}

.window-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--goal-color, var(--border));
    border-radius: var(--radius);
    padding: 1.5rem;
}

@media (min-width: 640px) {
    .window-card {
        display: grid;
        grid-template-columns: auto 1fr;
        gap: 0 2rem;
        align-items: start;
    }
}

.window-header {
    margin-bottom: 1rem;
}

@media (min-width: 640px) {
    .window-header {
        margin-bottom: 0;
        padding-right: 2rem;
        border-right: 1px solid var(--border);
        border-bottom: none;
    }
}

@media (max-width: 639px) {
    .window-header {
        padding-bottom: 0.75rem;
        border-bottom: 1px solid var(--border);
    }
}

.window-time {
    color: var(--text-muted);
    font-size: 1.1rem;
}

.window-best-time {
    color: var(--text-muted);
    font-size: 0.8rem;
    font-style: italic;
    margin-top: 0.1rem;
}

/* Calendar */
.calendar-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 1rem;
    padding: 0.75rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

/* Quiet caption above the grid telling the user whose sunrise/sunset
   the default day/night shading reflects. Updates to the focused bar's
   location while a bar is hovered/focused/tapped (calendar.js). */
.calendar-shading-caption {
    margin-bottom: 0.5rem;
    font-size: 0.8rem;
    color: var(--text-muted);
}

.calendar-shading-label-prefix {
    opacity: 0.85;
}

.legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.875rem;
    color: inherit;
    text-decoration: none;
    border-radius: 3px;
    padding: 0.1rem 0.25rem;
    margin: -0.1rem -0.25rem;
    transition: background 120ms ease-out;
}

.legend-item:hover,
.legend-item:focus-visible {
    background: var(--bg);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

.legend-swatch {
    display: inline-block;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 3px;
}

.calendar-grid {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.calendar-row {
    display: grid;
    grid-template-columns: 7rem 1fr;
    border-bottom: 1px solid var(--border);
}

.calendar-row:last-child {
    border-bottom: none;
}

.calendar-header {
    background: var(--bg);
}

.calendar-day-label {
    padding: 0.5rem 0.75rem;
    font-size: 0.8rem;
    color: var(--text-muted);
    border-right: 1px solid var(--border);
    display: flex;
    align-items: center;
}

.calendar-hours {
    position: relative;
    height: 1.5rem;
}

.hour-label {
    position: absolute;
    top: 0.25rem;
    font-size: 0.7rem;
    color: var(--text-muted);
    transform: translateX(-50%);
}

/* Container that stacks per-pattern tracks inside a single day row.
   When patterns don't overlap on a day they share one track; when any
   pair overlaps, overlapping patterns get separate tracks so each
   color stays visible. */
.calendar-day-tracks {
    display: flex;
    flex-direction: column;
}

.calendar-day-track {
    position: relative;
    height: 2.25rem;
    /* Day/night shading is driven by two CSS custom properties on the
       track: --sunrise and --sunset (as unitless 0–100 numbers). They
       default to --sunrise-default / --sunset-default, which CalendarView
       writes inline from the reference location's solar times. When a
       bar is hovered/focused/tapped, calendar.js overrides --sunrise /
       --sunset with that bar's own pattern solar, so the band reflects
       the match's real location.

       A gradient of 0 0 / transparent transparent / 0 0 flattens to
       "no band", which is what we want when no reference solar exists
       (both defaults are 0). */
    --sunrise: var(--sunrise-default, 0);
    --sunset: var(--sunset-default, 0);
    --night-tint: oklch(0.29 0.08 269.96 / 0.09);
    background:
        linear-gradient(
            to right,
            var(--night-tint) 0%,
            var(--night-tint) calc(var(--sunrise) * 1%),
            transparent calc(var(--sunrise) * 1%),
            transparent calc(var(--sunset) * 1%),
            var(--night-tint) calc(var(--sunset) * 1%),
            var(--night-tint) 100%
        );
}

/* When a bar is focused (hover on desktop, keyboard focus, or tap on
   mobile — see calendar.js for the tap handler), calendar.js copies
   that bar's data-sunrise/data-sunset onto this track as --sunrise /
   --sunset. The gradient above re-resolves automatically. */

/* Stacked tracks within one day get a thin separator so the horizontal
   time axis is still readable, but muted enough that a quiet day with
   a single track still feels like one strip. */
.calendar-day-track + .calendar-day-track {
    border-top: 1px dashed var(--border);
}

.calendar-gridline {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--border);
    opacity: 0.5;
}

.calendar-bar {
    position: absolute;
    top: 0.3rem;
    bottom: 0.3rem;
    border-radius: 3px;
    cursor: default;
    display: flex;
    align-items: center;
    padding: 0 0.35rem;
}

.calendar-bar-label {
    font-size: 0.7rem;
    color: oklch(0 0 0);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Info card overlay on hover */
.calendar-info-card {
    position: absolute;
    top: calc(100% + 0.25rem);
    left: 0;
    right: auto;
    min-width: 18rem;
    max-width: 22rem;
    padding: 0.75rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--goal-color);
    border-radius: var(--radius);
    box-shadow: 0 4px 12px oklch(0 0 0 / 0.15);
    font-size: 0.8rem;
    color: var(--text);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.1s ease-in;
    z-index: 10;
}

.calendar-bar.is-selected .calendar-info-card,
.calendar-bar:focus-visible .calendar-info-card {
    opacity: 1;
    pointer-events: auto;
}

/* :hover is sticky on iOS Safari — tap-focusing a bar leaves :hover
   active until the user taps somewhere else, which prevented the
   tap-to-dismiss gesture from actually hiding the overlay. Scope the
   hover trigger to devices with a real hover capability (desktop);
   mobile drives visibility entirely through .is-selected (calendar.js). */
@media (hover: hover) {
    .calendar-bar:hover .calendar-info-card {
        opacity: 1;
        pointer-events: auto;
    }
}

.calendar-bar.is-selected,
.calendar-bar:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}

/* Anchor the card relative to the bar based on where the bar sits in
   the track. Left: grows rightward from the bar's left edge. Right:
   grows leftward from the bar's right edge. Center: centered on the
   bar's midpoint. */
.calendar-bar.card-anchor-right .calendar-info-card {
    left: auto;
    right: 0;
}

.calendar-bar.card-anchor-center .calendar-info-card {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
}

.info-card-header {
    margin-bottom: 0.25rem;
    font-size: 0.875rem;
}

.info-card-time {
    color: var(--text-muted);
    margin-bottom: 0.15rem;
}

.info-card-best {
    color: var(--text-muted);
    font-style: italic;
    font-size: 0.75rem;
    margin-bottom: 0.5rem;
}

.info-card-conditions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.15rem 0.75rem;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--border);
}

.info-card-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.25rem;
}

.info-card-label {
    color: var(--text-muted);
    text-align: right;
    font-size: 0.75rem;
}

.info-card-value {
    font-weight: 500;
    text-align: left;
    font-size: 0.75rem;
}

/* Hue picker */
.hue-picker {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
}

.hue-slider {
    flex: 1;
    height: 0.75rem;
    border-radius: 0.375rem;
    background: linear-gradient(to right,
        oklch(65% 0.18 0),
        oklch(65% 0.18 60),
        oklch(65% 0.18 120),
        oklch(65% 0.18 180),
        oklch(65% 0.18 240),
        oklch(65% 0.18 300),
        oklch(65% 0.18 360));
    appearance: none;
    -webkit-appearance: none;
}

.hue-slider::-webkit-slider-thumb {
    appearance: none;
    -webkit-appearance: none;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: oklch(1 0 0);
    border: 2px solid var(--text);
    box-shadow: 0 1px 3px oklch(0 0 0 / 0.25);
    cursor: grab;
    transition: transform 100ms ease-out;
}

.hue-slider::-webkit-slider-thumb:hover {
    transform: scale(1.1);
}

.hue-slider::-moz-range-thumb {
    appearance: none;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: oklch(1 0 0);
    border: 2px solid var(--text);
    box-shadow: 0 1px 3px oklch(0 0 0 / 0.25);
    cursor: grab;
    transition: transform 100ms ease-out;
}

.hue-slider::-moz-range-thumb:hover {
    transform: scale(1.1);
}

.hue-swatch {
    display: inline-block;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    border: 1px solid var(--border);
}

.quality-badge {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: 4px;
    font-size: 0.8rem;
    font-weight: 600;
    margin-top: 0.5rem;
}

.quality-excellent { background: oklch(0.96 0.04 156.70); color: var(--success); }
.quality-good { background: oklch(0.93 0.03 255.58); color: var(--primary); }
.quality-fair { background: oklch(0.97 0.07 103.18); color: var(--warning); }
.quality-marginal { background: oklch(0.94 0.03 17.77); color: var(--danger); }

.window-conditions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.25rem 2rem;
}

@media (min-width: 640px) {
    .window-conditions {
        padding-top: 0.25rem;
    }
}

.condition-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
    padding: 0.25rem 0;
}

.condition-label {
    color: var(--text-muted);
    font-size: 0.875rem;
    text-align: right;
}

.condition-value {
    font-weight: 500;
    font-size: 0.875rem;
    text-align: left;
}

.page-actions {
    margin-top: 2rem;
}

/* Tables (admin) */
.data-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    margin-top: 1rem;
}

.data-table th,
.data-table td {
    padding: 0.75rem 1rem;
    text-align: left;
    border-bottom: 1px solid var(--border);
    font-size: 0.875rem;
}

.data-table th {
    background: var(--bg);
    font-weight: 600;
    color: var(--text-muted);
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}

.table-actions {
    display: flex;
    gap: 0.5rem;
}

.role-badge {
    display: inline-block;
    padding: 0.15rem 0.5rem;
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 600;
    background: var(--bg);
    color: var(--text-muted);
}

.role-admin {
    background: oklch(0.93 0.03 255.58);
    color: var(--primary);
}

/* Invite form */
.invite-form {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem;
    margin-bottom: 1rem;
}

/* Auth page (login/register) */
.auth-page main {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 60vh;
}

.auth-error {
    color: var(--danger);
    margin-bottom: 1rem;
}

.auth-button {
    padding: 0.75rem 1.5rem;
    border-radius: var(--radius);
    font-size: 1rem;
    font-weight: 500;
    border: none;
    cursor: pointer;
}

.auth-button.primary {
    background: var(--primary);
    color: oklch(1 0 0);
}

.auth-button.primary:hover {
    background: var(--primary-hover);
}

/* ────────────────────────────────────────────────────────────────
   Pattern editor map picker (Leaflet)
   ──────────────────────────────────────────────────────────────── */

.pattern-map {
    width: 100%;
    height: 400px;
    margin: 0.75rem 0 1rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    background: oklch(0.97 0 0);  /* shown briefly before tiles load */
}

/* Leaflet's divIcon containers don't have a default appearance — we
   supply our own graphics so station dots and the forecast-location pin
   look distinct. Keeps station pins lightweight (3,000+ of them can
   sit on the map at once). */

/* Tide station — small blue dot, centered in a generous 20px hitbox. */
.pattern-station-marker {
    display: flex;
    align-items: center;
    justify-content: center;
}

.pattern-station-dot {
    width: 12px;
    height: 12px;
    background: oklch(0.61 0.21 259.94);           /* OpenStreetMap-ish blue */
    border: 2px solid oklch(1 0 0);
    border-radius: 50%;
    box-shadow: 0 0 0 1px oklch(0 0 0 / 0.25);
    cursor: pointer;
    transition: transform 120ms ease-out;
}

.pattern-station-dot:hover {
    transform: scale(1.25);
}

/* Selected tide station — same blue family, just bolder and ringed,
   so it reads as "a station that's been chosen" rather than
   "the forecast location". */
.pattern-station-dot-selected {
    width: 16px;
    height: 16px;
    background: oklch(0.49 0.22 261.49);
    border-width: 3px;
    box-shadow: 0 0 0 2px oklch(0.49 0.22 261.49 / 0.35), 0 0 6px oklch(0.49 0.22 261.49 / 0.5);
}

.pattern-station-dot-selected:hover {
    transform: none;
}

/* Forecast location — orange teardrop pin, pointing down. Classic
   map-pin silhouette so it stands apart from the round station dots.
   Built with a rotated square + an inner circle; no SVG needed. */
.pattern-location-marker {
    filter: drop-shadow(0 2px 2px oklch(0 0 0 / 0.35));
}

.pattern-location-pin {
    position: relative;
    width: 22px;
    height: 22px;
    background: oklch(0.70 0.19 39.23);
    border: 2px solid oklch(1 0 0);
    /* Teardrop: round three corners, sharpen the bottom. */
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
    cursor: grab;
}

/* Small white bullseye inside the pin to hint at the exact point. */
.pattern-location-pin::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 6px;
    height: 6px;
    background: oklch(1 0 0);
    border-radius: 50%;
    transform: translate(-50%, -50%);
}

.pattern-location-marker:active .pattern-location-pin {
    cursor: grabbing;
}

/* ────────────────────────────────────────────────────────────────
   Range slider (range-slider.js)
   ──────────────────────────────────────────────────────────────── */

.range-slider {
    margin: 0.25rem 0 0.75rem;
    padding: 1rem 1.25rem 1.25rem;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;  /* let the JS handle drags, don't scroll */
}

.range-slider-label {
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--text);
    margin-bottom: 0.75rem;
    min-height: 1.25em;
}

.range-slider-track {
    position: relative;
    height: 12px;
    background: var(--border);
    border-radius: 6px;
    /* Allow thumbs to extend past the track ends without clipping. */
    margin: 0 1.25rem;
}

/* Muted-gradient layer beneath the fill. In the `--gradient` variants
   we paint the same gradient here with low chroma so the out-of-range
   portion of the track hints at the scale without competing with the
   active fill. */
.range-slider-track-bg {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    display: none;
}

.range-slider--gradient .range-slider-track {
    background: transparent;   /* track-bg takes over */
}

.range-slider--gradient .range-slider-track-bg {
    display: block;
}

.range-slider-fill {
    position: absolute;
    inset: 0;
    background: var(--primary);
    border-radius: inherit;
    pointer-events: none;
    /* clip-path is set inline by the JS so the fill's gradient aligns
       exactly with the track-bg's gradient in screen coordinates. */
}

/* Hitbox: 44×44, generous for touch. Visible circle inside is 28px. */
.range-slider-thumb {
    position: absolute;
    top: 50%;
    width: 44px;
    height: 44px;
    margin: -22px 0 0 -22px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: grab;
    touch-action: none;
}

.range-slider-thumb::after {
    content: "";
    display: block;
    width: 28px;
    height: 28px;
    background: oklch(1 0 0);
    border: 2px solid var(--primary);
    border-radius: 50%;
    box-shadow: 0 1px 3px oklch(0 0 0 / 0.25);
    transition: transform 100ms ease-out;
}

.range-slider-thumb:focus-visible {
    outline: none;
}

.range-slider-thumb:focus-visible::after {
    box-shadow: 0 0 0 3px oklch(0.51 0.17 255.79 / 0.35), 0 1px 3px oklch(0 0 0 / 0.25);
}

.range-slider-thumb:hover::after {
    transform: scale(1.1);
}

.range-slider-thumb-active {
    cursor: grabbing;
}

.range-slider-thumb-active::after {
    transform: scale(1.15);
}

/* ── Gradients ──────────────────────────────────────────────
   OKLCH so equal perceptual steps look equal. Each scheme
   supplies a vivid palette for the fill (active range) and
   a desaturated version for the track-bg (out-of-range). */

/* Temperature, 20°F → 100°F (NWS-inspired heat scale). */
.range-slider--temperature .range-slider-fill {
    background: linear-gradient(
        to right in oklch,
        oklch(55% 0.15 250) 0%,    /*  20°F cold blue */
        oklch(72% 0.12 215) 25%,   /*  40°F cool blue-teal */
        oklch(80% 0.13 165) 44%,   /*  55°F teal-green */
        oklch(85% 0.15 130) 60%,   /*  68°F green */
        oklch(88% 0.17  95) 69%,   /*  75°F warm yellow */
        oklch(75% 0.17  55) 81%,   /*  85°F orange */
        oklch(55% 0.22  30) 100%   /* 100°F hot red */
    );
}

.range-slider--temperature .range-slider-track-bg {
    background: linear-gradient(
        to right in oklch,
        oklch(90% 0.04 250) 0%,
        oklch(92% 0.03 215) 25%,
        oklch(93% 0.03 165) 44%,
        oklch(93% 0.04 130) 60%,
        oklch(94% 0.05  95) 69%,
        oklch(92% 0.05  55) 81%,
        oklch(88% 0.06  30) 100%
    );
}

/* Wind speed, 0–40 kn (Beaufort-inspired). */
.range-slider--wind .range-slider-fill {
    background: linear-gradient(
        to right in oklch,
        oklch(96% 0.04 140)   0%,    /*  0 kn calm */
        oklch(88% 0.10 140) 12.5%,   /*  5 kn light */
        oklch(82% 0.14 140)  25%,    /* 10 kn gentle */
        oklch(86% 0.16 110)  40%,    /* 16 kn moderate */
        oklch(88% 0.18  95) 52.5%,   /* 21 kn fresh */
        oklch(75% 0.17  55) 67.5%,   /* 27 kn strong */
        oklch(65% 0.20  30) 82.5%,   /* 33 kn near gale */
        oklch(50% 0.22  20) 100%     /* 40 kn gale */
    );
}

.range-slider--wind .range-slider-track-bg {
    background: linear-gradient(
        to right in oklch,
        oklch(96% 0.02 140)   0%,
        oklch(93% 0.04 140) 12.5%,
        oklch(91% 0.04 140)  25%,
        oklch(93% 0.04 110)  40%,
        oklch(94% 0.05  95) 52.5%,
        oklch(92% 0.05  55) 67.5%,
        oklch(90% 0.06  30) 82.5%,
        oklch(85% 0.07  20) 100%
    );
}

/* Air quality, US AQI 0–500 (EPA AirNow category colors). Low = clean
   air (green), high = hazardous (maroon), so the fill's direction alone
   tells the user which way is better. Colors converted from EPA's sRGB
   values to OKLCH; stops sit near each category's band on the 0–500 scale
   (boundaries at 50/100/150/200/300 → 10/20/30/40/60%). */
.range-slider--airquality .range-slider-fill {
    background: linear-gradient(
        to right in oklch,
        oklch(80% 0.27 142.5)   0%,     /*   0 good (green) */
        oklch(80% 0.27 142.5)   5%,     /*  25 good */
        oklch(97% 0.21 109.76)  15%,    /*  75 moderate (yellow) */
        oklch(73% 0.19 52.15)   25%,    /* 125 USG (orange) */
        oklch(63% 0.26 29.23)   35%,    /* 175 unhealthy (red) */
        oklch(51% 0.16 323.94)  50%,    /* 250 very unhealthy (purple) */
        oklch(38% 0.15 16.79)   80%,    /* 400 hazardous (maroon) */
        oklch(38% 0.15 16.79)   100%    /* 500 hazardous */
    );
}

.range-slider--airquality .range-slider-track-bg {
    background: linear-gradient(
        to right in oklch,
        oklch(93% 0.05 142.5)   0%,
        oklch(93% 0.05 142.5)   5%,
        oklch(95% 0.05 109.76)  15%,
        oklch(93% 0.05 52.15)   25%,
        oklch(90% 0.06 29.23)   35%,
        oklch(88% 0.05 323.94)  50%,
        oklch(85% 0.06 16.79)   80%,
        oklch(85% 0.06 16.79)   100%
    );
}

.range-slider-field {
    /* Looser vertical rhythm than a stock form field — the slider
       takes real estate. */
    margin-bottom: 1.25rem;
}

.range-slider-title {
    display: block;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-muted);
    margin-bottom: 0.25rem;
}
