section {
    margin-bottom: 40px;
}

/* ── Skeleton loading placeholders — subtle shimmer, not a text-color flash */
@keyframes skeletonShimmer {
    0%   { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

.skeleton-card {
    background: var(--lux-bg-1, var(--card-bg));
    border: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    border-radius: var(--lux-r-md, var(--radius-md));
    padding: 18px 20px 16px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-height: 140px;
    position: relative;
    overflow: hidden;
    /* keep solid — same flat black/white language as real cards */
}

/* Small corner bracket + left hairline so the skeleton reads as a module
   placeholder, not a blank box. */
.skeleton-card::before {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    background: var(--lux-line, var(--border-color));
    opacity: 0.5;
}
.skeleton-card::after {
    content: '';
    position: absolute;
    top: 8px; right: 8px;
    width: 12px; height: 12px;
    border-top: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    border-right: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    opacity: 0.6;
}

.skeleton-line {
    height: 12px;
    border-radius: 2px;
    background: linear-gradient(90deg,
        var(--lux-bg-2, var(--bg-secondary)) 0%,
        var(--lux-bg-3, var(--border-color)) 50%,
        var(--lux-bg-2, var(--bg-secondary)) 100%);
    background-size: 200% 100%;
    animation: skeletonShimmer 2.2s ease-in-out infinite;
}

.skeleton-line-title {
    width: 55%;
    height: 16px;
}

.skeleton-line-short {
    width: 35%;
}

.skeleton-line-medium {
    width: 72%;
}

.skeleton-actions {
    display: flex;
    gap: 8px;
    margin-top: auto;
    padding-top: 12px;
    border-top: var(--lux-hairline, 1px) dashed var(--lux-line, var(--border-color));
}

.skeleton-btn {
    height: 30px;
    flex: 1;
    border-radius: var(--lux-r-sm, var(--radius-sm));
    background: linear-gradient(90deg,
        var(--lux-bg-2, var(--bg-secondary)) 0%,
        var(--lux-bg-3, var(--border-color)) 50%,
        var(--lux-bg-2, var(--bg-secondary)) 100%);
    background-size: 200% 100%;
    animation: skeletonShimmer 2.2s ease-in-out infinite;
}

.displays-grid,
.devices-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--card-grid-min, 380px), 100%), 1fr));
    gap: var(--card-grid-gap, 14px);
}

.devices-grid > .loading,
.devices-grid > .loading-spinner {
    grid-column: 1 / -1;
}

.add-device-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border: 2px dashed var(--border-color);
    background: var(--card-bg);
    min-height: 160px;
    transition: border-color 0.25s ease, background 0.25s ease, transform 0.2s ease;
}

.add-device-card:hover {
    border-color: var(--primary-color);
    background: color-mix(in srgb, var(--primary-color) 5%, transparent); /* --primary-color tint */
    transform: translateY(-2px);
}

.add-device-icon {
    font-size: 2.5rem;
    font-weight: 400;
    color: var(--text-color);
    line-height: 1;
    transition: color 0.2s;
}

.add-device-card:hover .add-device-icon {
    color: var(--primary-text-color);
}

.add-device-label {
    font-size: 0.85rem;
    color: var(--text-secondary);
    margin-top: 8px;
}

.card {
    --ch: var(--ch-signal, var(--primary-color));  /* channel accent (override per type) */
    background: var(--lux-bg-1, var(--card-bg));
    border: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    border-radius: var(--lux-r-md, var(--radius-md));
    padding: 18px 20px 16px;
    position: relative;
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Channel stripe on left edge — always on at .6 opacity for cards
 * that adopt the modular markup (.mod-card), opt-in for legacy cards.
 *   [data-has-color="1"] → user picked a personal color via the picker
 *   .card-running        → "patched and live" indicator
 *   .mod-card            → ambient channel signal (matches dashboard)
 * Legacy cards without a personal color stay clean to avoid breaking
 * the visual rhythm of feature tabs that haven't migrated yet. Once
 * every create*Card() builder uses wrapCard({ mod }), the .mod-card
 * scope can be dropped and the stripe becomes ambient on every card. */
.card::before {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 3px;
    background: var(--ch);
    box-shadow: 0 0 10px color-mix(in srgb, var(--ch) 40%, transparent);
    pointer-events: none;
    z-index: 1;
    display: none;
    opacity: 0.6;
    transition: opacity 0.2s ease, box-shadow 0.2s ease, width 0.2s ease;
}

.card[data-has-color="1"]::before,
.card.card-running::before,
.card.mod-card::before {
    display: block;
}

.card.mod-card:hover::before {
    opacity: 1;
}

/* Corner bracket — silkscreened panel feel in the top-right */
.card::after {
    content: '';
    position: absolute;
    top: 8px; right: 8px;
    width: 12px; height: 12px;
    border-top: var(--lux-hairline, 1px) solid var(--lux-line-bold, var(--border-color));
    border-right: var(--lux-hairline, 1px) solid var(--lux-line-bold, var(--border-color));
    pointer-events: none;
    opacity: 0.7;
    z-index: 1;
}

.card:hover {
    box-shadow: var(--lux-shadow-rack, 0 8px 24px var(--shadow-color));
    transform: translateY(-2px);
    border-color: var(--lux-line-bold, var(--border-color));
}

/* Channel color variants — cards can opt in via class or data-attr.
   Implicit mappings via attributes the JS already emits (no JS changes
   required). Explicit classes provided as an escape hatch. */
.card[data-card-type="led"],
.card[data-card-type="target"],
.card[data-target-id],
.card.ch-signal  { --ch: var(--ch-signal, var(--primary-color)); }

.card[data-card-type="screen"],
.card[data-card-type="source"],
.card[data-stream-id],
.card.ch-cyan    { --ch: var(--ch-cyan, var(--info-color)); }

.card[data-card-type="audio"],
.card[data-audio-source-id],
.card[data-audio-template-id],
.card.ch-magenta { --ch: var(--ch-magenta, #ff4ade); }

.card[data-card-type="automation"],
.card[data-card-type="scene"],
.card[data-automation-id],
.card[data-scene-id],
.card.ch-violet  { --ch: var(--ch-violet, #8b7eff); }

.card.ch-amber   { --ch: var(--ch-amber, var(--warning-color)); }

.card[data-card-type="offline"],
.card.ch-coral   { --ch: var(--ch-coral, var(--danger-color)); }

/* ── Channel mapping for `.template-card` ──
 * Cards rendered by `wrapCard({ type: 'template-card' })` are used by the
 * Inputs and Integrations tabs (plus a few other CardSection consumers).
 * Many of those use a generic `data-id` attribute, so we scope by the
 * parent section's `data-card-section` instead of relying on a unique
 * data-attr per row. Direct attribute hooks come first for the cards that
 * already carry a domain-specific id.
 */

/* Direct attribute hooks (Inputs tab — known per-domain attrs) */
.template-card[data-stream-id],
.template-card[data-template-id],
.template-card[data-pp-template-id]      { --ch: var(--ch-cyan, var(--info-color)); }

.template-card[data-cspt-id],
.template-card[data-pattern-template-id] { --ch: var(--ch-signal, var(--primary-color)); }

.template-card[data-audio-template-id],
.template-card[data-apt-id]              { --ch: var(--ch-magenta, #ff4ade); }

/* Section-scoped hooks (cards that share `data-id` and need their channel
 * resolved via the surrounding section). Matches `<div class="subtab-section"
 * data-card-section="…">` emitted by `CardSection.render`. */

/* Network / data-input integrations → cyan (input language) */
[data-card-section="ha-sources"]   .template-card[data-id],
[data-card-section="mqtt-sources"] .template-card[data-id],
[data-card-section="weather-sources"] .template-card[data-id],
[data-card-section="value-sources"]   .template-card[data-id] { --ch: var(--ch-cyan, var(--info-color)); }

/* Game integrations → amber (events / surfaces) */
[data-card-section="game-integrations"] .template-card[data-id],
.template-card[data-gi-id] { --ch: var(--ch-amber, var(--warning-color)); }

/* Sync clocks → violet (timing / orchestration, mirrors automation/scenes) */
[data-card-section="sync-clocks"] .template-card[data-id] { --ch: var(--ch-violet, #8b7eff); }

/* HA light targets → signal (output target, mirrors led-targets) */
[data-card-section="ha-light-targets"] .template-card[data-ha-target-id] { --ch: var(--ch-signal, var(--primary-color)); }

/* ── Card glare effect ── */
.card-glare::after,
.template-card.card-glare::after,
.dashboard-target.card-glare::after,
.perf-chart-card.card-glare::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.3s ease;
    background: radial-gradient(
        circle 200px at var(--glare-x, 50%) var(--glare-y, 50%),
        rgba(255, 255, 255, 0.035) 0%,
        transparent 70%
    );
    z-index: 1;
}

.card-glare::after,
.template-card.card-glare::after,
.dashboard-target.card-glare::after,
.perf-chart-card.card-glare::after {
    opacity: 1;
}

[data-theme="light"] .card-glare::after,
[data-theme="light"] .template-card.card-glare::after,
[data-theme="light"] .dashboard-target.card-glare::after,
[data-theme="light"] .perf-chart-card.card-glare::after {
    background: radial-gradient(
        circle 200px at var(--glare-x, 50%) var(--glare-y, 50%),
        rgba(255, 255, 255, 0.12) 0%,
        transparent 70%
    );
}

/* Smaller spotlight for compact dashboard/perf cards */
.dashboard-target.card-glare::after,
.perf-chart-card.card-glare::after {
    background: radial-gradient(
        circle 100px at var(--glare-x, 50%) var(--glare-y, 50%),
        rgba(255, 255, 255, 0.04) 0%,
        transparent 70%
    );
}
[data-theme="light"] .dashboard-target.card-glare::after,
[data-theme="light"] .perf-chart-card.card-glare::after {
    background: radial-gradient(
        circle 100px at var(--glare-x, 50%) var(--glare-y, 50%),
        rgba(255, 255, 255, 0.15) 0%,
        transparent 70%
    );
}

/* ── Running module: channel stripe intensifies + signal-flow strip at the
       bottom edge ("patched and live" indicator). Lightweight replacement
       for the old rotating conic-gradient border — ~1 animated gradient on
       a 2 px line, no GPU layer compositing needed per card.                */

.card-running {
    border-color: color-mix(in srgb, var(--ch) 35%, var(--lux-line, var(--border-color)));
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--ch) 20%, transparent),
        0 6px 20px rgba(0, 0, 0, 0.25);
}

.card-running::before {
    width: 4px;
    box-shadow:
        0 0 14px color-mix(in srgb, var(--ch) 65%, transparent),
        0 0 4px color-mix(in srgb, var(--ch) 80%, transparent);
}

/* Signal-flow strip — running cards replace the corner bracket with a
   moving gradient along the bottom edge (the "patched and live"
   indicator). Idle cards keep the corner bracket. */
.card-running::after {
    top: auto; right: auto;
    left: 4px; bottom: 0;
    width: calc(100% - 4px);
    height: 2px;
    border: none;
    opacity: 0.7;
    background:
        linear-gradient(90deg,
            transparent 0%,
            color-mix(in srgb, var(--ch) 85%, transparent) 50%,
            transparent 100%);
    background-size: 30% 100%;
    background-repeat: no-repeat;
    animation: signalFlow 2.4s linear infinite;
}

@keyframes signalFlow {
    0%   { background-position: -30% 0; }
    100% { background-position: 130% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .card-running::after {
        animation: none;
        background-position: 50% 0;
        background-size: 60% 100%;
    }
}

/* Keep the corner bracket visible when NOT running (default),
   and replace it with the signal flow when running (above).
   No extra work needed — `.card::after` rules below cover this. */

/* ── Card entrance animation ── */
@keyframes cardEnter {
    from { opacity: 0; transform: translateY(12px); }
    to { transform: translateY(0); }
}

.card-enter {
    animation: cardEnter 0.25s ease-out backwards;
}

/* ── Card drag-and-drop reordering ── */

.card-drag-handle {
    position: absolute;
    top: 4px;
    left: 4px;
    width: 18px;
    height: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: grab;
    opacity: 0;
    color: var(--text-secondary);
    font-size: 10px;
    letter-spacing: 1px;
    line-height: 1;
    border-radius: 3px;
    transition: opacity 0.2s ease, background 0.15s ease;
    z-index: 2;
    touch-action: none;
    user-select: none;
}

.card:hover > .card-drag-handle,
.template-card:hover > .card-drag-handle {
    opacity: 0.5;
}

.card-drag-handle:hover {
    opacity: 1 !important;
    background: var(--border-color);
}

.card-drag-handle:active {
    cursor: grabbing;
}

/* Clone floating during drag */
.card-drag-clone {
    position: fixed;
    z-index: var(--z-modal-top);
    pointer-events: none;
    opacity: 0.92;
    transform: scale(1.03) rotate(0.8deg);
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);
    transition: none;
    will-change: left, top;
}

/* Placeholder in grid */
.card-drag-placeholder {
    border: 2px dashed var(--primary-color);
    border-radius: 8px;
    background: color-mix(in srgb, var(--primary-color) 4%, transparent); /* --primary-color tint */
    min-height: 80px;
    transition: none;
}

/* Suppress hover effects globally during drag */
body.cs-drag-active .card,
body.cs-drag-active .template-card,
body.cs-drag-active .add-template-card {
    transition: none !important;
    transform: none !important;
}

body.cs-drag-active .add-template-card {
    pointer-events: none;
    border-color: var(--border-color) !important;
    background: transparent !important;
    box-shadow: none !important;
}

body.cs-drag-active .card-drag-handle {
    opacity: 0 !important;
}

/* Hide drag handles when filter is active or bulk selecting */
.cs-filtering .card-drag-handle,
.cs-selecting .card-drag-handle {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    .card-drag-clone {
        transform: none;
    }
}

.card-tutorial-btn {
    position: absolute;
    bottom: 10px;
    right: 10px;
    background: none;
    border: 1.5px solid var(--border-color);
    color: var(--text-muted, #777);
    font-size: 0.7rem;
    font-weight: bold;
    width: 20px;
    height: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 50%;
    transition: color 0.2s, background 0.2s, border-color 0.2s;
    padding: 0;
}

.card-tutorial-btn:hover {
    border-color: var(--primary-color);
    color: var(--primary-text-color);
}


.card-top-actions {
    position: absolute;
    top: 8px;
    right: 8px;
    display: flex;
    align-items: center;
    gap: 2px;
}

.card-top-actions .card-remove-btn,
.card-top-actions .card-hide-btn {
    position: static;
}

.card-hide-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 2px;
    color: var(--text-secondary);
    opacity: 0;
    transition: opacity 0.15s, color 0.15s;
}
.card:hover .card-hide-btn,
.template-card:hover .card-hide-btn {
    opacity: 0.6;
}
.card-hide-btn:hover {
    opacity: 1 !important;
    color: var(--text-primary);
}
.card-hide-btn .icon {
    width: 14px;
    height: 14px;
}

/* Hidden card dimmed style */
.cs-card-hidden {
    opacity: 0.55;
    border-style: dashed !important;
}

.card-actions .color-picker-wrapper,
.template-card-actions .color-picker-wrapper {
    display: flex;
    align-items: center;
    margin-left: auto;
}

.card-actions .color-picker-popover,
.template-card-actions .color-picker-popover {
    top: auto;
    bottom: calc(100% + 8px);
    left: auto;
    right: 0;
}

.card.cp-elevated,
.template-card.cp-elevated {
    z-index: 10;
}

.card-autostart-btn {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 1rem;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    transition: color 0.2s, background 0.2s;
}

.card-autostart-btn:hover {
    color: var(--warning-color, #ffc107);
    background: rgba(255, 193, 7, 0.1);
}

.card-autostart-btn.active {
    color: var(--warning-color, #ffc107);
}

.card-power-btn {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 1rem;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    transition: color 0.2s, background 0.2s;
}

.card-power-btn:hover {
    color: var(--primary-text-color);
    background: color-mix(in srgb, var(--primary-color) 10%, transparent); /* --primary-color tint */
}

.card-remove-btn {
    position: absolute;
    top: 10px;
    right: 10px;
    background: none;
    border: none;
    color: var(--text-muted);
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    transition: color 0.2s, background 0.2s;
}

.card-remove-btn .icon {
    width: 16px;
    height: 16px;
}

.card-remove-btn:hover {
    color: var(--danger-color);
    background: color-mix(in srgb, var(--danger-color) 10%, transparent); /* --danger-color tint */
}

.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;
    padding-right: 60px;
}

.card-title {
    font-family: var(--font-body, inherit);
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    overflow: hidden;
    color: var(--lux-ink, var(--text-color));
}

.card-title-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.card-title > .icon {
    color: var(--primary-text-color);
    vertical-align: middle;
    margin-right: 6px;
    flex-shrink: 0;
}

.device-url-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 0.68rem;
    font-weight: 500;
    color: var(--lux-ink-dim, var(--text-secondary));
    background: var(--lux-bg-0, var(--border-color));
    border: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    padding: 2px 8px;
    border-radius: 2px;
    letter-spacing: 0.04em;
    font-family: var(--font-mono, monospace);
    text-decoration: none;
    transition: background 0.2s, border-color 0.2s, color 0.2s;
    white-space: nowrap;
    flex-shrink: 1;
    overflow: hidden;
    min-width: 0;
}

.device-url-text {
    overflow: hidden;
    text-overflow: ellipsis;
}

.device-url-badge:hover {
    background: var(--lux-bg-2, var(--text-muted));
    border-color: var(--lux-line-bold, var(--border-color));
    color: var(--lux-ink, var(--text-color));
}

.device-url-icon {
    font-size: 0.6rem;
}

.device-url-badge .icon {
    color: var(--primary-text-color);
}

.card-subtitle {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
    flex-wrap: wrap;
}

.card-meta {
    font-family: var(--font-mono, monospace);
    font-size: 0.7rem;
    color: var(--lux-ink-mute, var(--text-secondary));
    display: inline-flex;
    align-items: center;
    gap: 5px;
    letter-spacing: 0.04em;
}

.card-meta .icon {
    color: var(--primary-text-color);
}

.device-type-badge {
    font-size: 10px;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.15);
    letter-spacing: 0.5px;
}

/* Device discovery */
.discovery-section {
    margin-bottom: 12px;
}
.btn-block {
    width: 100%;
}
.discovery-list {
    max-height: 200px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 8px;
}
.discovery-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 12px;
    border: 1px solid var(--border-color);
    border-radius: 4px;
    background: var(--card-bg);
    cursor: pointer;
    transition: opacity 0.15s;
}
.discovery-item:not(.discovery-item--added):hover {
    opacity: 0.8;
}
.discovery-item--added {
    opacity: 0.5;
    cursor: default;
}
.discovery-item-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.discovery-badge {
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 10px;
    background: var(--border-color);
    white-space: nowrap;
}
.discovery-type-badge {
    font-size: 10px;
    padding: 1px 5px;
    border-radius: 3px;
    background: var(--primary-color);
    color: var(--primary-contrast);
    font-weight: 600;
    vertical-align: middle;
    margin-right: 2px;
}
.modal-divider {
    border: none;
    border-top: 1px solid var(--border-color);
    margin: 12px 0;
}
.discovery-loading {
    display: flex;
    justify-content: center;
    padding: 12px;
}
.discovery-spinner {
    width: 20px;
    height: 20px;
    border: 2px solid var(--border-color);
    border-top-color: var(--primary-color);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

/* OpenRGB zone checkboxes */
.zone-checkbox-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-height: 180px;
    overflow-y: auto;
    padding: 4px 0;
}
.zone-checkbox-list .zone-loading,
.zone-checkbox-list .zone-error {
    font-size: 0.75rem;
    color: var(--text-secondary);
    padding: 4px 0;
}
.zone-checkbox-list .zone-error { color: var(--danger-color, #e53935); }
.zone-checkbox-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 6px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.8125rem;
    transition: background 0.15s;
}
.zone-checkbox-item:hover { background: var(--hover-bg, rgba(255,255,255,0.05)); }
.zone-checkbox-item input[type="checkbox"] { margin: 0; flex-shrink: 0; }
.zone-checkbox-item .zone-led-count {
    margin-left: auto;
    font-size: 11px;
    color: var(--text-secondary);
    white-space: nowrap;
}

.zone-mode-radios {
    display: flex;
    gap: 16px;
}
.zone-mode-option {
    display: flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    font-size: 0.8125rem;
}
.zone-mode-option input[type="radio"] { margin: 0; }

/* Group device child rows */
.group-children-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 280px;
    overflow-y: auto;
    padding: 2px 0;
}
.group-child-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    background: var(--bg-color);
    transition: border-color 0.2s, background 0.15s, box-shadow 0.2s;
}
.group-child-row:hover {
    border-color: color-mix(in srgb, var(--primary-color) 40%, var(--border-color));
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
.group-child-index {
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text-secondary);
    min-width: 20px;
    text-align: center;
    opacity: 0.6;
}
.group-child-device {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    min-width: 0;
    padding: 2px 0;
}
.group-child-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
}
.group-child-icon .icon {
    width: 18px;
    height: 18px;
}
.group-child-name {
    flex: 1;
    font-size: 0.9rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.group-child-meta {
    font-size: 0.75rem;
    color: var(--text-secondary);
    white-space: nowrap;
    flex-shrink: 0;
}
.group-child-empty .group-child-icon {
    opacity: 0.4;
}
.group-child-empty .group-child-name {
    color: var(--text-secondary);
    font-style: italic;
}
.group-child-empty {
    border-style: dashed;
}
.group-child-actions {
    display: flex;
    gap: 2px;
    flex-shrink: 0;
}
.group-child-actions .btn-icon {
    padding: 3px;
    line-height: 1;
}
.group-child-actions .btn-icon .icon {
    width: 14px;
    height: 14px;
}
.group-child-up,
.group-child-down {
    opacity: 0.5;
    transition: opacity 0.15s;
}
.group-child-up:hover,
.group-child-down:hover {
    opacity: 1;
}
.group-child-up:disabled,
.group-child-down:disabled {
    opacity: 0.15;
    cursor: default;
}
.group-child-remove {
    opacity: 0.5;
    transition: opacity 0.15s;
}
.group-child-remove:hover {
    opacity: 1;
}

.zone-badge {
    font-size: 10px;
    padding: 1px 5px;
    border-radius: 3px;
    background: var(--border-color);
    font-weight: 600;
}

.channel-indicator {
    display: inline-flex;
    gap: 2px;
    align-items: center;
}

.channel-indicator .ch {
    width: 8px;
    height: 10px;
    border-radius: 1px;
}

.display-card {
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 15px;
}

.display-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
}

.display-index {
    font-size: 1.3rem;
    font-weight: 700;
    color: var(--info-color);
}

.primary-star {
    color: var(--primary-text-color);
    font-size: 1.2rem;
}

/* Display Layout Visualization */
.layout-container {
    position: relative;
    background: transparent;
}

.layout-display {
    position: absolute;
    border: 3px solid;
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
    transition: box-shadow 0.2s, border-color 0.2s;
}

.layout-display:hover {
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
    z-index: 10;
}

.layout-display.primary {
    border-color: var(--primary-color);
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--primary-color) 15%, transparent), /* --primary-color tint */
        color-mix(in srgb, var(--primary-color) 5%, transparent)   /* --primary-color tint */
    );
}

.layout-display.secondary {
    border-color: var(--border-color);
    background: linear-gradient(135deg, rgba(128, 128, 128, 0.1), rgba(128, 128, 128, 0.05));
}

.layout-position-label {
    position: absolute;
    top: 4px;
    left: 6px;
    font-size: 0.7rem;
    color: var(--text-secondary);
}

.layout-index-label {
    position: absolute;
    bottom: 6px;
    left: 6px;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-color);
    background: var(--display-badge-bg);
    padding: 1px 6px;
    border-radius: 4px;
    letter-spacing: 0.5px;
}

.layout-display-label {
    text-align: center;
    padding: 5px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.layout-display-label strong {
    font-size: 0.9rem;
    color: var(--text-color);
    font-weight: 600;
}

.layout-display-label small {
    font-size: 0.75rem;
    color: var(--text-secondary);
}

.primary-indicator {
    position: absolute;
    top: 2px;
    right: 4px;
    color: var(--primary-text-color);
    font-size: 1.5rem;
    line-height: 1;
    text-shadow: 0 0 4px rgba(0, 0, 0, 0.4);
}


/* Card brightness slider */
.brightness-control {
    padding: 0;
    margin-bottom: 12px;
}

.brightness-slider {
    width: 100%;
}

.brightness-loading .brightness-slider {
    opacity: 0.3;
    pointer-events: none;
}

/* Mod-card brightness fader — loading + disabled states */
.card.mod-card.brightness-loading .mod-fader,
.template-card.mod-card.brightness-loading .mod-fader,
.mod-fader:has(.mod-fader__slider:disabled) {
    opacity: 0.4;
    pointer-events: none;
}

/* Static color picker — inline in card-subtitle */
.section-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 8px;
}

.section-header h2 {
    margin-bottom: 0;
}

.section-tip {
    font-size: 0.82rem;
    color: var(--text-secondary);
    margin: 0 0 15px 0;
    line-height: 1.5;
    padding: 8px 12px;
    background: color-mix(in srgb, var(--info-color, #2196F3) 8%, transparent); /* --info-color tint */
    border-left: 3px solid var(--info-color, #2196F3);
    border-radius: 0 6px 6px 0;
}

.section-tip a {
    color: var(--info-color, #2196F3);
    text-decoration: underline;
}

ul.section-tip {
    list-style: disc;
    padding-left: 28px;
}

ul.section-tip li {
    margin: 2px 0;
}

.metrics-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px 12px;
    margin-top: 8px;
}

.metric {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 3px 8px;
    background: var(--bg-color);
    border-radius: 4px;
}

.metric-value {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--primary-text-color);
    font-family: var(--font-mono, monospace);
}

.metric-label {
    font-size: 0.8rem;
    color: var(--text-secondary);
}

/* Target FPS sparkline row */
.target-fps-row {
    display: flex;
    align-items: center;
    gap: 8px;
    grid-column: 1 / -1;
    padding: 3px 8px;
    background: var(--bg-color);
    border-radius: 4px;
}

.target-fps-sparkline {
    position: relative;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    height: 36px;
}

.target-fps-label {
    flex-shrink: 0;
    text-align: right;
}

.target-fps-target {
    font-weight: 400;
    opacity: 0.5;
    font-size: 0.75rem;
}

.target-fps-avg {
    display: block;
    font-size: 0.65rem;
    font-weight: 400;
    opacity: 0.45;
    line-height: 1.1;
    color: var(--primary-color);
}

.fps-unreachable {
    color: #ff5252;
}

/* Error indicator next to target name */
.target-error-indicator {
    display: none;
    color: var(--danger-color);
    margin-left: 4px;
    vertical-align: middle;
}
.target-error-indicator .icon {
    width: 14px;
    height: 14px;
}
.target-error-indicator.visible {
    display: inline-flex;
    align-items: center;
}

/* Timing breakdown bar — used by graph-editor and the api-input test
   modal; the targets card uses its own .target-pipeline wrapper. */
.timing-breakdown {
    margin-top: 8px;
    padding: 6px 8px;
    background: var(--bg-color);
    border-radius: 4px;
}

.timing-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.timing-total {
    font-size: 0.8rem;
    color: var(--primary-text-color);
}

.timing-bar {
    display: flex;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    margin: 4px 0;
    gap: 1px;
}

.timing-seg {
    min-width: 2px;
    transition: flex 0.3s ease;
}

.timing-extract { background: var(--primary-color); }
.timing-map { background: #FF9800; }
.timing-smooth { background: var(--info-color, #2196F3); }
.timing-send { background: #E91E63; }
.timing-audio-read { background: var(--primary-color); }
.timing-audio-fft { background: #FF9800; }
.timing-audio-render { background: var(--info-color, #2196F3); }

.timing-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 0.75rem;
    color: var(--text-secondary);
    margin-top: 4px;
}

.timing-legend-item {
    display: flex;
    align-items: center;
    gap: 3px;
}

.timing-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 2px;
}

.timing-dot.timing-extract { background: var(--primary-color); }
.timing-dot.timing-map { background: #FF9800; }
.timing-dot.timing-smooth { background: var(--info-color, #2196F3); }
.timing-dot.timing-send { background: #E91E63; }
.timing-dot.timing-audio-read { background: var(--primary-color); }
.timing-dot.timing-audio-fft { background: #FF9800; }
.timing-dot.timing-audio-render { background: var(--info-color, #2196F3); }

@media (max-width: 768px) {
    .displays-grid,
    .devices-grid {
        grid-template-columns: 1fr;
    }
}

/* ── LED Preview Panel ────────────────────────────────────────── */

.led-preview-panel {
    padding: 4px 0 0;
    position: relative;
}

.led-preview-canvas {
    display: block;
    width: 100%;
    height: 32px;
    border-radius: 3px;
    image-rendering: pixelated;
    background: #111;
}

.led-preview-brightness {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.8rem;
    font-family: var(--font-mono, monospace);
    color: #fff;
    text-shadow: 0 0 3px rgba(0,0,0,0.8);
    pointer-events: none;
    opacity: 0.8;
}

/* Per-zone LED preview (OpenRGB separate mode) */
.led-preview-zones {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.led-preview-zone {
    position: relative;
}

.led-preview-zone-canvas {
    display: block;
    width: 100%;
    height: 18px;
    border-radius: 2px;
    image-rendering: pixelated;
    background: #111;
}

.led-preview-zone-label {
    position: absolute;
    left: 4px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.65rem;
    font-family: var(--font-mono, monospace);
    color: #fff;
    text-shadow: 0 0 3px rgba(0,0,0,0.9), 0 0 6px rgba(0,0,0,0.6);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
    white-space: nowrap;
}

.led-preview-zones:hover .led-preview-zone-label {
    opacity: 1;
}

/* Per-layer LED preview (composite sources) */
.led-preview-layers {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.led-preview-layer {
    position: relative;
}

.led-preview-layer-canvas {
    display: block;
    width: 100%;
    height: 24px;
    border-radius: 3px;
    image-rendering: pixelated;
    background: #111;
}

.led-preview-layer-label {
    position: absolute;
    left: 4px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.6rem;
    font-family: var(--font-mono, monospace);
    color: #fff;
    text-shadow: 0 0 3px rgba(0,0,0,0.9), 0 0 6px rgba(0,0,0,0.6);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
    white-space: nowrap;
}

.led-preview-layers:hover .led-preview-layer-label {
    opacity: 1;
}

/* ── Hidden cards toggle ──────────────────────────────────── */

.cs-hidden-toggle {
    background: none;
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    font-size: 0.7rem;
    height: 24px;
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 0 6px;
    cursor: pointer;
    border-radius: 4px;
    transition: color 0.2s, background 0.2s, border-color 0.2s;
    flex-shrink: 0;
}
.cs-hidden-toggle .icon {
    width: 14px;
    height: 14px;
}
.cs-hidden-toggle:hover {
    border-color: var(--primary-color);
    color: var(--text-primary);
}
.cs-hidden-toggle.active {
    background: var(--primary-color);
    border-color: var(--primary-color);
    color: var(--primary-contrast, #fff);
}
.cs-hidden-badge {
    font-size: 0.65rem;
    font-weight: 600;
}

/* ── Bulk selection ────────────────────────────────────────── */

/* Toggle button in section header */
.cs-bulk-toggle {
    background: none;
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    font-size: 0.75rem;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    transition: color 0.2s, background 0.2s, border-color 0.2s;
    flex-shrink: 0;
    line-height: 1;
}

.cs-bulk-toggle:hover {
    border-color: var(--primary-color);
    color: var(--primary-text-color);
}

.cs-bulk-toggle.active {
    background: var(--primary-color);
    border-color: var(--primary-color);
    color: var(--primary-contrast, #fff);
}

/* Checkbox inside card — hidden unless selecting */
.card-bulk-check {
    display: none;
    width: 16px;
    height: 16px;
    cursor: pointer;
    accent-color: var(--primary-color);
    flex-shrink: 0;
}

.cs-selecting .card-bulk-check {
    display: block;
}

/* Selected card highlight — accent ring, soft outer glow, and a subtle
   lift so the chosen card pops above the dimmed siblings. */
.cs-selecting .card-selected,
.cs-selecting .card-selected.template-card {
    border-color: var(--primary-color);
    box-shadow:
        0 0 0 1px var(--primary-color),
        0 0 24px color-mix(in srgb, var(--primary-color) 22%, transparent),
        0 8px 28px rgba(0, 0, 0, 0.35);
    transform: translateY(-1px);
}

/* Non-selected siblings during selection: desaturate, dim and softly blur
   so the eye locks onto the active picks. Hover restores full clarity to
   keep the card affordance obvious. */
.cs-selecting .card:not(.card-selected),
.cs-selecting .template-card:not(.card-selected) {
    opacity: 0.55;
    filter: saturate(0.55) blur(0.4px);
    transition:
        opacity 0.2s ease,
        filter 0.25s ease,
        transform 0.2s ease,
        border-color 0.2s ease,
        box-shadow 0.2s ease;
}

.cs-selecting .card:not(.card-selected):hover,
.cs-selecting .template-card:not(.card-selected):hover {
    opacity: 0.92;
    filter: none;
}

@media (prefers-reduced-motion: reduce) {
    .cs-selecting .card:not(.card-selected),
    .cs-selecting .template-card:not(.card-selected) {
        filter: saturate(0.55);
    }
}

/* Make cards visually clickable in selection mode — card root is the
   click target, descendants are inert. */
.cs-selecting .card,
.cs-selecting .template-card {
    cursor: pointer;
}

/* All card descendants pass clicks through to the card root in
   selection mode. This disables buttons, sliders, kebab, color dot,
   and any other interactive child without needing per-element rules. */
.cs-selecting .card *,
.cs-selecting .template-card * {
    pointer-events: none;
}

/* Suppress hover lift during selection */
.cs-selecting .card:hover,
.cs-selecting .template-card:hover {
    transform: none;
}

/* Corner checkmark on selected cards — replaces the per-card checkbox
   that used to be injected at the top of the card body. Sits in the
   top-right corner where the kebab is otherwise visible (kebab is
   pointer-events:none in this mode, so the indicator can overlay it). */
.cs-selecting .card-selected::before,
.cs-selecting .template-card.card-selected::before {
    /* override the channel stripe pulse by restoring full opacity */
    opacity: 1;
}
.cs-selecting .card-selected,
.cs-selecting .template-card.card-selected {
    position: relative;
}
.cs-selecting .card-selected > .mod-bulk-tick,
.cs-selecting .template-card.card-selected > .mod-bulk-tick {
    display: flex;
}
/* The tick itself — injected once per card via JS so it picks up the
   .card-selected toggle naturally. Hidden until the card is selected. */
.mod-bulk-tick {
    display: none;
    position: absolute;
    top: 8px;
    right: 8px;
    width: 22px;
    height: 22px;
    align-items: center;
    justify-content: center;
    background: var(--primary-color);
    color: var(--primary-contrast, #fff);
    border-radius: 50%;
    box-shadow:
        0 0 0 2px var(--card-bg, var(--lux-bg-1)),
        0 0 14px color-mix(in srgb, var(--primary-color) 50%, transparent);
    z-index: 3;
    pointer-events: none;
}

.mod-bulk-tick svg {
    display: block;
    width: 13px;
    height: 13px;
    stroke-width: 2.6;
}

.cs-selecting .card-selected > .mod-bulk-tick,
.cs-selecting .template-card.card-selected > .mod-bulk-tick {
    animation: bulkTickPop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes bulkTickPop {
    0%   { transform: scale(0.4); opacity: 0; }
    60%  { transform: scale(1.12); opacity: 1; }
    100% { transform: scale(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .cs-selecting .card-selected > .mod-bulk-tick,
    .cs-selecting .template-card.card-selected > .mod-bulk-tick {
        animation: none;
    }
}

/* ── Bulk toolbar ──────────────────────────────────────────── */

#bulk-toolbar {
    --bulk-ch: var(--primary-color);
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%) translateY(calc(100% + 40px));
    background: linear-gradient(180deg,
        var(--lux-bg-1, var(--card-bg)) 0%,
        var(--lux-bg-2, var(--card-bg)) 100%);
    border: var(--lux-hairline, 1px) solid var(--lux-line-bold, var(--border-color));
    border-radius: var(--lux-r-lg, var(--radius-md, 10px));
    padding: 8px 10px 8px 12px;
    display: flex;
    align-items: center;
    gap: 10px;
    z-index: var(--z-bulk-toolbar);
    box-shadow:
        0 0 0 1px rgba(255, 255, 255, 0.02),
        0 12px 36px rgba(0, 0, 0, 0.5),
        0 4px 16px var(--shadow-color, rgba(0, 0, 0, 0.4));
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
}

#bulk-toolbar.visible {
    transform: translateX(-50%) translateY(0);
    opacity: 1;
    pointer-events: auto;
}

/* Top accent stripe — same channel-glow language as modals */
#bulk-toolbar::before {
    content: '';
    position: absolute;
    left: 0; right: 0; top: 0;
    height: 1.5px;
    background: linear-gradient(90deg,
        transparent 0%,
        var(--bulk-ch) 20%,
        var(--bulk-ch) 80%,
        transparent 100%);
    box-shadow: 0 0 12px color-mix(in srgb, var(--bulk-ch) 55%, transparent);
    border-radius: inherit;
    pointer-events: none;
}

/* Pick group (Select all / Deselect all) */
.bulk-pick {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    background: var(--lux-bg-0, var(--bg-color));
    border: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    border-radius: var(--lux-r-sm, 4px);
}

.bulk-pick-btn {
    width: 28px;
    height: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--lux-ink-mute, var(--text-secondary));
    border-radius: var(--lux-r-sm, 3px);
    cursor: pointer;
    transition: color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}

.bulk-pick-btn .icon {
    width: 16px;
    height: 16px;
}

.bulk-pick-btn:hover:not(:disabled) {
    color: var(--bulk-ch);
    background: color-mix(in srgb, var(--bulk-ch) 14%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bulk-ch) 35%, transparent),
                0 0 12px color-mix(in srgb, var(--bulk-ch) 30%, transparent);
}

.bulk-pick-btn:disabled,
.bulk-pick-btn[aria-disabled="true"] {
    opacity: 0.35;
    cursor: not-allowed;
}

.bulk-count {
    font-family: var(--font-mono, monospace);
    font-size: 0.78rem;
    letter-spacing: 0.08em;
    color: var(--lux-ink, var(--text-color));
    min-width: 90px;
    padding: 0 4px;
    font-variant-numeric: tabular-nums;
}

.bulk-count-total {
    color: var(--lux-ink-mute, var(--text-secondary));
    font-size: 0.95em;
    margin-left: 2px;
}

.bulk-actions {
    display: flex;
    gap: 4px;
    padding-left: 8px;
    margin-left: 4px;
    border-left: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
}

.bulk-action-btn {
    width: 32px;
    height: 32px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: box-shadow 0.18s ease, transform 0.15s ease;
}

.bulk-action-btn .icon {
    width: 16px;
    height: 16px;
}

.bulk-action-btn:hover:not(:disabled) {
    box-shadow: 0 0 16px color-mix(in srgb, var(--primary-color) 30%, transparent);
    transform: translateY(-1px);
}

.bulk-action-btn.btn-danger:hover:not(:disabled) {
    box-shadow: 0 0 16px color-mix(in srgb, var(--danger-color, #ef4444) 40%, transparent);
}

.bulk-action-btn:disabled,
.bulk-action-btn[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
}

.bulk-close {
    width: 30px;
    height: 30px;
    background: transparent;
    border: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    color: var(--lux-ink-mute, var(--text-muted));
    cursor: pointer;
    padding: 0;
    border-radius: var(--lux-r-sm, 4px);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: 2px;
    transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}

.bulk-close .icon {
    width: 14px;
    height: 14px;
}

.bulk-close:hover {
    color: var(--lux-ink, var(--text-color));
    border-color: color-mix(in srgb, var(--bulk-ch) 50%, var(--lux-line, var(--border-color)));
    background: color-mix(in srgb, var(--bulk-ch) 8%, transparent);
}

@media (prefers-reduced-motion: reduce) {
    #bulk-toolbar {
        transition: opacity 0.15s ease;
    }
    .bulk-action-btn:hover:not(:disabled) {
        transform: none;
    }
}

/* ════════════════════════════════════════════════════════════════════
   Mod-cards — entity cards that adopt the dashboard's `.mod-*` markup
   ════════════════════════════════════════════════════════════════════
   The `.mod-*` child selectors (mod-head, mod-leds, mod-metrics,
   mod-foot, mod-patch, mod-btn, etc.) live in dashboard.css and apply
   regardless of host class. The rules below adapt the .card /
   .template-card hosts to layout the modular children correctly (gap,
   padding, suppress legacy decorations) and add the kebab menu styles
   that are unique to entity cards.
   ════════════════════════════════════════════════════════════════════ */

.card.mod-card,
.template-card.mod-card {
    /* Vertical flex stack matches .dashboard-target:has(.mod-head) */
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 16px 18px 14px 22px;
    align-items: stretch;
}

/* Foot pinned to the card bottom — entity cards have variable body
   content (chips/metrics/fader may all be absent), so without this the
   foot floats in the middle when the grid forces a tall card. */
.card.mod-card .mod-foot,
.template-card.mod-card .mod-foot {
    margin-top: auto;
}

/* Suppress legacy decorations when modular children are present */
.card.mod-card .card-header,
.card.mod-card .card-actions,
.card.mod-card .card-top-actions,
.template-card.mod-card .template-card-header,
.template-card.mod-card .template-card-actions,
.template-card.mod-card .card-top-actions { display: none; }

/* Idle silkscreen corner bracket — drop on mod-cards with a kebab.
   The kebab visually replaces the bracket as the top-right anchor. */
.card.mod-card:has(.mod-menu-wrap):not(.is-running)::after,
.template-card.mod-card:has(.mod-menu-wrap):not(.is-running)::after {
    display: none;
}

/* Running state on mod-cards — same intensified stripe + bottom signal-
   flow used by .dashboard-target.is-running. Two class names keep
   markup interchangeable: callers can use `is-running` (dashboard
   convention) or `card-running` (legacy entity-card convention). */
.card.mod-card.is-running,
.card.mod-card.card-running,
.template-card.mod-card.is-running,
.template-card.mod-card.card-running {
    border-color: color-mix(in srgb, var(--ch) 32%, var(--lux-line, var(--border-color)));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--ch) 18%, transparent),
                0 6px 20px rgba(0, 0, 0, 0.25);
}

.card.mod-card.is-running::before,
.card.mod-card.card-running::before,
.template-card.mod-card.is-running::before,
.template-card.mod-card.card-running::before {
    opacity: 1;
    width: 4px;
    box-shadow: 0 0 14px color-mix(in srgb, var(--ch) 70%, transparent),
                0 0 4px color-mix(in srgb, var(--ch) 90%, transparent);
}

/* Bottom signal-flow strip (running indicator) */
.card.mod-card.is-running::after,
.card.mod-card.card-running::after,
.template-card.mod-card.is-running::after,
.template-card.mod-card.card-running::after {
    content: '';
    position: absolute;
    top: auto; right: auto;
    left: 4px; bottom: 0;
    width: calc(100% - 4px); height: 2px;
    border: none;
    opacity: 0.7;
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, var(--ch) 85%, transparent) 50%,
        transparent 100%);
    background-size: 30% 100%;
    background-repeat: no-repeat;
    animation: signalFlow 2.4s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    .card.mod-card.is-running::after,
    .card.mod-card.card-running::after,
    .template-card.mod-card.is-running::after,
    .template-card.mod-card.card-running::after {
        animation: none;
        background-position: 50% 0;
        background-size: 60% 100%;
    }
}

/* ── Color picker dot inside .mod-badge ─────────────────────────── */

.mod-badge { padding-left: 4px; }

.mod-badge__color-wrap {
    display: inline-flex;
    align-items: center;
    margin-right: 6px;
    line-height: 0;
    position: relative;
}

.mod-badge__color {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--ch);
    border: var(--lux-hairline, 1px) solid color-mix(in srgb, var(--ch) 60%, var(--lux-line-bold, var(--border-color)));
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--ch) 40%, transparent);
}
.mod-badge__color:hover,
.mod-badge__color:focus-visible {
    transform: scale(1.25);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ch) 25%, transparent);
    outline: none;
}
/* User picked a personal hue — fill with --user-color but keep the
   channel-tinted ring */
.mod-badge__color[data-custom] {
    background: var(--user-color);
}

/* ── Overflow menu (kebab) ──────────────────────────────────────── */

.mod-menu-wrap {
    position: relative;
    flex-shrink: 0;
    align-self: flex-start;
    /* Match the .mod-leds bezel height (~22px) so the kebab and the LED
       cluster sit on the same visual baseline at the top of the head row.
       Without this, the 26px kebab dropped 4px below the 22px bezel and
       read as misaligned. */
    display: inline-flex;
    align-items: center;
    height: 22px;
}

.mod-menu-btn {
    appearance: none;
    background: transparent;
    border: none;
    width: 22px; height: 22px;
    border-radius: 3px;
    color: var(--lux-ink-mute, var(--text-secondary));
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: color 0.15s, background 0.15s, opacity 0.15s;
    opacity: 0.45;
    padding: 0;
}
.card.mod-card:hover .mod-menu-btn,
.template-card.mod-card:hover .mod-menu-btn,
.dashboard-target:hover .mod-menu-btn,
.mod-menu-wrap.is-open .mod-menu-btn {
    opacity: 1;
}
.mod-menu-btn:hover,
.mod-menu-wrap.is-open .mod-menu-btn {
    color: var(--lux-ink, var(--text-color));
    background: var(--lux-bg-3, var(--border-color));
}
.mod-menu-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--ch) 50%, transparent);
}
.mod-menu-btn .icon {
    width: 14px;
    height: 14px;
    /* The kebab path uses fill (filled circles) — disable stroke */
    stroke: none;
    fill: currentColor;
}

/* The dropdown — `position: fixed` with JS-computed coordinates.
 * mod-menu.ts portals the element to <body> on open so that a
 * transformed ancestor (e.g. card hover translate) doesn't trap the
 * fixed positioning, and the card's `overflow: hidden` doesn't clip
 * the dropdown. Display toggles on `.mod-menu.is-open` (set whether
 * the menu is in the wrap or portaled to body). */
.mod-menu {
    position: fixed;
    z-index: var(--z-dropdown, 200);
    min-width: 172px;
    background: var(--lux-bg-1, var(--card-bg));
    border: var(--lux-hairline, 1px) solid var(--lux-line-bold, var(--border-color));
    border-radius: var(--lux-r-sm, 4px);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45),
                0 0 0 1px rgba(255, 255, 255, 0.02);
    padding: 4px;
    display: none;
    flex-direction: column;
    transform-origin: top right;
    animation: modMenuIn 0.12s cubic-bezier(0.16, 1, 0.3, 1);
}
.mod-menu.is-open { display: flex; }

@keyframes modMenuIn {
    from { opacity: 0; transform: scale(0.96) translateY(-4px); }
    to   { opacity: 1; transform: none; }
}

.mod-menu__item {
    appearance: none;
    background: transparent;
    border: none;
    text-align: left;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-radius: 2px;
    font-family: var(--font-mono, monospace);
    font-size: 0.66rem;
    font-weight: 500;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--lux-ink-dim, var(--text-secondary));
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
    white-space: nowrap;
    width: 100%;
}
.mod-menu__item:hover,
.mod-menu__item:focus-visible {
    background: var(--lux-bg-3, var(--border-color));
    color: var(--lux-ink, var(--text-color));
    outline: none;
}
.mod-menu__item .icon {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    color: var(--lux-ink-mute, var(--text-secondary));
}
.mod-menu__item:hover .icon,
.mod-menu__item:focus-visible .icon {
    color: var(--lux-ink, var(--text-color));
}

.mod-menu__sep {
    height: 1px;
    margin: 4px 6px;
    background: var(--lux-line, var(--border-color));
}

.mod-menu__item--danger {
    color: var(--ch-coral, var(--danger-color));
}
.mod-menu__item--danger .icon {
    color: var(--ch-coral, var(--danger-color));
    opacity: 0.85;
}
.mod-menu__item--danger:hover,
.mod-menu__item--danger:focus-visible {
    background: color-mix(in srgb, var(--ch-coral, var(--danger-color)) 12%, transparent);
    color: var(--ch-coral, var(--danger-color));
}
.mod-menu__item--danger:hover .icon,
.mod-menu__item--danger:focus-visible .icon {
    color: var(--ch-coral, var(--danger-color));
    opacity: 1;
}

/* ── Property chips (replaces parallel .card-meta / .stream-card-prop
      systems on mod-cards). Reused by the legacy .stream-card-prop
      class so cards can mix-and-match during migration. ─────────── */

.mod-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.mod-card .chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono, monospace);
    font-size: 0.66rem;
    font-weight: 500;
    letter-spacing: 0.04em;
    color: var(--lux-ink-dim, var(--text-secondary));
    background: transparent;
    border: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    padding: 3px 9px;
    border-radius: var(--lux-r-sm, 3px);
    cursor: default;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
    white-space: nowrap;
}
.mod-card .chip .icon {
    width: 11px;
    height: 11px;
    flex-shrink: 0;
    color: var(--ch);
}
.mod-card .chip--link { cursor: pointer; }
.mod-card .chip--link:hover {
    color: var(--lux-ink, var(--text-color));
    border-color: color-mix(in srgb, var(--ch) 40%, transparent);
    background: color-mix(in srgb, var(--ch) 12%, transparent);
}
.mod-card .chip--tag {
    color: var(--ch);
    border-color: color-mix(in srgb, var(--ch) 22%, transparent);
    background: color-mix(in srgb, var(--ch) 10%, transparent);
}
.mod-card .chip--err {
    color: var(--ch-coral, var(--danger-color));
    border-color: color-mix(in srgb, var(--ch-coral, var(--danger-color)) 30%, transparent);
    background: color-mix(in srgb, var(--ch-coral, var(--danger-color)) 10%, transparent);
}

/* Inline chip variant — used by the pipeline strip on running target
   cards. A bigger numeric (display-font-ish) sits beside a small mono
   caps unit, so a chip can carry "127K · frames" without looking like
   a label-shaped pill. The data values inside still update via
   _patchTargetMetrics. */
.mod-card .chip--inline {
    gap: 4px;
    padding: 3px 8px;
}
.mod-card .chip--inline > span {
    font-family: var(--font-mono, monospace);
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--lux-ink, var(--text-color));
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
}
.mod-card .chip--inline > small {
    font-family: var(--font-mono, monospace);
    font-size: 0.55rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--lux-ink-mute, var(--text-secondary));
}

/* ── Pipeline strip — replaces the old target-metrics-collapse. A
   thin always-visible segmented timing bar plus a tight chip row
   below it (timing total / frames / keepalive). The bar is the
   diagnostic; the chips are the counters. */
.mod-card .target-pipeline {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.mod-card .target-pipeline .timing-bar {
    height: 4px;
    border-radius: 2px;
    background: var(--lux-bg-0, var(--bg-color));
    box-shadow: inset 0 0 0 1px var(--lux-line, var(--border-color));
    margin: 0;
    overflow: hidden;
    gap: 0;
}
.mod-card .target-pipeline .timing-seg {
    transition: flex 0.3s ease;
    opacity: 0.85;
}
.mod-card .target-pipeline .timing-seg:hover {
    opacity: 1;
}
.mod-card .target-pipeline-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* ── Brightness fader (mod-card variant) ────────────────────────── */

.mod-fader {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    background: var(--lux-bg-0, var(--bg-color));
    border: var(--lux-hairline, 1px) solid var(--lux-line, var(--border-color));
    border-radius: var(--lux-r-sm, 4px);
}
.mod-fader__k {
    font-family: var(--font-mono, monospace);
    font-size: 0.55rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--lux-ink-mute, var(--text-secondary));
    min-width: 42px;
}
.mod-fader__lane {
    flex: 1;
    position: relative;
    display: flex;
    align-items: center;
    min-width: 0;
}
.mod-fader__track {
    flex: 1;
    position: relative;
    height: 5px;
    border-radius: 99px;
    background: color-mix(in srgb, var(--ch) 12%, var(--lux-bg-3, var(--border-color)));
    overflow: hidden;
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.4);
    pointer-events: none;
}
[data-theme="light"] .mod-fader__track {
    box-shadow: inset 0 1px 2px rgba(15, 20, 25, 0.08);
}
.mod-fader__fill {
    position: absolute;
    left: 0; top: 0; bottom: 0;
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--ch) 50%, transparent),
        var(--ch));
    box-shadow: 0 0 8px color-mix(in srgb, var(--ch) 60%, transparent);
}
/* The slider input overlays the visible track exactly — same flex slot,
   so its hit-zone aligns to the fill regardless of label/value width. */
.mod-fader__slider {
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    height: 18px;
    width: auto;
    margin: 0;
    opacity: 0;
    cursor: pointer;
    z-index: 2;
}
.mod-fader__v {
    font-family: var(--font-mono, monospace);
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--lux-ink, var(--text-color));
    min-width: 34px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* ── Preview surface (gradient strip / asset thumb / LED preview) ── */

.mod-preview {
    border-radius: var(--lux-r-sm, 4px);
    box-shadow: inset 0 0 0 1px var(--lux-line, var(--border-color));
    overflow: hidden;
    position: relative;
}
.mod-preview canvas {
    display: block;
    width: 100%;
    height: 100%;
}

/* Corner tag overlay on a preview surface (e.g. "BUILT-IN" on built-in
   gradient strips). Sits in the top-right with a backdrop-blurred dark
   pill so it stays legible on any gradient — light, dark, or mid-tone. */
.mod-preview__tag {
    position: absolute;
    top: 5px;
    right: 5px;
    padding: 2px 7px;
    font-family: var(--font-mono, monospace);
    font-size: 0.55rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1.4;
    color: rgba(255, 255, 255, 0.95);
    background: rgba(0, 0, 0, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: var(--lux-r-sm, 3px);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    pointer-events: none;
    user-select: none;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* ── Description text ──────────────────────────────────────────── */

.mod-desc {
    font-size: 0.82rem;
    color: var(--lux-ink-dim, var(--text-secondary));
    line-height: 1.45;
}

/* ── Mod-card specific tweaks for chips inside cards ─────────────
   The flex order in .mod-foot is: .mod-patch (margin-right:auto) on
   the left, primary action(s) on the right. Multiple .chip rows above
   the foot inherit normal flex-wrap behavior from .mod-chips. */

/* Mobile — reduce padding on mod-cards */
@media (max-width: 768px) {
    .card.mod-card,
    .template-card.mod-card {
        padding: 14px 14px 12px 18px;
        gap: 12px;
    }
    .mod-menu {
        min-width: 200px;
    }
}
