/**
 * KUNAI Mouse Driver — Component Styles
 * Structure layer: chrome, nav, panels, controls, hero, overlays.
 * Load after kunai-base.css; kunai-shine.css adds the static material sheen on top.
 */

/* =============================================================================
   1. TOP BAR (full-width chrome: brand · device gem · profile · actions)
   ============================================================================= */
.top-bar {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    height: 64px;
    padding: 0 28px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    background: linear-gradient(180deg, rgba(12, 12, 13, 0.7) 0%, rgba(8, 8, 9, 0.4) 100%);
    -webkit-backdrop-filter: var(--overlay-blur);
    backdrop-filter: var(--overlay-blur);
    position: relative;
    z-index: var(--z-chrome);
}

.top-bar-brand { display: flex; align-items: center; gap: 18px; min-width: 0; }

/* Live device state, in the one strip that never scrolls away. Each reading
   wears its own glyph, so the strip is scannable without being read. */
.vitals {
    display: flex;
    align-items: center;
    gap: 18px;
    margin: 0;
    padding-left: 4px;
    font-size: var(--text-sm);
    color: var(--text-tertiary);
    white-space: nowrap;
    animation: fade-in 0.6s var(--ease-ios) both;
}
.vitals[hidden] { display: none; }
.vital-value {
    color: var(--text-primary);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}
.vital-unit { font-size: var(--text-xs); color: var(--text-tertiary); margin-left: 3px; }
.vital { display: inline-flex; align-items: center; gap: 6px; }
.vital[hidden] { display: none; }
.vital-icon { width: 14px; height: 14px; color: var(--text-tertiary); flex: 0 0 auto; }
.vital.low .vital-value,
.vital.low .vital-icon { color: var(--accent); }
@media (max-width: 1000px) { .vitals { display: none; } }

/* The wordmark, not a bold letterspaced approximation of it. Keyed to white ink
   on transparency in build/, so it carries no background into the bar. */
.logo {
    display: block;
    height: 19px;
    width: auto;
    padding-right: 18px;
    border-right: 1px solid rgba(255, 255, 255, 0.08);
}

/* Device status gem */
.status-gem {
    display: flex;
    align-items: center;
    gap: 9px;
    height: 34px;
    padding: 0 14px 0 12px;
    border-radius: var(--radius-pill);
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    box-shadow: var(--edge-highlight-subtle);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text-secondary);
    transition: border-color var(--duration-base), background-color var(--duration-base), color var(--duration-base);
}
.status-gem-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--text-tertiary);
    box-shadow: none;
    transition: background-color var(--duration-base), box-shadow var(--duration-base);
}
.status-gem.connected { color: var(--text-primary); border-color: rgba(var(--accent-rgb), 0.32); }
.status-gem.connected .status-gem-dot {
    background: var(--accent);
    box-shadow: 0 0 8px rgba(var(--accent-rgb), 0.6);
    animation: pulse-glow 2s var(--ease-balanced) infinite;
}
.status-gem.connecting .status-gem-dot { background: var(--accent-warning); box-shadow: 0 0 8px rgba(var(--accent-warning-rgb), 0.5); }

.top-bar-actions { display: flex; align-items: center; gap: 10px; }

/* Profile selector + popover */
.profile-shell { position: relative; }
.profile-selector {
    display: flex; align-items: center; gap: 10px;
    height: 36px; padding: 0 12px 0 14px;
    border-radius: var(--radius-pill);
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    box-shadow: var(--edge-highlight-subtle);
    color: var(--text-primary);
    font-size: var(--text-base); font-weight: 500;
    cursor: pointer;
    transition: background-color var(--duration-base), border-color var(--duration-base), color var(--duration-base);
}
.profile-selector:hover { background: var(--glass-control-hover); border-color: var(--glass-border-strong); color: var(--text-bright); }
.profile-selector:active { transform: scale(var(--press-standard)); }
.profile-selector .profile-selector-label { color: var(--text-tertiary); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.08em; }
.profile-selector .profile-selector-chevron { width: 12px; height: 12px; color: var(--text-tertiary); transition: transform var(--duration-base) var(--ease-ios); }
.profile-shell.open .profile-selector-chevron { transform: rotate(180deg); }

.profile-popover {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 280px;
    background: var(--overlay-surface);
    border: 1px solid var(--overlay-border);
    box-shadow: var(--overlay-shadow);
    border-radius: var(--radius-lg);
    padding: 10px;
    z-index: var(--z-popover);
    opacity: 0; visibility: hidden; pointer-events: none;
    transform: translateY(-6px);
    transition:
        opacity var(--motion-menu-duration) var(--ease-ios),
        transform var(--motion-menu-duration) var(--ease-ios),
        visibility 0s linear var(--motion-menu-duration);
}
.profile-shell.open .profile-popover {
    opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0);
    transition-delay: 0s;
    -webkit-backdrop-filter: var(--overlay-blur-soft);
    backdrop-filter: var(--overlay-blur-soft);
}
.profile-popover-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 4px 8px 10px;
}
.section-label {
    font-size: var(--text-xs); font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: 0.08em;
}

/* =============================================================================
   2. PROFILE LIST (inside popover)
   ============================================================================= */
.profile-list { display: flex; flex-direction: column; gap: 2px; }
.profile-item {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-primary);
    font-size: var(--text-base);
    transition: background-color var(--duration-micro), color var(--duration-micro);
}
.profile-item:hover { background: var(--glass-control-hover); color: var(--text-bright); }
.profile-item:active { transform: scale(var(--press-subtle)); }
.profile-item.active {
    background: rgba(var(--accent-rgb), 0.12);
    color: var(--text-bright);
    box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), 0.34);
}
.profile-item .profile-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn-profile-action {
    display: grid; place-items: center;
    width: 24px; height: 24px;
    border: none; background: transparent;
    color: var(--text-tertiary);
    border-radius: var(--radius-xs);
    cursor: pointer;
    transition: background-color var(--duration-micro), color var(--duration-micro);
}
.btn-profile-action:hover { background: rgba(255, 255, 255, 0.08); color: var(--text-bright); }
.btn-profile-action:active { transform: scale(var(--press-deep)); }

.profile-popover-footer { margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(255, 255, 255, 0.06); }
.btn-import {
    display: flex; align-items: center; gap: 8px; width: 100%;
    height: 34px; padding: 0 12px;
    border-radius: var(--radius-sm);
    background: transparent; border: none;
    color: var(--text-secondary); font-size: var(--text-base); font-weight: 500;
    cursor: pointer;
    transition: background-color var(--duration-micro), color var(--duration-micro);
}
.btn-import:hover { background: var(--glass-control-hover); color: var(--text-bright); }
.btn-import svg { color: var(--text-tertiary); }

/* =============================================================================
   3. MODE-ORBIT NAV (floating glass capsule + sliding liquid pill)
   ============================================================================= */
/* Lifted out of the flow so the work can travel underneath it. The pill kept
   its 20px lead and its own 20px blur; what it never had was anything behind
   it to look through, which is why the top of the list read as a cut edge
   rather than as something passing under glass. */
.mode-orbit-wrap {
    position: absolute;
    top: var(--pill-top);
    left: 0;
    right: 0;
    z-index: var(--z-sticky);
    display: flex;
    justify-content: center;
    pointer-events: none;
}
.mode-orbit-wrap > * { pointer-events: auto; }

.mode-orbit {
    display: inline-flex;
    align-items: center;
    gap: 0;
    padding: 3px;
    background: rgba(18, 18, 18, 0.52);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-radius: var(--radius-pill);
    box-shadow: var(--edge-highlight), 0 8px 22px rgba(0, 0, 0, 0.24);
    position: relative;
    isolation: isolate;
    overflow: hidden;
}
.mode-orbit::before {
    content: '';
    position: absolute; inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: linear-gradient(180deg,
        rgba(255, 255, 255, 0.095) 0%, rgba(255, 255, 255, 0.042) 34%,
        rgba(255, 255, 255, 0.012) 62%, rgba(255, 255, 255, 0.03) 100%);
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
    pointer-events: none; opacity: 0.86;
}
.tab-button {
    position: relative;
    z-index: var(--z-local);
    height: 36px;
    padding: 0 20px;
    border: none;
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--text-base);
    font-weight: 500;
    letter-spacing: 0.01em;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    white-space: nowrap;
    transition: color var(--duration-base) var(--ease-ios), background-color var(--duration-base) var(--ease-ios);
}
.tab-button:hover:not(.active) { color: var(--text-primary); background: rgba(255, 255, 255, 0.03); }
.tab-button.active { color: var(--text-bright); background: transparent; }

.active-tab-underline {
    position: absolute;
    top: 3px; bottom: 3px; left: 3px;
    width: 0;
    border-radius: var(--radius-pill);
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.02) 100%),
        rgba(40, 42, 47, 0.92);
    box-shadow:
        var(--edge-highlight-bright),
        inset 0 0 0 1px rgba(255, 255, 255, 0.03),
        0 8px 18px rgba(0, 0, 0, 0.28);
    z-index: var(--z-base);
    pointer-events: none;
    opacity: 0;
    --indicator-sheen-origin: 50%;
}
.active-tab-underline.ready { opacity: 1; }
/* leading-edge sheen that tracks travel direction (origin set by JS) */
.active-tab-underline::after {
    content: '';
    position: absolute; inset: 1px;
    border-radius: inherit;
    background: radial-gradient(120% 120% at var(--indicator-sheen-origin) 0%,
        rgba(var(--accent-rgb), 0.16) 0%, rgba(var(--accent-rgb), 0.04) 38%, transparent 72%);
    opacity: 0.9;
    pointer-events: none;
}

/* =============================================================================
   4. TABS
   ============================================================================= */
.tab-content { display: none; }
.tab-content.active {
    display: flex;
    flex-direction: column;
    gap: 20px;
    animation: fade-in-tab var(--duration-base) var(--ease-ios);
}

/* =============================================================================
   5. PANELS / CARDS
   ============================================================================= */
/* The same surface every other section uses. This carried a 20px
   backdrop-filter until it did not. Macros is the only tab built out of panels,
   so the filter had nothing to rasterise until the tab was shown, and then had
   to sample a backdrop that is itself a blended layer: the blur arrived a beat
   after the tab and popped in. It was also picking up the product image out in
   the gutter and smearing it across the editor. At this alpha, over a near
   black page, a solid surface reads the same and arrives with the tab. */
.panel {
    position: relative;
    background: var(--glass-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--surface-radius-primary);
    box-shadow: var(--glass-shadow);
    padding: var(--surface-padding-lg);
    display: flex;
    flex-direction: column;
    gap: 16px;
    isolation: isolate;
    overflow: hidden;
    contain: layout style;
    transition: box-shadow var(--duration-base) ease;
}
.panel:hover { box-shadow: var(--glass-shadow-hover); }
/* glass rim of light (bright top, dark belly, faint bottom) */
.panel::before {
    content: '';
    position: absolute; inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: var(--glass-edge-mask);
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
    pointer-events: none;
    opacity: 0.7;
    z-index: var(--z-local-top);
}

/* An open dropdown (or hovered tooltip) lifts its whole panel above sibling
   cards so the popover renders ON TOP of neighbours instead of under them. */
.panel:has(.custom-select.open),
.panel:has(.tooltip-trigger:hover),
.panel:has(.tooltip-trigger:focus-visible) {
    overflow: visible;
    contain: none;
    z-index: var(--z-sticky);
}

.panel-header {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px;
    min-height: 28px;
}
.panel-title {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: var(--text-md);
    font-weight: 600;
    color: var(--text-bright);
    letter-spacing: -0.01em;
    margin: 0;
}
.panel-hint {
    font-size: var(--text-sm);
    color: var(--text-tertiary);
    line-height: 1.5;
    margin: 0;
}
.hint-text {
    font-size: var(--text-sm);
    color: var(--text-tertiary);
    line-height: 1.45;
}

.label {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text-secondary);
}

/* =============================================================================
   6. BUTTONS
   ============================================================================= */
.btn-primary, .btn-secondary, .btn-danger {
    position: relative;
    height: var(--control-height-md);
    padding: var(--control-padding-md);
    border-radius: var(--radius-pill);
    font-size: var(--text-base);
    font-weight: 600;
    letter-spacing: 0.01em;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    overflow: hidden;
    transition:
        background-color var(--duration-base), color var(--duration-base),
        border-color var(--duration-base), box-shadow var(--duration-base);
}
.btn-primary > *, .btn-secondary > *, .btn-danger > * { position: relative; z-index: var(--z-local-raised); }

/* Affirmative — solid crimson */
.btn-primary {
    background: var(--accent);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: var(--text-bright);
    text-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.14),
        0 10px 24px rgba(var(--accent-rgb), 0.20);
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active:not(:disabled) { transform: scale(var(--press-standard)); background: var(--accent-pressed); }

/* Secondary — graphite glass */
.btn-secondary {
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    color: var(--text-primary);
    font-weight: 500;
    box-shadow: var(--edge-highlight-subtle);
}
.btn-secondary:hover { background: var(--glass-control-hover); border-color: var(--glass-border-strong); color: var(--text-bright); }
.btn-secondary:active:not(:disabled) { transform: scale(var(--press-standard)); background: var(--glass-control-active); }

/* Destructive — outlined crimson boundary */
.btn-danger {
    background: rgba(var(--accent-rgb), 0.08);
    border: 1px solid rgba(var(--accent-rgb), 0.36);
    color: var(--accent);
    font-weight: 500;
    box-shadow: var(--edge-highlight);
}
.btn-danger:hover { background: rgba(var(--accent-rgb), 0.16); border-color: rgba(var(--accent-rgb), 0.5); color: var(--text-bright); }
.btn-danger:active:not(:disabled) { transform: scale(var(--press-standard)); background: rgba(var(--accent-rgb), 0.22); }

.btn-primary:disabled, .btn-secondary:disabled, .btn-danger:disabled {
    opacity: 0.5; cursor: not-allowed; pointer-events: none; transform: none;
}

.btn-sm { height: 30px; padding: 0 12px; font-size: var(--text-sm); }

/* Connect button (the gateway) */
.btn-connect {
    position: relative;
    height: 36px; padding: 0 18px;
    border-radius: var(--radius-pill);
    background: var(--accent);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: var(--text-bright);
    font-size: var(--text-base); font-weight: 600; letter-spacing: 0.01em;
    cursor: pointer;
    display: inline-flex; align-items: center; gap: 8px;
    overflow: hidden;
    text-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 10px 24px rgba(var(--accent-rgb), 0.24);
    transition: background-color var(--duration-base), box-shadow var(--duration-base), border-color var(--duration-base);
}
.btn-connect > * { position: relative; z-index: var(--z-local-raised); }
.btn-connect:hover:not(:disabled) { background: var(--accent-hover); }
.btn-connect:active:not(:disabled) { transform: scale(var(--press-standard)); background: var(--accent-pressed); }
.btn-connect:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-connect .icon-disconnect { display: none; }
.btn-connect.connected { background: var(--glass-control); border-color: var(--glass-border); color: var(--text-primary); text-shadow: none; box-shadow: var(--edge-highlight-subtle); }
.btn-connect.connected:hover { background: var(--glass-control-hover); border-color: var(--glass-border-strong); color: var(--text-bright); }
.btn-connect.connected .icon-connect { display: none; }
.btn-connect.connected .icon-disconnect { display: inline; }

/* =============================================================================
   7. TOGGLE SWITCH (two-speed: track 0.16s / knob 0.22s)
   ============================================================================= */
.toggle-switch { position: relative; display: inline-block; width: 40px; height: 22px; flex-shrink: 0; }
.toggle-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-slider {
    position: absolute; inset: 0;
    background-color: rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: background-color var(--duration-quick) ease;
}
.toggle-slider::before {
    content: '';
    position: absolute; height: 18px; width: 18px; left: 2px; bottom: 2px;
    background-color: var(--slider-thumb);
    border-radius: 50%;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.25);
    transition: transform var(--duration-base) ease, background-color var(--duration-base) ease;
}
.toggle-switch:hover .toggle-slider { background-color: rgba(255, 255, 255, 0.2); }
input:checked + .toggle-slider { background-color: var(--accent); }
.toggle-switch:hover input:checked + .toggle-slider { background-color: var(--accent-hover); }
input:checked + .toggle-slider::before { transform: translateX(18px); background-color: var(--text-bright); }

/* =============================================================================
   8. RANGE SLIDER (track fill via --slider-fill, white→crimson thumb)
   ============================================================================= */
input[type="range"] {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: 20px;
    background: transparent; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
    width: 100%; height: 4px;
    border-radius: var(--radius-xs);
    background: linear-gradient(to right,
        var(--accent) 0%, var(--accent) var(--slider-fill, 0%),
        rgba(255, 255, 255, 0.1) var(--slider-fill, 0%), rgba(255, 255, 255, 0.1) 100%);
    transition: background var(--duration-base);
}
input[type="range"]:hover::-webkit-slider-runnable-track {
    background: linear-gradient(to right,
        var(--accent) 0%, var(--accent) var(--slider-fill, 0%),
        rgba(255, 255, 255, 0.2) var(--slider-fill, 0%), rgba(255, 255, 255, 0.2) 100%);
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    height: 18px; width: 18px; border-radius: 50%;
    background: var(--text-bright);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
    margin-top: -7px;
    transition: transform var(--duration-base) var(--ease-squish), background-color var(--duration-base), box-shadow var(--duration-base);
}
input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.15); }
input[type="range"]:active::-webkit-slider-thumb {
    transform: scale(1.3);
    background: var(--accent);
    box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.2);
}
input[type="range"]::-moz-range-track { height: 4px; border-radius: var(--radius-xs); background: rgba(255, 255, 255, 0.1); }
input[type="range"]::-moz-range-progress { height: 4px; border-radius: var(--radius-xs); background: var(--accent); }
input[type="range"]::-moz-range-thumb {
    height: 18px; width: 18px; border: none; border-radius: 50%;
    background: var(--text-bright); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}

/* =============================================================================
   9. CUSTOM SELECT / DROPDOWN
   ============================================================================= */
.custom-select { position: relative; }
.custom-select.full-width { width: 100%; }
.select-trigger {
    width: 100%;
    height: var(--control-height-md);
    padding: 0 14px;
    background-color: var(--glass-control);
    border: 1px solid var(--glass-border);
    color: var(--text-primary);
    border-radius: var(--radius-pill);
    font-size: var(--text-base); font-weight: 500;
    cursor: pointer;
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    box-shadow: var(--edge-highlight-subtle), inset 0 2px 4px -2px rgba(0, 0, 0, 0.3);
    transition: background-color var(--duration-base), border-color var(--duration-base), box-shadow var(--duration-base);
}
.select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.select-arrow {
    flex-shrink: 0;
    width: 0; height: 0;
    border-left: 4px solid transparent; border-right: 4px solid transparent;
    border-top: 5px solid rgba(255, 255, 255, 0.55);
    font-size: 0;
    transition: transform var(--duration-base) var(--ease-ios);
}
.select-trigger:hover { background-color: var(--glass-control-hover); border-color: var(--glass-border-strong); }
.select-trigger:active { transform: scale(var(--press-subtle)); background-color: var(--glass-control-active); }
.custom-select.open .select-trigger {
    background-color: rgba(255, 255, 255, 0.05);
    border-color: rgba(var(--accent-rgb), 0.45);
    box-shadow: var(--active-state);
}
.custom-select.open .select-arrow { transform: rotate(180deg); }

.options-list {
    position: absolute;
    top: calc(100% + 4px); left: 0; right: 0;
    min-width: 160px;
    list-style: none;
    background: var(--overlay-surface);
    border: 1px solid var(--overlay-border);
    box-shadow: var(--overlay-shadow);
    border-radius: var(--radius-md);
    padding: 6px;
    z-index: var(--z-popover);
    transform-origin: top;
    opacity: 0; visibility: hidden; pointer-events: none;
    transform: translateY(calc(var(--motion-menu-distance) * -1));
    transition:
        opacity var(--motion-menu-duration) var(--ease-ios),
        transform var(--motion-menu-duration) var(--ease-ios),
        visibility 0s linear var(--motion-menu-duration);
    max-height: 280px; overflow-y: auto;
}
/* Keep the scrollbar out of the rounded corners. */
.options-list::-webkit-scrollbar-track { margin: var(--radius-md) 0; }
.custom-select.open .options-list {
    opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0);
    transition-delay: 0s;
    -webkit-backdrop-filter: var(--overlay-blur-soft);
    backdrop-filter: var(--overlay-blur-soft);
}
/* Near the bottom of the viewport the menu opens upward instead of vanishing
   off screen. The class is set on open, from measurement. */
.custom-select.drop-up .options-list {
    top: auto; bottom: calc(100% + 4px);
    transform-origin: bottom;
    transform: translateY(var(--motion-menu-distance));
}
.custom-select.drop-up.open .options-list { transform: translateY(0); }
.option {
    display: flex; align-items: center;
    min-height: 36px;
    padding: 8px 12px;
    margin-bottom: 2px;
    font-size: var(--text-base); font-weight: 400;
    color: var(--text-primary);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color var(--duration-micro), color var(--duration-micro);
}
.option:last-child { margin-bottom: 0; }
.option:hover { background-color: var(--glass-control-hover); color: var(--text-bright); }
.option:active { transform: scale(var(--press-subtle)); }
/* Neutral, like the tab lozenge and the segmented control. Accent means the
   value you set, not the row you are standing on, and this was the only menu
   row in either driver claiming it. Same values Titan's options-list uses. */
.option.selected {
    color: var(--text-bright); font-weight: 600;
    background-color: rgba(255, 255, 255, 0.07);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.035);
}
.option.selected:hover { background-color: rgba(255, 255, 255, 0.1); }

/* =============================================================================
   10. TEXT / NUMBER INPUTS
   ============================================================================= */
.text-input, .number-input {
    height: var(--control-height-md);
    padding: 0 14px;
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-pill);
    color: var(--text-primary);
    font-size: var(--text-base); font-weight: 500;
    box-shadow: var(--edge-highlight-subtle), inset 0 2px 4px -2px rgba(0, 0, 0, 0.3);
    transition: background-color var(--duration-base), border-color var(--duration-base);
}
.text-input.full-width { width: 100%; }
.text-input::placeholder { color: var(--text-tertiary); }
.text-input:hover:not(:focus), .number-input:hover:not(:focus) { background: var(--glass-control-hover); border-color: var(--glass-border-strong); }
.text-input:focus, .number-input:focus { background: rgba(255, 255, 255, 0.08); border-color: rgba(var(--accent-rgb), 0.55); outline: none; }
.number-input {
    width: 72px; text-align: center; padding: 0;
    color: var(--text-value); font-weight: 600; font-family: var(--font-mono);
    -moz-appearance: textfield;
}
.number-input::-webkit-outer-spin-button, .number-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.input-label {
    display: block;
    font-size: var(--text-sm); font-weight: 500;
    color: var(--text-secondary);
    margin-bottom: 8px;
}

/* =============================================================================
   11. RADIO GROUPS (segmented pill style)
   ============================================================================= */
.radio-group {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 4px;
    background: var(--bg-input);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
}
.radio-option {
    flex: 1 1 auto;
    position: relative;
    display: flex; align-items: center; justify-content: center;
    min-width: 56px;
    cursor: pointer;
}
.radio-option input[type="radio"] { position: absolute; opacity: 0; width: 0; height: 0; }
.radio-label {
    flex: 1; text-align: center;
    padding: 8px 14px;
    border-radius: var(--radius-md);
    font-size: var(--text-sm); font-weight: 500;
    color: var(--text-secondary);
    transition: background-color var(--duration-quick) var(--ease-ios), color var(--duration-quick) var(--ease-ios), box-shadow var(--duration-quick);
}
.radio-option:hover input:not(:checked):not(:disabled) + .radio-label {
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-primary);
}
.radio-option input:checked + .radio-label {
    color: var(--text-bright);
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.02) 100%),
        rgba(40, 42, 47, 0.95);
    box-shadow: var(--edge-highlight-bright), inset 0 0 0 1px rgba(var(--accent-rgb), 0.22), 0 4px 10px rgba(0, 0, 0, 0.25);
}
.radio-option input:focus-visible + .radio-label { outline: 2px solid var(--accent); outline-offset: 2px; }

/* =============================================================================
   12. SETTING ROWS
   ============================================================================= */
.setting-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px 0; }
.setting-label { font-size: var(--text-base); color: var(--text-primary); }
.setting-control { display: flex; align-items: center; gap: 8px; }
.setting-suffix { font-size: var(--text-sm); color: var(--text-tertiary); }

/* The scale: one slider with its named values printed on the track. A tuner
   revealing a second control was two instruments for one job, and it hid the
   most adjusted value on the page behind a glyph. */
.scale-block { display: flex; align-items: center; gap: 14px; }
.scale-axes { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 22px; }
/* The chain brackets the tracks, so it centres between them and not on the
   block: the printed numbers hang below the last track and would otherwise pull
   it half a tick row low. A bottom margin of one tick row lifts it by half. */
.scale-block > .axis-link { margin-bottom: var(--scale-ticks); }

/* Two rows: the track and everything that labels it on row one, the printed
   numbers on row two, under the track and nothing else. The name and the value
   used to sit in a flex row beside a box holding the slider AND its scale, so
   they centred against the pair and read as floating between the two. Gaps are
   margins rather than column-gap, so the name's column leaves nothing behind
   when there is one axis and it is not there. */
.scale-axis {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
}
.scale-axis[hidden] { display: none; }
/* The name column opens and closes on the same beat as the axis it labels.
   Appearing instantly took 98px out of the track while the second row was still
   unfolding, so one gesture ran on two clocks: the thumb snapped 48px sideways
   and the row below arrived afterwards. Durations match revealSection. */
.scale-axis-name {
    grid-area: 1 / 1;
    width: 0;
    margin-right: 0;
    overflow: hidden;
    white-space: nowrap;
    font-size: var(--text-sm);
    color: var(--text-secondary);
    /* Leaving, with the fold that takes the second axis away. */
    transition: width 98ms cubic-bezier(0.4, 0, 1, 1),
                margin-right 98ms cubic-bezier(0.4, 0, 1, 1);
}
.scale-axes.split .scale-axis-name {
    width: 84px;
    margin-right: 14px;
    /* Arriving, with the fold that brings it. */
    transition: width 140ms var(--ease-ios), margin-right 140ms var(--ease-ios);
}
.scale-axis .range-slider { grid-area: 1 / 2; display: block; }
.scale-axis .value-numeral { grid-area: 1 / 3; margin-left: 14px; }

.scale-stops { grid-area: 2 / 2; position: relative; height: var(--scale-ticks); }
.scale-stop {
    position: absolute;
    /* Against the thumb's travel, not the track's width: the thumb centre stops
       half its own width short of each end, so a plain percentage would print
       every number slightly off the value it names. */
    left: calc(9px + (100% - 18px) * var(--at));
    transform: translateX(-50%);
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    padding: 0; border: 0; background: none; cursor: pointer;
    color: var(--text-tertiary);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    transition: color var(--duration-base) ease;
}
.scale-tick {
    width: 1px; height: 5px;
    background: currentColor;
    opacity: 0.45;
    transition: background-color var(--duration-base) ease, opacity var(--duration-base) ease;
}
/* No lit state. The thumb sits over the value it has reached, so highlighting
   the label as well made the stage chip, the scale and the numeral all state
   the same number, which is the duplication the readout was retired for once
   already. The scale names what is available; the thumb says where you are. */
.scale-stop:hover { color: var(--text-secondary); }

/* A lit icon button: the chain, while the axes are apart. One glyph carrying
   its own state, rather than a second one. */
.axis-link.broken {
    color: var(--accent);
    border-color: rgba(var(--accent-rgb), 0.35);
    background: rgba(var(--accent-rgb), 0.1);
}

/* =============================================================================
   13. THE PRODUCT AS ATMOSPHERE
   ============================================================================= */
/* The product used to own 41% of the window and do nothing with it, which put
   the most dominant thing on screen furthest from the work. It now costs no
   layout at all: it lives in the gutter beside the centred column, screened
   into the dark so black contributes no light and no edge can exist. Where the
   product is actually a control (Buttons, Sensor) it appears inside that
   section instead, at the size the job needs. */
/* No z-index here, ever. A z-index on a positioned element makes it a stacking
   context, and the screen blend below would then have only this box to blend
   with instead of the page, which puts the render back on a black rectangle.
   Paint order is handled by .work-side lifting itself instead. */
.backdrop {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}
/* Light for it to emerge from, fading to nothing long before any edge. */
.backdrop::before {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse 34% 46% at 16% 50%,
        rgba(44, 44, 54, 0.42) 0%,
        rgba(22, 22, 28, 0.16) 46%,
        rgba(0, 0, 0, 0) 78%);
}
/* Anchored to the column's left edge and sized from the gutter, so the same
   share of the product shows at every window width instead of being cropped. */
.backdrop-image {
    position: absolute;
    top: 52%;
    right: calc(50% + 460px);
    /* Sized from the gutter so the same share shows at ordinary widths, but
       capped: the gutter keeps growing on an ultrawide and atmosphere that
       scales without limit stops being atmosphere and becomes a billboard. */
    width: min(calc((50vw - 380px) * 1.05), 640px);
    transform: translateY(-50%);
    /* The screen blend is gone. It existed because the old render carried its
       own flat #020202 backdrop, and screening it meant that black added no
       light and could draw no edge. This render is a real cutout, so it
       composites normally, and screening it now would only wash the shell grey.
       The mask stays, for the other half of the job: the product runs off the
       side of the window, and it should fade out there rather than end on a
       cut. Opacity because a cutout at full strength stops being atmosphere and
       starts competing with the work; this holds the gutter at the brightness
       it had before. */
    opacity: 0.42;
    -webkit-mask-image: radial-gradient(ellipse 66% 76% at 43% 50%, #000 30%, transparent 100%);
    mask-image: radial-gradient(ellipse 66% 76% at 43% 50%, #000 30%, transparent 100%);
    animation: hero-settle var(--duration-emphasis) var(--ease-ios) both;
}
/* The end state deliberately omits opacity. This animation fills both ways, so
   naming a value here would hold it forever and overrule the resting opacity
   above; leaving it out makes the arrival end on whatever the element is set
   to, which is the point. */
@keyframes hero-settle {
    from { opacity: 0; transform: translateY(-50%) translateX(-10px); }
    to   { transform: translateY(-50%); }
}
/* Below this the gutter is gone, and atmosphere with nowhere to sit is clutter. */
@media (max-width: 1180px) {
    .backdrop { display: none; }
}

/* -----------------------------------------------------------------------------
   The product inside a section, where it is the control
   -------------------------------------------------------------------------- */
.device-stage {
    margin: 0 0 4px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
}
.device-hint { font-size: var(--text-sm); color: var(--text-tertiary); }
.device-stage .button-map-figure { width: min(210px, 44%); }

/* -----------------------------------------------------------------------------
   Sensor stage: one mouse, two tools
   -------------------------------------------------------------------------- */
/* Rotation and virtual center both answer the question "where is the sensor
   relative to your hand", so they share one drawing. The mouse holds the
   centre and only the marks around it change. */
.sensor-stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
}
/* Placed absolutely, at an explicit height. A percentage height on a grid item
   resolves against the item's own intrinsic size, not the box, which is how the
   product ended up four times the size of its frame.
   The box is taller and wider than the drawing on purpose: the dial arc sits
   above the nose, the axis names sit beside the flank, and a mouse turned 30
   degrees sweeps wider than it stands. */
.sensor-device {
    /* The drawing's own height, the space above it the dial needs, and the box
       they stand in. The box used to centre the drawing, which left 30px under
       the tail doing nothing: a mouse turned to either end of the travel sweeps
       16px taller, so 16 is the whole requirement and the other 14 was air. */
    --mouse-height: 220px;
    --mouse-top: 30px;
    --stage-height: 266px;
    /* The top view is a cutout with air around it: the shell is 1328 of the
       asset's 1412 rows, so 2.97% at each end is transparent. The centre point
       rides the shell, not the image, or it floats off the nose and past the
       tail at the ends of the travel.
       Two insets, not one. They were equal while the box centred the drawing
       and stopped being equal the moment it did not, and a single value left
       the axis 14px short of the tail: you could not point at the last of the
       shell, and the centre tool and the rotation pivot disagreed by 7px about
       where the middle of the mouse was. */
    --shell-pad: calc(var(--mouse-height) * 0.0297);
    --shell-top: calc(var(--mouse-top) + var(--shell-pad));
    --shell-bottom: calc(var(--stage-height) - var(--mouse-top) - var(--mouse-height) + var(--shell-pad));
    position: relative;
    width: 300px;
    height: var(--stage-height);
    touch-action: none;          /* the drag is ours, not the scroller's */
    cursor: grab;
    border-radius: var(--radius-lg);
}
.sensor-device.grabbing { cursor: grabbing; }
.sensor-device:focus { outline: none; }
.sensor-device:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; }
/* Off means the tool is unavailable, not that the drawing should vanish: the
   product still says which mouse this is. */
.sensor-device.control-off { cursor: default; opacity: 0.4; }

.sensor-mouse {
    position: absolute;
    left: 50%;
    top: var(--mouse-top);
    height: var(--mouse-height);
    width: auto;
    opacity: 0.9;
    /* Pinned, not centred: the box lost its spare bottom air and the drawing
       must not drift up with it. It still turns about its own middle, which is
       still 140px down, so the dial's origin is unchanged. */
    transform: translate(-50%, 0) rotate(var(--sensor-angle, 0deg));
    transition: transform var(--duration-base) var(--ease-ios);
    -webkit-user-drag: none;
    user-select: none;
}
/* While a hand is on it the drawing must not lag the pointer. */
.sensor-device.grabbing .sensor-mouse,
.sensor-device.grabbing .sensor-dial-hand { transition: none; }

/* -----------------------------------------------------------------------------
   Rotation: the dial the product turns in
   -------------------------------------------------------------------------- */
/* A track the width of the travel and a handle on it. Without this the drawing
   is a picture that happens to respond, and nothing on screen says it can be
   taken hold of. The arc also states the range, which is why the ends of the
   travel no longer need to be printed as numbers. */
.sensor-dial {
    position: absolute;
    top: 0; left: 0;
    width: 100%;
    /* Its own authored height, not the box's. The viewBox is 280 tall and the
       arc was derived against it; scaling that into a shorter box would move
       the track off the nose it was measured to. The last 14 rows are empty. */
    height: 280px;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--duration-base);
}
.sensor-stage.tool-rotation .sensor-dial { opacity: 1; }
.sensor-dial-track {
    fill: none;
    stroke: rgba(255, 255, 255, 0.13);
    stroke-width: 1.5;
    stroke-linecap: round;
    transition: stroke var(--duration-base);
}
/* Straight ahead. The handle rests on it when the product is square to the
   screen, so neutral is a mark you can hit rather than a number to read. */
.sensor-dial-zero {
    stroke: rgba(255, 255, 255, 0.3);
    stroke-width: 1;
}
.sensor-dial-hand {
    transform-box: view-box;
    transform-origin: 150px 140px;
    transform: rotate(var(--sensor-angle, 0deg));
    transition: transform var(--duration-base) var(--ease-ios);
}
.sensor-dial-hand circle {
    fill: var(--slider-thumb);
    stroke: rgba(0, 0, 0, 0.45);
    stroke-width: 1;
}
.sensor-device:hover .sensor-dial-track,
.sensor-device:focus-visible .sensor-dial-track { stroke: rgba(255, 255, 255, 0.28); }
.sensor-device.grabbing .sensor-dial-track { stroke: rgba(var(--accent-rgb), 0.5); }

/* -----------------------------------------------------------------------------
   Virtual centre: the axis the point rides
   -------------------------------------------------------------------------- */
/* The travel is drawn where the value lives, down the spine of the mouse. This
   element is the travel: the marker is positioned inside it as a percentage and
   the pointer is read against its box, so the geometry has one source. */
.sensor-axis {
    position: absolute;
    top: var(--shell-top);
    bottom: var(--shell-bottom);
    left: 50%;
    width: 1px;
    margin-left: -0.5px;
    background: linear-gradient(180deg,
        rgba(255, 255, 255, 0) 0%,
        rgba(255, 255, 255, 0.2) 14%,
        rgba(255, 255, 255, 0.2) 86%,
        rgba(255, 255, 255, 0) 100%);
    opacity: 0;
    transition: opacity var(--duration-base);
}
.sensor-stage.tool-center .sensor-axis { opacity: 1; }
.sensor-marker {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 10px rgba(var(--accent-rgb), 0.75);
    transform: translate(-50%, -50%);
    opacity: 0;
    transition: opacity var(--duration-base), top var(--duration-quick) var(--ease-ios);
}
.sensor-stage.tool-center .sensor-marker { opacity: 1; }
.sensor-stage.tool-center.center-off .sensor-marker { opacity: 0.28; }
.sensor-device.grabbing .sensor-marker { transition: opacity var(--duration-base); }

/* Front, middle and back, standing beside the positions they name. The same
   rule as the scale under a track: the names say what is available, the marker
   says where you are, so no name lights up. */
.sensor-axis-names {
    position: absolute;
    top: var(--shell-top);
    bottom: var(--shell-bottom);
    left: calc(50% + 74px);
    width: 84px;
    opacity: 0;
    transition: opacity var(--duration-base);
}
.sensor-stage.tool-center .sensor-axis-names { opacity: 1; }
.sensor-axis-name {
    position: absolute;
    top: calc(var(--at) * 100%);
    left: 0;
    transform: translateY(-50%);
    font-size: var(--text-xs);
    color: var(--text-tertiary);
    white-space: nowrap;
}
.sensor-axis-name::before {
    content: '';
    position: absolute;
    left: -12px; top: 50%;
    width: 6px; height: 1px;
    background: currentColor;
    opacity: 0.45;
}
/* The switch sits centred over the product; the selected tool's own control
   rides the right edge of the same line. Absolute so the switch stays centred
   on the drawing whatever the control beside it weighs, and so swapping a
   readout for a toggle cannot shift it. */
.sensor-head {
    position: relative;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: var(--control-height-md);
}
/* Centred by stretching to the line's full height, not by translating half its
   own. The arrival animation when you swap tools keyframes `transform`, so a
   transform holding the vertical position is overwritten for the length of the
   run and reinstated when it is removed: the cluster settled 18px low, then
   jumped. Nothing here owns `transform` any more, so the animation can have it. */
.sensor-tool {
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 10px;
}
.sensor-tool[hidden] { display: none; }

/* Two switches, two instruments. Each gauge is the switch's travel drawn as a
   vertical bore: rest at the top, bottomed out at the base. The live press
   fills it, the trigger line is set by dragging it on the travel itself, and
   the tinted zone below the line is where the switch is fired. */
#panel-switches { position: relative; }
.switch-deck {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
.switch-side { min-width: 0; padding: 18px; }
.switch-side + .switch-side { border-left: 1px solid var(--border-subtle); }
.switch-side-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}
/* Label-and-value pairs space through the flex gap, not a text-node space:
   Chrome's page translation rewrites text nodes and eats the whitespace
   between them, which glued "Trigger 6" into "Trigger6". */
.switch-live {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    font-size: var(--text-caption);
    color: var(--text-tertiary);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    opacity: 0.45;
    transition: opacity var(--duration-base) var(--ease-ios);
}
/* The live layer runs only while the cluster is being watched. */
.live-open .switch-live { opacity: 1; }
.no-live .switch-live { opacity: 0.45; }
.switch-live output,
.switch-readouts output {
    font-family: var(--font-mono);
    color: var(--text-primary);
}
.no-live .switch-live output { display: none; }

.switch-gauge {
    position: relative;
    height: 172px;
    margin-top: 14px;
    touch-action: none;
}
.gauge-bore {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    width: 38px;
    height: 100%;
    overflow: hidden;
    border: 1px solid var(--border-subtle);
    border-radius: 8px;
    background:
        repeating-linear-gradient(to bottom,
            transparent 0, transparent calc(10% - 1px),
            rgba(255, 255, 255, 0.05) calc(10% - 1px), rgba(255, 255, 255, 0.05) 10%),
        var(--bg-input);
    box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.4);
}
.gauge-fill {
    position: absolute;
    top: 0; left: 0; right: 0;
    height: calc(var(--travel, 0) * 100%);
    background: linear-gradient(to bottom, rgba(var(--accent-rgb), 0.25), rgba(var(--accent-rgb), 0.6));
    transition: height 50ms linear;
}
.no-live .gauge-fill { display: none; }
.gauge-zone {
    position: absolute;
    top: calc(var(--point, 0.5) * 100%);
    left: 0; right: 0; bottom: 0;
    background: rgba(var(--accent-rgb), 0.1);
    border-top: 2px solid var(--accent);
    transition: background var(--duration-base) var(--ease-ios), box-shadow var(--duration-base) var(--ease-ios);
}
/* The press has crossed the trigger line: the zone announces the click. */
.fired .gauge-zone {
    background: rgba(var(--accent-rgb), 0.32);
    box-shadow: 0 0 18px rgba(var(--accent-rgb), 0.5);
}
.gauge-reset-line {
    position: absolute;
    left: calc(50% + 2px);
    width: 17px;
    top: calc(var(--reset-top, 0.5) * 100%);
    border-top: 2px dashed rgba(255, 255, 255, 0.45);
}
.reset-off .gauge-reset-line { display: none; }

.gauge-handle {
    position: absolute;
    width: 26px;
    height: 26px;
    top: calc(var(--pos, 0.5) * 100% - 13px);
    cursor: grab;
    border-radius: 50%;
}
.gauge-handle:active { cursor: grabbing; }
.gauge-handle::after {
    content: '';
    position: absolute;
    inset: 7px;
    border-radius: 50%;
    background: var(--text-bright);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
    transition: transform var(--duration-base) var(--ease-ios);
}
.gauge-handle:hover::after, .gauge-handle:focus-visible::after { transform: scale(1.25); }
.gauge-handle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.gauge-handle-trigger { left: calc(50% - 19px - 26px); --pos: var(--point, 0.5); }
.gauge-handle-trigger::after { background: var(--accent); }
.gauge-handle-reset { left: calc(50% + 19px); --pos: var(--reset-top, 0.5); }
.reset-off .gauge-handle-reset::after { opacity: 0.45; }

.switch-readouts {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    margin-top: 12px;
    font-size: var(--text-sm);
    color: var(--text-secondary);
}
.switch-readouts span {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
}
.switch-deck + .row { border-top: 1px solid var(--border-subtle); }

/* First run: the instruments are visible through the glass, but calibration is
   the door in. Skip once to be warned, twice to pass. */
#panel-switches:has(> .cal-glass:not([hidden])) > .switch-deck,
#panel-switches:has(> .cal-glass:not([hidden])) > #panel-calibration {
    filter: blur(5px);
}
.cal-glass {
    position: absolute;
    inset: 0;
    z-index: var(--z-local-raised);
    display: grid;
    place-items: center;
    padding: 24px;
    border-radius: inherit;
    background: rgba(5, 5, 5, 0.55);
}
.cal-glass[hidden] { display: none; }
.cal-glass-card { max-width: 360px; text-align: center; }
.cal-glass-title { font-size: var(--text-lg); color: var(--text-bright); }
.cal-glass-body { margin-top: 8px; font-size: var(--text-sm); color: var(--text-secondary); }
.cal-glass-note { margin-top: 8px; font-size: var(--text-xs); color: var(--text-tertiary); }
.cal-glass-actions {
    display: flex;
    justify-content: center;
    gap: 10px;
    margin-top: 18px;
}

@media (max-width: 620px) {
    .switch-deck { grid-template-columns: 1fr; }
    .switch-side + .switch-side {
        border-top: 1px solid var(--border-subtle);
        border-left: 0;
    }
}

/* =============================================================================
   14. SHEETS — one surface per section, hairline rows
   ============================================================================= */
/* Cards of differing natural height can never align, and forcing them to align
   invents dead space. So there are no cards: each section is a single surface
   whose rows are divided by hairlines, and alignment stops being a question. */
.sheet-stack {
    width: 100%;
    max-width: 720px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
}
.section-heading {
    margin: 30px 0 10px 2px;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}
.sheet-stack > .section-heading:first-child { margin-top: 0; }

/* No overflow clip: dropdowns and tooltips must escape the surface. Nothing in
   a sheet paints past the corners, so the radius needs no clipping. */
.sheet {
    background: var(--glass-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--surface-radius-primary);
    box-shadow: var(--glass-shadow);
}
/* An open dropdown lifts its sheet above the next surface down the page. */
.sheet:has(.custom-select.open),
.sheet:has(.tooltip-trigger:hover),
.sheet:has(.tooltip-trigger:focus-visible) {
    position: relative;
    z-index: var(--z-sticky);
}

.row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 24px;
    min-height: 58px;
    padding: 12px 18px;
}
.row + .row { border-top: 1px solid var(--border-subtle); }
.row-text { min-width: 0; }
.row-label { font-size: var(--text-base); color: var(--text-primary); }
.row-hint {
    display: block;
    margin-top: 3px;
    font-size: var(--text-xs);
    color: var(--text-tertiary);
}
/* Links in hints keep the hint's voice; no browser blue. */
.row-hint a {
    color: var(--text-secondary);
    text-decoration: underline;
    text-decoration-color: rgba(255, 255, 255, 0.22);
    text-underline-offset: 2px;
    transition: color var(--duration-base);
}
.row-hint a:hover { color: var(--text-bright); }
.row-value {
    justify-self: end;
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}
/* A row whose control needs the full width sits under its own label. */
.row-stacked { display: block; }
/* .row and .row-stacked both set display, which would otherwise beat [hidden]. */
.row[hidden] { display: none; }
.row-stacked .row-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
}
.row-stacked .row-body { margin-top: 10px; }

/* The one large numeral on the page, beside the control that changes it. */
.value-numeral {
    display: flex;
    align-items: center;
    gap: 7px;
    font-variant-numeric: tabular-nums;
}
/* A number you can type into is a control, so it is drawn as one: the same
   fill, stroke and lip every other control wears at rest. It used to be bare
   text with a border that stayed transparent until focus, which meant it
   looked identical to .value-readout, the numbers you cannot edit, and gave no
   signal at rest or on hover that there was anything to click. Titan's
   slider-value-input has been this shape all along. */
.value-numeral input {
    height: var(--control-height-md);
    width: 68px;
    padding: 0;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-pill);
    background: var(--glass-control);
    box-shadow: var(--edge-highlight-subtle);
    /* Titan tints its values with the accent. Blue at that alpha reads as
       information; crimson would be the third red thing in the same row, next
       to the track fill and the stage ring, and would read as an alarm. */
    color: var(--text-bright);
    font-family: inherit;
    font-size: var(--text-base);
    font-weight: 600;
    text-align: center;
    font-variant-numeric: tabular-nums;
    transition: background-color var(--duration-base), border-color var(--duration-base);
}
.value-numeral input:hover:not(:focus) {
    background: var(--glass-control-hover);
    border-color: var(--glass-border-strong);
}
/* Focus with the pointer away: still engaged, stated in white. */
.value-numeral input:focus {
    outline: none;
    background: rgba(255, 255, 255, 0.1);
    border-color: rgba(255, 255, 255, 0.25);
}
/* Focus with the pointer on it: actively here. */
.value-numeral input:focus:hover { border-color: var(--accent); }
.value-numeral .unit {
    font-size: var(--text-xs);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-tertiary);
}
/* A symbol used to be set bright and flush, because it sat against a bare
   numeral and belonged to it. The numeral is inside a pill now, so the symbol
   is outside the value rather than part of it, and it joins the unit family:
   same tertiary ink as DPI and mm. It keeps its own size because a degree sign
   set at the unit size is a speck, and it drops the tracking and the uppercase,
   which do nothing to a mark that has no case. */
.value-numeral.symbol { gap: 3px; }
.value-numeral.symbol .unit {
    font-size: var(--text-md);
    letter-spacing: 0;
    text-transform: none;
}
.value-readout {
    font-size: var(--text-md);
    color: var(--text-bright);
    font-variant-numeric: tabular-nums;
}
.row-unit {
    font-size: var(--text-xs);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin-left: 4px;
}

/* Stage rail: the whole DPI stage set as one line of chips. */
.stage-rail { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.stage-chip {
    position: relative;
    min-width: 56px;
    /* Was asymmetric to clear the colour bar along the floor. The bar is gone. */
    padding: 7px 10px;
    border-radius: var(--radius-md);
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    box-shadow: var(--edge-highlight-subtle);
    color: var(--text-secondary);
    cursor: pointer;
    text-align: left;
    transition: background-color var(--duration-quick), border-color var(--duration-quick),
                color var(--duration-quick), transform var(--duration-quick) var(--ease-ios);
}
.stage-chip:hover { background: var(--glass-control-hover); color: var(--text-primary); }
.stage-chip:active { transform: scale(var(--press-subtle)); }
.stage-chip.active {
    border-color: rgba(var(--accent-rgb), 0.5);
    color: var(--text-bright);
    box-shadow: var(--active-state);
}
.stage-chip-index {
    display: block;
    font-size: var(--text-caption);
    letter-spacing: 0.12em;
    color: var(--text-tertiary);
}
/* The stage's indicator colour, as a token beside its number rather than a bar
   along its edge. Drive uses a bar to say "this is the mode you chose"; the
   same mark cannot also mean "this is the colour the mouse will show". A dot is
   a different kind of mark: points label, edges state. */
.stage-chip-index::before {
    content: '';
    display: inline-block;
    width: 5px; height: 5px;
    margin-right: 5px;
    vertical-align: 1px;
    border-radius: 50%;
    background: var(--stage-color, transparent);
    opacity: 0.5;
    transition: opacity var(--duration-quick);
}
.stage-chip.active .stage-chip-index::before { opacity: 1; }
/* The ring already says "active". Coloring the label too would be a third
   signal on one chip. */
.stage-chip.active .stage-chip-index { color: var(--text-secondary); }
.stage-chip-dpi {
    display: block;
    margin-top: 1px;
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
}

.rail-actions { display: flex; gap: 6px; }

/* A glyph where a sentence used to be. Same target size, a fraction of the ink. */
.icon-btn {
    display: grid;
    place-items: center;
    width: 28px; height: 28px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text-tertiary);
    cursor: pointer;
    transition: background-color var(--duration-quick), color var(--duration-quick),
                border-color var(--duration-quick), transform var(--duration-quick) var(--ease-ios);
}
.icon-btn svg { width: 15px; height: 15px; }
.icon-btn:hover:not(:disabled) { background: var(--glass-control-hover); color: var(--text-bright); }
.icon-btn:active:not(:disabled) { transform: scale(var(--press-deep)); }
.icon-btn:disabled { opacity: 0.35; cursor: default; }

/* Linked axes carry the connecting bar; broken ones drop it and go crimson. */
.axis-link { margin-right: 2px; color: var(--text-secondary); }
.axis-link.broken .link-bar { opacity: 0; }
.axis-link .link-bar { transition: opacity var(--duration-quick); }

/* A hint with nothing to say takes no room. */
.row-hint:empty { display: none; }

@media (max-width: 760px) {
    .row { grid-template-columns: 1fr; gap: 12px; }
    .row-value { justify-self: start; }
}

/* =============================================================================
   14b. DRIVE — one selector the whole latency cluster hangs off
   ============================================================================= */
/* Weighted like a drive-mode selector, not like a row of settings: it is the
   loudest object on the page because it is the only one most people will use. */
.drive { display: flex; flex-direction: column; }

/* The dial is one instrument: rail, expansion and pull share a surface, so
   opening it reads as the card unfolding rather than a second card arriving. */
.drive {
    background: var(--glass-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--surface-radius-primary);
    box-shadow: var(--glass-shadow);
}
/* Open menus and tooltips inside the expansion must clear the next surface. */
.drive:has(.custom-select.open),
.drive:has(.tooltip-trigger:hover),
.drive:has(.tooltip-trigger:focus-visible) {
    position: relative;
    z-index: var(--z-sticky);
}
.drive-rail {
    position: relative;
    display: flex;
    overflow: hidden;
    border-radius: var(--surface-radius-primary) var(--surface-radius-primary) 0 0;
}
.drive-seg {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 14px 8px 16px;
    border: 0;
    background: none;
    cursor: pointer;
    transition: background-color var(--duration-base), transform var(--duration-quick) var(--ease-ios);
}
.drive-seg + .drive-seg::before {
    content: '';
    position: absolute;
    left: 0; top: 24%; bottom: 24%;
    border-left: 1px solid var(--border-subtle);
}
/* One instrument, read at a different position for each Drive mode. */
.drive-icon {
    width: 22px;
    height: 22px;
    margin-bottom: 2px;
    color: var(--text-tertiary);
    transition: color var(--duration-base), transform var(--duration-base) var(--ease-ios);
}
.drive-seg:hover:not(:disabled) .drive-icon { color: var(--text-secondary); }
.drive-seg.active .drive-icon { color: var(--accent); }

.drive-name {
    font-size: var(--text-md);
    font-weight: 500;
    color: var(--text-secondary);
    transition: color var(--duration-base);
}
.drive-spec {
    font-size: var(--text-xs);
    letter-spacing: 0.07em;
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
    transition: color var(--duration-base);
}
.drive-seg:hover:not(:disabled) { background: rgba(255, 255, 255, 0.03); }
.drive-seg:hover:not(:disabled) .drive-name { color: var(--text-primary); }
.drive-seg:active:not(:disabled) { transform: scale(var(--press-subtle)); }
.drive-seg.unavailable { cursor: default; opacity: 0.32; }
.drive-seg.active .drive-name { color: var(--text-bright); }
.drive-seg.active .drive-spec { color: rgba(var(--accent-rgb), 0.85); }
/* Light spilling up from the floor of the chosen segment. */
.drive-seg.active::after {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse 76% 116% at 50% 120%, rgba(var(--accent-rgb), 0.2), transparent 68%);
    pointer-events: none;
}
/* Equal segments, so the bar is placed by index and needs no measuring. */
.drive-indicator {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 2px;
    width: calc(100% / var(--drive-n, 4));
    background: var(--accent);
    box-shadow: 0 0 12px rgba(var(--accent-rgb), 0.6);
    transform: translateX(calc(var(--drive-i, 0) * 100%));
    transition: transform var(--duration-base) var(--ease-ios), opacity var(--duration-quick);
}
.drive-rail.is-custom .drive-indicator { opacity: 0; }

.dial-expand { border-top: 1px solid var(--border-subtle); }
.dial-expand[hidden] { display: none; }
.drive-line {
    margin: 0;
    padding: 13px 18px;
    border-bottom: 1px solid var(--border-subtle);
    font-size: var(--text-sm);
    line-height: 1.5;
    color: var(--text-tertiary);
}
/* The pull: a wordless handle on the card's bottom edge. */
/* At rest the pull whispers: a handle should be findable, not calling. It
   wakes when the pointer is anywhere on the card, and speaks only under it. */
.dial-pull {
    position: relative;
    display: grid;
    place-items: center;
    width: 100%;
    padding: 7px 0 8px;
    border: 0;
    border-top: 1px solid var(--border-subtle);
    border-radius: 0 0 var(--surface-radius-primary) var(--surface-radius-primary);
    background: none;
    color: rgba(255, 255, 255, 0.22);
    cursor: pointer;
    transition: color var(--duration-base), background-color var(--duration-quick);
}
.drive:hover .dial-pull,
.dial-pull.open,
.dial-pull:focus-visible { color: var(--text-tertiary); }
.dial-pull:hover { color: var(--text-primary); background: rgba(255, 255, 255, 0.03); }
.drive-chevron {
    width: 12px; height: 12px;
    transition: transform var(--duration-base) var(--ease-ios);
}
.dial-pull.open .drive-chevron { transform: rotate(180deg); }
/* The word for a dial in no preset, on the bar rather than beside the chevron:
   sharing the centre would shove the handle sideways by half a badge every time
   the settings stopped matching, and a handle that moves is no longer a handle.
   Left edge lines up with the prose inside the expansion it opens. */
.dial-badge {
    position: absolute;
    left: 18px;
    top: 50%;
    transform: translateY(-50%);
}

@media (max-width: 620px) {
    .drive-rail { flex-wrap: wrap; }
    .drive-seg { flex-basis: 50%; }
    .drive-indicator { display: none; }
}

/* =============================================================================
   15. SHARED PULSE
   ============================================================================= */
@keyframes pulse-glow {
    0%, 100% { opacity: 0.5; transform: scale(1); }
    50% { opacity: 1; transform: scale(1.15); }
}

/* =============================================================================
   16. DPI STAGES
   ============================================================================= */
/* One badge, wherever a surface has to name its own state: the active profile,
   a dial with no preset matching. A second look for the same job would read as
   two different kinds of fact. */
.badge {
    font-size: var(--text-caption); font-weight: 600;
    color: var(--accent);
    text-transform: uppercase; letter-spacing: 0.1em;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    background: rgba(var(--accent-rgb), 0.14);
}

/* =============================================================================
   17. ANGLE DIAL
   ============================================================================= */

/* =============================================================================
   18. BUTTONS TAB — ASSIGNMENT ROWS
   ============================================================================= */
/* Sheet rows. Pointing at one lights the matching button on the product. */
.assignment-row {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 24px;
    min-height: 58px;
    padding: 12px 18px;
    transition: background-color var(--duration-base);
}
.assignment-row + .assignment-row,
.button-assignments + .row { border-top: 1px solid var(--border-subtle); }
.assignment-row.linked { background: rgba(255, 255, 255, 0.035); }
/* The first row's tint must follow the sheet's rounded corners. */
.button-assignments:first-child .assignment-row:first-child {
    border-top-left-radius: calc(var(--surface-radius-primary) - 1px);
    border-top-right-radius: calc(var(--surface-radius-primary) - 1px);
}
.assignment-label { font-size: var(--text-base); color: var(--text-primary); }
.assignment-row .custom-select { width: 220px; }

/* =============================================================================
   19. MACROS
   ============================================================================= */
.macros-list { display: flex; flex-direction: column; gap: 8px; }
.macro-item {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 12px 14px;
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--edge-highlight-subtle);
    cursor: pointer;
    transition: background-color var(--duration-base), border-color var(--duration-base), box-shadow var(--duration-base);
}
.macro-item:hover { background: var(--glass-control-hover); border-color: var(--glass-border-strong); }
.macro-item.selected { border-color: rgba(var(--accent-rgb), 0.4); box-shadow: var(--active-state); }
.macro-info { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.macro-name { font-size: var(--text-base); font-weight: 500; color: var(--text-bright); }
.macro-description { font-size: var(--text-xs); color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.macro-actions { display: flex; gap: 8px; flex-shrink: 0; }

.macros-list-empty, .macro-editor-empty {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 14px; padding: 40px 20px; text-align: center;
    color: var(--text-tertiary);
}
.macros-list-empty-icon, .macro-editor-empty svg { color: var(--text-tertiary); opacity: 0.5; }
.macros-list-empty-text { font-size: var(--text-sm); line-height: 1.5; }

.macro-editor-panel { min-height: 320px; }
.macro-name-input-wrapper { margin-bottom: 16px; }
.macro-actions-list { display: flex; flex-direction: column; gap: 10px; }
.macro-actions-header { display: flex; align-items: center; justify-content: space-between; }
.macro-action-buttons { display: flex; gap: 8px; }
.macro-steps { display: flex; flex-direction: column; gap: 6px; min-height: 60px; }
.macro-steps-empty { padding: 20px; text-align: center; font-size: var(--text-sm); color: var(--text-tertiary); background: var(--bg-input); border-radius: var(--radius-md); }
.macro-step {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 12px;
    background: var(--bg-input);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25);
}
.macro-step-icon { font-size: var(--text-md); opacity: 0.8; }
.macro-step-content { flex: 1; font-size: var(--text-sm); color: var(--text-primary); }
.macro-step-remove {
    width: 22px; height: 22px; border: none; background: transparent;
    color: var(--text-tertiary); cursor: pointer; border-radius: var(--radius-xs); font-size: 16px;
    transition: background-color var(--duration-micro), color var(--duration-micro);
}
.macro-step-remove:hover { background: rgba(var(--accent-rgb), 0.16); color: var(--accent); }
.macro-options { display: flex; flex-direction: column; gap: 4px; margin: 16px 0; padding: 12px 0; border-top: 1px solid rgba(255, 255, 255, 0.06); border-bottom: 1px solid rgba(255, 255, 255, 0.06); }
.macro-editor-footer { display: flex; gap: 10px; justify-content: flex-end; }
.record-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); margin-right: 4px; }
#btn-record-macro.recording .record-dot { animation: pulse-glow 1s var(--ease-balanced) infinite; }

/* =============================================================================
   21. GRIDS
   ============================================================================= */
/* The work column is one measure wide; the macro list and editor stack in it. */
.macros-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
    align-items: start;
    /* The measure was described here and never set, so Macros alone ran the
       full width and reached into the gutter the product sits in. */
    width: 100%;
    max-width: 720px;
    margin: 0 auto;
}

/* =============================================================================
   23. MODALS
   ============================================================================= */
.modal-overlay {
    position: fixed; inset: 0;
    background-color: var(--glass-scrim);
    z-index: var(--z-modal);
    opacity: 0; pointer-events: none;
    transition: opacity var(--motion-modal-duration) var(--ease-ios);
}
.modal-overlay.show {
    opacity: 1; pointer-events: auto;
    -webkit-backdrop-filter: var(--overlay-blur); backdrop-filter: var(--overlay-blur);
}
.modal {
    position: fixed; top: 50%; left: 50%;
    transform: translate(-50%, calc(-50% + var(--motion-modal-distance)));
    width: 90%; max-width: 460px;
    background: var(--overlay-surface-solid);
    border: 1px solid var(--overlay-border);
    border-radius: var(--surface-radius-primary);
    box-shadow: var(--overlay-shadow-strong);
    padding: var(--surface-padding-lg);
    z-index: var(--z-modal-content);
    display: flex; flex-direction: column; gap: 16px;
    opacity: 0; pointer-events: none;
    transition: opacity var(--motion-modal-duration) var(--ease-ios), transform var(--motion-modal-duration) var(--ease-ios);
}
.modal.show { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%); }
.modal.modal-wide { max-width: 560px; }
.modal-header { display: flex; align-items: center; justify-content: space-between; }
.modal-title { font-size: var(--text-title); font-weight: 600; color: var(--text-bright); }
.modal-close {
    width: 30px; height: 30px; border: none; background: var(--glass-control);
    border: 1px solid var(--glass-border);
    color: var(--text-tertiary); border-radius: var(--radius-pill);
    font-size: 18px; line-height: 1; cursor: pointer;
    box-shadow: var(--edge-highlight-subtle);
    transition: background-color var(--duration-base), color var(--duration-base), border-color var(--duration-base);
}
.modal-close:hover { background: var(--glass-control-hover); color: var(--text-bright); border-color: var(--glass-border-strong); }
.modal-close:active { transform: scale(var(--press-deep)); }
.modal-body { display: flex; flex-direction: column; gap: 12px; color: var(--text-secondary); font-size: var(--text-base); line-height: 1.5; }
.modal-footer { display: flex; gap: 10px; justify-content: flex-end; }
.modal-instruction { font-size: var(--text-base); color: var(--text-secondary); }

.key-capture-display {
    display: grid; place-items: center;
    min-height: 72px;
    background: var(--bg-input);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.35);
    transition: border-color var(--duration-base), box-shadow var(--duration-base);
}
.key-capture-display.active { border-color: rgba(var(--accent-rgb), 0.5); box-shadow: var(--active-state); }
.key-placeholder { color: var(--text-tertiary); font-size: var(--text-sm); }
.captured-key { font-family: var(--font-mono); font-size: var(--text-xl); font-weight: 600; color: var(--text-bright); }

.action-type-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.action-type-btn {
    display: flex; flex-direction: column; align-items: center; gap: 10px;
    padding: 20px;
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    color: var(--text-secondary);
    font-size: var(--text-sm); font-weight: 500;
    cursor: pointer;
    box-shadow: var(--edge-highlight-subtle);
    transition: background-color var(--duration-base), border-color var(--duration-base), color var(--duration-base);
}
.action-type-btn:hover { background: var(--glass-control-hover); border-color: var(--glass-border-strong); color: var(--text-bright); }
.action-type-btn:hover svg { color: var(--accent); }
.action-type-btn:active { transform: scale(var(--press-subtle)); }
.action-type-btn svg { color: var(--text-tertiary); transition: color var(--duration-base); }

/* =============================================================================
   24. TOASTS
   ============================================================================= */
.toast-container {
    position: fixed; bottom: 24px; right: 24px;
    z-index: var(--z-toast);
    display: flex; flex-direction: column; align-items: flex-end;
    gap: 10px;
    pointer-events: none;
}
.toast {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; column-gap: 12px;
    width: fit-content; max-width: min(360px, calc(100vw - 48px));
    padding: 12px 16px;
    background: var(--glass-surface-strong);
    background-blend-mode: luminosity;
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-pill);
    box-shadow: var(--glass-shadow);
    font-size: var(--text-base); font-weight: 500; color: var(--text-bright);
    pointer-events: auto;
    opacity: 0;
    transform: translateY(var(--motion-float-distance)) scale(var(--motion-float-scale));
    transition:
        transform var(--motion-float-duration) var(--ease-ios),
        opacity var(--motion-float-duration) var(--ease-ios),
        border-color var(--duration-base) ease;
}
.toast.show { opacity: 1; transform: translateY(0) scale(1); }
.toast-icon { display: grid; place-items: center; width: 18px; height: 18px; font-size: 13px; flex-shrink: 0; }
.toast-message { min-width: 0; line-height: 1.4; letter-spacing: 0.01em; word-break: break-word; }
.toast-close {
    width: 22px; height: 22px; border: none; background: transparent;
    color: rgba(255, 255, 255, 0.34); border-radius: 50%; cursor: pointer; font-size: 15px; line-height: 1;
    transition: color var(--duration-base), background-color var(--duration-base);
}
.toast-close:hover { color: var(--text-bright); background: rgba(255, 255, 255, 0.06); }
.toast-close:active { transform: scale(var(--press-deep)); }
/* The class is `toast-<type>`, which is what showToast writes. These read
   `.toast.success` for a long time and so never matched anything. */
.toast.toast-success { border-color: rgba(var(--accent-rgb), 0.30); }
.toast.toast-success .toast-icon { color: var(--accent); }
.toast.toast-error { border-color: rgba(var(--accent-rgb), 0.40); }
.toast.toast-error .toast-icon { color: var(--accent); }
.toast.toast-warning { border-color: rgba(var(--accent-warning-rgb), 0.35); }
.toast.toast-warning .toast-icon { color: var(--accent-warning); }
.toast.toast-info .toast-icon { color: var(--text-secondary); }

/* =============================================================================
   25. TOOLTIPS (inline, pure-CSS hover/focus — ported from Titan)
   ============================================================================= */
/* The floating hover tip: same material as the inline tooltips, one element for
   the whole app, replacing the OS title balloon everywhere. */
.hover-tip {
    position: fixed;
    max-width: 250px;
    padding: 8px 11px;
    background: var(--tooltip-surface);
    border: 1px solid var(--tooltip-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--tooltip-shadow);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 1.45;
    text-align: left;
    z-index: var(--z-tooltip);
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--duration-quick) var(--ease-ios);
}
.hover-tip.show { opacity: 1; }
.hover-tip::after {
    content: '';
    position: absolute;
    left: var(--arrow-x, 50%);
    transform: translateX(-50%);
    border: 5px solid transparent;
}
.hover-tip.below::after { bottom: 100%; border-bottom-color: var(--tooltip-surface); }
.hover-tip.above::after { top: 100%; border-top-color: var(--tooltip-surface); }

.tooltip-container { position: relative; display: inline-flex; align-items: center; }
.tooltip-trigger {
    display: inline-flex; align-items: center;
    color: var(--text-tertiary);
    cursor: help;
    transition: color var(--duration-base);
}
.tooltip-trigger:hover, .tooltip-trigger:focus-visible { color: var(--text-primary); outline: none; }
.tooltip-trigger svg { width: 14px; height: 14px; }
/* Downward, always. The work column clips its own overflow, and the rows most
   likely to carry a tooltip are the ones nearest the top of it, where upward
   had nowhere to go. */
.tooltip-content {
    display: none;
    position: absolute;
    top: calc(100% + 10px);
    left: 50%;
    transform: translateX(-50%);
    width: 220px;
    padding: 10px 12px;
    background: var(--tooltip-surface);
    border: 1px solid var(--tooltip-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--tooltip-shadow);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    font-weight: 400;
    line-height: 1.45;
    letter-spacing: 0;
    text-align: left;
    white-space: normal;
    z-index: var(--z-tooltip);
    pointer-events: none;
}
.tooltip-content::after {
    content: ''; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
    border: 5px solid transparent; border-bottom-color: var(--tooltip-surface);
}
.tooltip-trigger:hover + .tooltip-content,
.tooltip-trigger:focus-visible + .tooltip-content {
    display: block;
    animation: tooltip-fade var(--duration-base) var(--ease-ios);
}
@keyframes tooltip-fade { from { opacity: 0; transform: translateX(-50%) translateY(-3px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }

/* =============================================================================
   27. CONNECT BLOOM (one-shot on connect, carried by the status gem)
   ============================================================================= */
.status-gem.just-connected .status-gem-dot { animation: connect-bloom var(--duration-emphasis) var(--ease-ios); }
@keyframes connect-bloom {
    0% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
    50% { box-shadow: 0 0 14px 3px rgba(var(--accent-rgb), 0.6); }
    100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
}

/* =============================================================================
   28. OUT OF SERVICE
   ============================================================================= */

/* A panel or row the device cannot honour right now: legible, clearly not live.
   This is only the appearance. What actually removes it is the `inert` set
   alongside the class by setOff() in kunai.js, because pointer-events stops a
   mouse and nothing else. */
.section-off > *:not(.panel-header),
.control-off {
    opacity: 0.42;
    pointer-events: none;
    filter: saturate(0.6);
    transition: opacity var(--duration-base) var(--ease-ios);
}

/* =============================================================================
   29. BATTERY (detail on the Device tab; the vitals line carries the number)
   ============================================================================= */
.battery-large { display: flex; align-items: center; gap: 16px; }
.battery-large-shell {
    position: relative; flex: 1; height: 12px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), inset 0 2px 4px -2px rgba(0, 0, 0, 0.4);
}
.battery-large-fill {
    height: 100%; border-radius: var(--radius-pill);
    background: linear-gradient(90deg, rgba(255, 255, 255, 0.85), var(--text-bright));
    transition: width var(--duration-settle) var(--ease-ios);
}
#panel-battery.low .battery-large-fill { background: linear-gradient(90deg, var(--accent-pressed), var(--accent)); }
.battery-large-note { font-size: var(--text-xs); color: var(--text-tertiary); }

/* =============================================================================
   30. SLIDER ROWS AND SEGMENTED GROUPS
   ============================================================================= */
.radio-option.unavailable { opacity: 0.3; }
.setting-control.grow { flex: 1; display: flex; align-items: center; gap: 12px; min-width: 0; }
.setting-control.grow input[type="range"] { flex: 1; }
.setting-suffix.mono { font-family: var(--font-mono); min-width: 2ch; text-align: right; }
.number-input.tiny { width: 66px; }
.number-input.wide { width: 108px; }

/* =============================================================================
   31. COLOUR SWATCHES
   ============================================================================= */
.swatch { position: relative; display: inline-flex; cursor: pointer; }
.swatch input[type="color"] {
    position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%;
    border: none; padding: 0; cursor: pointer;
}
.swatch-face {
    display: block; width: 28px; height: 28px;
    border-radius: var(--radius-sm);
    border: 1px solid rgba(255, 255, 255, 0.22);
    box-shadow: var(--edge-highlight), 0 2px 8px rgba(0, 0, 0, 0.4);
    transition: transform var(--duration-quick) var(--ease-ios), border-color var(--duration-quick);
}
.swatch:hover .swatch-face { border-color: rgba(255, 255, 255, 0.42); transform: scale(1.06); }
.swatch:active .swatch-face { transform: scale(var(--press-standard)); }

.swatch-presets { display: flex; gap: 6px; }
.preset-dot {
    width: 20px; height: 20px; padding: 0;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.18);
    cursor: pointer;
    transition: transform var(--duration-quick) var(--ease-squish), box-shadow var(--duration-quick);
}
.preset-dot:hover { transform: scale(1.18); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.08); }
.preset-dot:active { transform: scale(var(--press-deep)); }

/* =============================================================================
   33. GROUPED SELECT OPTIONS
   ============================================================================= */
.option-group-label {
    padding: 8px 12px 4px;
    font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--text-tertiary);
    pointer-events: none;
}
.option-group-label:not(:first-child) {
    margin-top: 4px;
    border-top: 1px solid var(--border-subtle);
    padding-top: 10px;
}

/* =============================================================================
   34. LIGHT MODE TILES
   ============================================================================= */
.mode-tiles {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
    gap: 10px; margin-bottom: 18px;
}
.mode-tile {
    display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
    padding: 12px;
    border-radius: var(--radius-md);
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    box-shadow: var(--edge-highlight);
    cursor: pointer;
    transition: background-color var(--duration-quick), border-color var(--duration-quick),
                transform var(--duration-quick) var(--ease-ios), box-shadow var(--duration-quick);
}
.mode-tile:hover { background: var(--glass-control-hover); border-color: var(--glass-border-strong); }
.mode-tile:active { transform: scale(var(--press-subtle)); }
.mode-tile.active { border-color: rgba(var(--accent-rgb), 0.55); box-shadow: var(--active-state); }
.mode-swatch {
    width: 100%; height: 26px; border-radius: var(--radius-sm);
    background: var(--tile-color, var(--accent));
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
.mode-tile.mode-0 .mode-swatch { background: #17181b; }
.mode-tile.mode-1 .mode-swatch,
.mode-tile.mode-5 .mode-swatch,
.mode-tile.mode-6 .mode-swatch {
    background: linear-gradient(90deg, #ff333f, #ffd426, #3ddc84, #3d7bff, #a45cff, #ff333f);
}
.mode-tile.mode-2 .mode-swatch { background: linear-gradient(90deg, var(--tile-color, var(--accent)), #0c0c0e); }
.mode-tile.mode-4 .mode-swatch { background: linear-gradient(90deg, #0c0c0e, var(--tile-color, var(--accent)), #0c0c0e); }
.mode-name { font-size: var(--text-xs); color: var(--text-secondary); }
.mode-tile.active .mode-name { color: var(--text-bright); }

/* =============================================================================
   35. RECEIVER STATUS LEDS
   ============================================================================= */
.led-preview {
    display: flex; gap: 14px; justify-content: center;
    padding: 16px; border-radius: var(--radius-md);
    background: linear-gradient(180deg, #101113, #08090a);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), inset 0 8px 18px -10px rgba(0, 0, 0, 0.9);
}
.led {
    width: 12px; height: 12px; border-radius: 50%;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.12);
    transition: background-color var(--duration-base), box-shadow var(--duration-base);
}

/* =============================================================================
   36. SENSITIVITY CURVE
   ============================================================================= */
.curve-wrap {
    position: relative;
    border-radius: var(--radius-md);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), transparent);
    border: 1px solid var(--border-subtle);
    padding: 8px 8px 20px;
}
#dynamic-curve { width: 100%; height: 260px; display: block; touch-action: none; }
.curve-axis-x {
    position: absolute; font-size: var(--text-xs); color: var(--text-tertiary);
    pointer-events: none;
}
.curve-axis-x { bottom: 4px; left: 50%; transform: translateX(-50%); }
.curve-points { display: flex; flex-wrap: wrap; gap: 10px; }
.curve-point {
    display: flex; align-items: center; gap: 6px;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
}
.curve-point-index {
    width: 18px; height: 18px; border-radius: 50%;
    display: grid; place-items: center;
    background: rgba(var(--accent-rgb), 0.16); color: var(--accent);
    font-size: var(--text-caption); font-weight: 700;
}
.curve-point-sep { color: var(--text-tertiary); }

/* =============================================================================
   39. DEVICE INFO LIST
   ============================================================================= */
.info-list {
    margin: 0;
    display: grid; grid-template-columns: max-content minmax(0, 1fr);
    gap: 10px 24px;
}
.info-list dt { font-size: var(--text-xs); color: var(--text-tertiary); }
.info-list dd { margin: 0; font-size: var(--text-sm); color: var(--text-primary); }

/* =============================================================================
   40. PICKER LIST
   ============================================================================= */
.picker-list {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 8px; max-height: 46vh; overflow-y: auto;
}
.picker-item {
    padding: 10px 12px; text-align: left;
    border-radius: var(--radius-sm);
    background: var(--glass-control);
    border: 1px solid var(--glass-border);
    color: var(--text-primary);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: background-color var(--duration-quick), border-color var(--duration-quick), transform var(--duration-quick);
}
.picker-item:hover { background: var(--glass-control-hover); border-color: var(--glass-border-strong); color: var(--text-bright); }
.picker-item:active { transform: scale(var(--press-subtle)); }

/* =============================================================================
   41. NARROW WINDOWS
   ============================================================================= */
@media (max-width: 780px) {
    .status-gem-label { display: none; }
    .status-gem { padding: 6px; }
    /* Six tabs will not fit: let the capsule scroll rather than clip. */
    .mode-orbit {
        max-width: 100%;
        overflow-x: auto;
        scrollbar-width: none;
        justify-content: flex-start;
    }
    .mode-orbit::-webkit-scrollbar { display: none; }
    .tab-button { flex: 0 0 auto; }
}

/* =============================================================================
   42. MISCELLANEOUS
   ============================================================================= */
/* Button map: the rendered product with hotspots over it, not a drawing of it.
   Sizing comes from whichever stage hosts it. */
.button-map-figure { position: relative; width: 100%; }
.button-map-image { display: block; width: 100%; height: auto; opacity: 0.9; }
.hotspot {
    position: absolute;
    transform: translate(-50%, -50%);
    width: 30px; height: 30px;
    display: grid; place-items: center;
    padding: 0; border: 0; background: none; cursor: pointer;
}
.hotspot-dot {
    width: 11px; height: 11px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.24);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
    transition: background-color var(--duration-quick), box-shadow var(--duration-quick),
                transform var(--duration-quick) var(--ease-ios);
}
/* Lit while the pointer is on this button or on its row, and dark again after.
   The link is a sightline between the two halves of one control, not a state. */
.hotspot.linked .hotspot-dot {
    background: var(--accent);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 0 12px rgba(var(--accent-rgb), 0.7);
    transform: scale(1.2);
}

/* Stage colors, quoted here in Lighting where color belongs. */
.stage-color-row { display: flex; gap: 10px; flex-wrap: wrap; }
.stage-color {
    position: relative;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    cursor: pointer;
}
.stage-color input[type="color"] {
    position: absolute; inset: 0 0 auto; height: 30px;
    opacity: 0; border: none; padding: 0; cursor: pointer;
}
.stage-color .swatch-face { width: 30px; height: 30px; }
.stage-color-label { font-size: 10px; color: var(--text-tertiary); }

.key-plus { margin: 0 6px; color: var(--text-tertiary); }
.macro-step-label { font-size: var(--text-sm); color: var(--text-primary); }
.step-count { font-family: var(--font-mono); color: var(--text-tertiary); font-weight: 400; }
