        /* ============ GEOMASK theme tokens ============
           Two sibling themes (Dark / Light) sharing one cyan+violet accent
           system — the dual tone is a deliberate callback to the synth's own
           wave-MASKing concept (translucent overlapping layers), not just
           decoration. Every themed surface reads these custom properties
           rather than hardcoded colors, so the toggle is instant and total. */
        :root, html[data-theme="dark"] {
            --void: #0B0E17;
            --void-tint-1: rgba(70,232,208,0.07);
            --void-tint-2: rgba(180,140,255,0.06);
            --app-bg: #12161F;
            --glass-fill: rgba(255,255,255,0.045);
            --glass-fill-hover: rgba(255,255,255,0.075);
            --glass-fill-strong: rgba(255,255,255,0.09);
            --glass-border: rgba(255,255,255,0.10);
            --glass-border-strong: rgba(255,255,255,0.20);
            --header-grad: linear-gradient(to right, rgba(70,232,208,0.10), rgba(140,170,220,0.06), rgba(180,140,255,0.10));
            --cyan: #46E8D0;
            --cyan-dim: rgba(70,232,208,0.35);
            --cyan-soft: rgba(70,232,208,0.12);
            --violet: #B48CFF;
            --violet-dim: rgba(180,140,255,0.35);
            --violet-soft: rgba(180,140,255,0.12);
            --signal: #EAF3F5;
            --signal-dim: #8A97A8;
            --signal-faint: #56606F;
            --amber: #FFB454;
            --amber-dim: rgba(255,180,84,0.30);
            --amber-soft: rgba(255,180,84,0.10);
            /* GEOSynths brand red — deliberately outside the cyan/violet system so the
               studio panel reads as its own thing rather than another control surface.
               Sampled from the logo, so it belongs to the brand rather than the UI. */
            --geo-red: #ED1C24;
            --geo-red-soft: rgba(237,28,36,0.12);
            --geo-red-glow: rgba(237,28,36,0.45);
            --danger: #FF6B6B;
            --danger-dim: rgba(255,107,107,0.35);
            --danger-soft: rgba(255,107,107,0.12);
            --scope-bg: rgba(0,0,0,0.28);
            --shadow-glass: 0 8px 32px rgba(0,0,0,0.40);
            --shadow-glass-sm: 0 4px 16px rgba(0,0,0,0.30);
            --knob-face-grad: linear-gradient(145deg, rgba(255,255,255,0.09), rgba(255,255,255,0.02));
            --knob-track: rgba(255,255,255,0.09);
            --grid-line: rgba(255,255,255,0.05);
            --scrollbar-thumb: rgba(255,255,255,0.15);
            --scrollbar-track: rgba(255,255,255,0.03);
            --modal-backdrop: rgba(5,7,12,0.65);
            --slot-overlay: rgba(5,7,12,0.88);
        }
        html[data-theme="light"] {
            --void: #DDE3EC;
            --void-tint-1: rgba(14,165,160,0.07);
            --void-tint-2: rgba(139,92,246,0.05);
            --app-bg: #EAEFF5;
            --glass-fill: rgba(255,255,255,0.65);
            --glass-fill-hover: rgba(255,255,255,0.88);
            --glass-fill-strong: rgba(255,255,255,0.92);
            --glass-border: rgba(15,23,42,0.08);
            --glass-border-strong: rgba(15,23,42,0.16);
            --header-grad: linear-gradient(to right, rgba(14,165,160,0.12), rgba(148,163,184,0.08), rgba(124,77,255,0.12));
            --cyan: #0EA5A0;
            --cyan-dim: rgba(14,165,160,0.28);
            --cyan-soft: rgba(14,165,160,0.10);
            --violet: #7C4DFF;
            --violet-dim: rgba(124,77,255,0.28);
            --violet-soft: rgba(124,77,255,0.10);
            --signal: #1E293B;
            --signal-dim: #64748B;
            --signal-faint: #94A3B8;
            --amber: #D97706;
            --amber-dim: rgba(217,119,6,0.28);
            --amber-soft: rgba(217,119,6,0.10);
            --geo-red: #D3121A;
            --geo-red-soft: rgba(211,18,26,0.10);
            --geo-red-glow: rgba(211,18,26,0.30);
            --danger: #DC2626;
            --danger-dim: rgba(220,38,38,0.28);
            --danger-soft: rgba(220,38,38,0.08);
            --scope-bg: rgba(255,255,255,0.45);
            --shadow-glass: 0 8px 32px rgba(15,23,42,0.10);
            --shadow-glass-sm: 0 4px 16px rgba(15,23,42,0.07);
            --knob-face-grad: linear-gradient(145deg, rgba(255,255,255,0.95), rgba(255,255,255,0.55));
            --knob-track: rgba(15,23,42,0.08);
            --grid-line: rgba(15,23,42,0.05);
            --scrollbar-thumb: rgba(15,23,42,0.15);
            --scrollbar-track: rgba(15,23,42,0.03);
            --modal-backdrop: rgba(148,163,184,0.40);
            --slot-overlay: rgba(15,23,42,0.82);
        }

        html, body { background: var(--void); }
        body {
            background:
                radial-gradient(ellipse 1200px 800px at 15% -10%, var(--void-tint-1), transparent 60%),
                radial-gradient(ellipse 1000px 700px at 100% 10%, var(--void-tint-2), transparent 55%),
                var(--void);
            color: var(--signal);
            transition: background-color 0.25s ease, color 0.25s ease;
        }

        ::-webkit-scrollbar { width: 8px; height: 8px; }
        ::-webkit-scrollbar-track { background: var(--scrollbar-track); }
        ::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 4px; }
        ::-webkit-scrollbar-thumb:hover { background: var(--glass-border-strong); }

        .knob-surround { transition: stroke-dashoffset 0.08s ease; }
        .panel-grid { background-color: var(--app-bg); background-image: none; }
        .knob-base { fill: var(--knob-face-grad); }

        /* Reusable glass component classes — every JS-generated panel/card/
           button reaches for these instead of hardcoded Tailwind colors, so
           the whole app reskins from these ~20 rules. */
        .gm-app { background: var(--app-bg); border: 1px solid var(--glass-border); box-shadow: var(--shadow-glass); }
        .gm-glass { background: var(--glass-fill); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border: 1px solid var(--glass-border); }
        .gm-glass-strong { background: var(--glass-fill-strong); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); border: 1px solid var(--glass-border-strong); }
        .gm-panel { background: var(--glass-fill); border: 1px solid var(--glass-border); box-shadow: var(--shadow-glass-sm); transition: border-color 0.15s, background 0.15s; }
        .gm-panel:hover { border-color: var(--glass-border-strong); }
        .gm-text { color: var(--signal); }
        .gm-text-dim { color: var(--signal-dim); }
        .gm-text-faint { color: var(--signal-faint); }
        .gm-border { border-color: var(--glass-border) !important; }
        .gm-scope { background: var(--scope-bg); border: 1px solid var(--glass-border); box-shadow: inset 0 2px 12px rgba(0,0,0,0.25); }
        .gm-btn { background: var(--glass-fill); border: 1px solid var(--glass-border-strong); color: var(--signal); transition: background 0.15s, border-color 0.15s, transform 0.1s; }
        .gm-btn:hover { background: var(--glass-fill-hover); }
        .gm-btn:active { transform: scale(0.97); }
        .gm-btn-accent { background: linear-gradient(135deg, var(--cyan-soft), var(--violet-soft)); border: 1px solid var(--cyan-dim); color: var(--cyan); }
        .gm-btn-accent:hover { background: linear-gradient(135deg, var(--cyan-dim), var(--violet-dim)); }
        .gm-btn-danger { background: var(--amber-soft); border: 1px solid var(--amber-dim); color: var(--amber); }
        .gm-btn-danger:hover { background: var(--amber-dim); }
        .gm-input { background: var(--glass-fill); border: 1px solid var(--glass-border-strong); color: var(--signal); }
        .gm-input:focus { outline: none; border-color: var(--cyan-dim); box-shadow: 0 0 0 3px var(--cyan-soft); }
        .gm-badge-cyan { background: var(--cyan-soft); border: 1px solid var(--cyan-dim); color: var(--cyan); }
        .gm-badge-violet { background: var(--violet-soft); border: 1px solid var(--violet-dim); color: var(--violet); }
        .gm-badge-danger { background: var(--danger-soft); border: 1px solid var(--danger-dim); color: var(--danger); }
        .gm-active { background: linear-gradient(135deg, var(--cyan), var(--violet)); color: var(--void); box-shadow: 0 0 16px var(--cyan-dim); }
        /* A selected Librarian / ghost slot: outlined, not filled, so its name and category stay readable. */
        .gm-selected { box-shadow: 0 0 0 4px var(--cyan), 0 0 16px var(--cyan-dim); }
        .gm-glow-cyan { filter: drop-shadow(0 0 5px var(--cyan)); }
        .gm-glow-violet { filter: drop-shadow(0 0 5px var(--violet)); }
        .gm-header-accent-line { background: linear-gradient(to right, transparent, var(--cyan-dim), var(--violet-dim), transparent); }

        /* Retro LED screen — mimics the synth's own physical display for the active-patch readout,
           deliberately breaking from the app's cyan/violet glass language for this one element
           since it's meant to read as "a real embedded screen", not another glass panel. */
        .led-screen {
            background: #140A0A;
            border: 1px solid rgba(0,0,0,0.5);
            box-shadow: inset 0 2px 6px rgba(0,0,0,0.7), inset 0 0 16px rgba(255,60,60,0.07);
            font-family: 'Fira Code', monospace;
            position: relative;
            overflow: hidden;
        }
        .led-screen::before {
            content: ''; position: absolute; inset: 0; pointer-events: none;
            background: repeating-linear-gradient(0deg, rgba(0,0,0,0.18) 0px, rgba(0,0,0,0.18) 1px, transparent 1px, transparent 2px);
        }
        .led-text { color: #FF4545; text-shadow: 0 0 4px rgba(255,69,69,0.85), 0 0 9px rgba(255,69,69,0.35); }
        .led-text-dim { color: rgba(255,69,69,0.5); text-shadow: 0 0 3px rgba(255,69,69,0.3); }
        .led-text-amber { color: #FFC15E; text-shadow: 0 0 4px rgba(255,193,94,0.85), 0 0 9px rgba(255,193,94,0.35); }
        .led-text-amber-dim { color: rgba(255,193,94,0.55); text-shadow: 0 0 3px rgba(255,193,94,0.3); }

        /* ---- GEOSynths studio panel (Librarian sidebar) ----
           The brand mark is literally a knob: serrated collar, red cap, indicator line
           at twelve o'clock. This app is a knob editor. So the panel's one bold move is
           to treat the logo as a real control — it turns on hover, the way every other
           dial here does, and picks up a glow in the brand's own red. That rotation is
           the only flourish: the mark is already a dense stack of concentric rings, so
           everything around it stays quiet and borrows the app's existing glass,
           type scale, and spacing rather than adding more detail. */
        .geo-promo-dial {
            transition: transform 420ms cubic-bezier(0.22, 1, 0.36, 1), filter 420ms ease;
            transform-origin: 50% 50%;
            filter: drop-shadow(0 2px 6px rgba(0,0,0,0.35));
        }
        /* Scoped to the logo's own link rather than the whole panel: the turn is feedback
           for a clickable control, so it should fire when you're actually over it — not
           when the cursor happens to be down on the Patreon button. */
        .geo-promo-logolink:hover .geo-promo-dial,
        .geo-promo-logolink:focus-visible .geo-promo-dial {
            transform: rotate(24deg);
            filter: drop-shadow(0 0 10px var(--geo-red-glow)) drop-shadow(0 2px 6px rgba(0,0,0,0.35));
        }
        .geo-promo-domain { color: var(--signal-faint); transition: color 160ms ease; }
        .geo-promo-logolink:hover .geo-promo-domain,
        .geo-promo-logolink:focus-visible .geo-promo-domain { color: var(--geo-red); }
        /* Detent ticks around the dial — the travel the logo rotates through, so the
           turn reads as a control moving between positions rather than a spin. */
        .geo-promo-cta {
            background: var(--geo-red); color: #fff;
            transition: filter 160ms ease, transform 160ms ease, box-shadow 160ms ease;
            box-shadow: 0 1px 0 rgba(255,255,255,0.16) inset, 0 2px 10px var(--geo-red-soft);
        }
        .geo-promo-cta:hover { filter: brightness(1.09); transform: translateY(-1px); box-shadow: 0 1px 0 rgba(255,255,255,0.2) inset, 0 4px 14px var(--geo-red-glow); }
        .geo-promo-cta:active { transform: translateY(0); filter: brightness(0.97); }
        .geo-promo-alt {
            background: var(--glass-fill); color: var(--signal-dim);
            border: 1px solid var(--glass-border);
            transition: background 160ms ease, color 160ms ease, border-color 160ms ease;
        }
        .geo-promo-alt:hover { background: var(--glass-fill-hover); color: var(--signal); border-color: var(--glass-border-strong); }
        .geo-promo a:focus-visible { outline: 2px solid var(--geo-red); outline-offset: 2px; }
        @media (prefers-reduced-motion: reduce) {
            .geo-promo-dial, .geo-promo-cta { transition: none; }
            .geo-promo-logolink:hover .geo-promo-dial, .geo-promo-logolink:focus-visible .geo-promo-dial { transform: none; }
            .geo-promo-cta:hover { transform: none; }
        }

/* Librarian / ghost bank slots (4 per row): the hover buttons take no room until shown; then the name area makes room
   for them (the name shortens with an ellipsis instead of running under them). The category tag never grows past
   about half the slot. --actions-w is measured from the slot's own buttons. */
.slot-actions { position: absolute; right: 6px; bottom: 4px; display: flex; gap: 4px; opacity: 0; pointer-events: none; transition: opacity .15s; z-index: 20; }
.group:hover .slot-actions { opacity: 1; pointer-events: auto; }
.group:hover .slot-name-area { padding-right: var(--actions-w, 120px); }
.slot-actions > button { padding-top: 3px !important; padding-bottom: 3px !important; }
.slot-tag { top: 5px !important; max-width: 52%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
