Files
ledgrab/docs/cards-redesign-demo.html
alexei.dolgolyov a56569b02f feat(ui): cards redesign + settings, modal, toolbar polish
Dashboard cards (mod-card system)
- New mod-card / mod-menu modules backing dashboard cards
- Reworked card colors, sections, dashboard layout, perf charts
- Channel-stripe styling, hairline borders, signal-flow animation
  on running cards, refined metric grid

Multiselect bulk toolbar
- Replaced tri-state checkbox with explicit Select-all / Deselect-all
  icon buttons; both disable when not applicable
- Dim + slight blur on non-selected siblings during selection mode so
  the active picks pop; selected card gains a subtle lift + primary-color
  glow halo
- Bulk tick uses ICON_CHECK from the icon registry (was U+2713) and
  scale-pops in via a cubic-bezier overshoot keyframe
- Toolbar restyled with luxury gradient bg, top accent stripe, glass
  blur, neon hover glows on each button group

Settings modal
- Tab bar converted to icon-only (cog / hard-drive / bell / palette /
  refresh / help) so labels never overflow at any locale; title and
  aria-label preserve translated names. Tabs distribute evenly via
  flex: 1 1 0 + space-around — no overflow possible
- IconSelect auto-populates <option> elements when the underlying select
  is empty, fixing the blank notification triggers (root cause: setting
  .value on an empty select is a no-op)
- Tab activation calls scrollIntoView on the active button as a safety
  net for narrow viewports

Modal exit animation
- Added symmetric fadeOut + slideDown keyframes; .modal.closing applies
  them with animation-fill-mode: forwards
- Modal.forceClose() defers display:none until animationend (with timer
  fallback). State cleanup (focus, body lock, stack) runs immediately so
  callers querying state get correct values
- isOpen returns false during the close animation; open() cancels any
  in-flight close so re-open works during the animation
- prefers-reduced-motion disables all modal animations

Locale picker
- Dropped redundant English/Русский/中文 long-form labels — picker now
  shows only EN / RU / ZH
- IconSelect trigger/cell hides empty icon/label slots via :empty so the
  layout collapses cleanly for minimal items

Filter input (cards section)
- Embedded magnifier icon via data URI (no HTML change); monospace
  uppercase placeholder, lux-bg-0 background, neon focus ring with inset
  shadow + outer glow
- Reset button only shows when the input has content (CSS-only via
  :placeholder-shown sibling selector — JS-resilient)

Snack toast
- Glass background (gradient + backdrop-blur) with top channel-color
  accent stripe matching the modal/toolbar language
- Per-type --toast-ch drives border/glow/timer color (success → primary,
  error → danger, info → info)
- Undo button gets a tinted hover with channel-color halo

Top header toolbar
- Removed hairline border from .header-btn for a flatter look; hover
  keeps the subtle background tint and primary-color glow

Device URL hyperlink
- Styled .mod-meta__link to pick up the card's --ch accent (instead of
  inheriting browser-blue underline). Dotted underline at rest solidifies
  on hover; soft text-shadow glow; web icon dims at rest, brightens on
  hover

Misc
- ICON_CHECK and ICON_HARD_DRIVE added to the icon registry
- Existing card-redesign demos checked in under docs/
- Removed obsolete docs/plans/device-typed-configs.md
2026-04-26 03:10:16 +03:00

1118 lines
58 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>LedGrab · Entity Card · Patchbay redesign</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Big+Shoulders+Display:wght@600;800&family=JetBrains+Mono:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
/* =====================================================================
PATCHBAY — entity card redesign for LedGrab
Aesthetic: studio-rack hardware module. Always-on channel signal,
tactile silkscreen labels, LCD-style readouts, tiny micro-motion.
Drops into the existing Lumenworks token system.
===================================================================== */
:root{
--primary-color:#4CAF50;
--primary-text:#66bb6a;
--primary-contrast:#fff;
--font-display:'Big Shoulders Display','Orbitron',sans-serif;
--font-body:'Manrope',-apple-system,BlinkMacSystemFont,sans-serif;
--font-mono:'JetBrains Mono','Cascadia Code',ui-monospace,monospace;
--lux-r-sm:3px;
--lux-r-md:8px;
/* channel palette — same hex values as production */
--ch-signal:#4CAF50; /* led / target */
--ch-cyan:#00d8ff; /* source / data */
--ch-magenta:#ff4ade; /* audio / fft */
--ch-amber:#ffb800; /* autostart / pending */
--ch-coral:#ff5e5e; /* offline / alarm */
--ch-violet:#8b7eff; /* automation / scenes */
--duration:.22s;
--ease:cubic-bezier(.16,1,.3,1);
}
[data-theme="dark"]{
--bg-page:#000;
--bg-card:#0e1014;
--bg-recess:#06080b;
--bg-chip:#0a0c10;
--line:#1f242c;
--line-bold:#2c333d;
--line-soft:#181c22;
--ink:#e6ebf2;
--ink-dim:#8b95a5;
--ink-mute:#5b6473;
--ink-faint:#3a414c;
--shadow:0 1px 0 rgba(255,255,255,.03), 0 14px 36px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.4);
--shadow-flat:0 1px 0 rgba(255,255,255,.025), 0 4px 12px rgba(0,0,0,.35);
--grain:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence baseFrequency='.9' numOctaves='2' seed='4'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .045 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}
[data-theme="light"]{
--bg-page:#f0f1f4;
--bg-card:#fff;
--bg-recess:#f6f8fb;
--bg-chip:#f6f8fb;
--line:#dce1e8;
--line-bold:#bcc4d0;
--line-soft:#e8ebf0;
--ink:#0f1419;
--ink-dim:#4c5866;
--ink-mute:#6b7684;
--ink-faint:#a5afbc;
--shadow:0 1px 0 rgba(255,255,255,.6), 0 12px 28px rgba(15,20,25,.10), 0 2px 6px rgba(15,20,25,.06);
--shadow-flat:0 1px 0 rgba(255,255,255,.6), 0 3px 10px rgba(15,20,25,.06);
--grain:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence baseFrequency='.9' numOctaves='2' seed='4'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .035 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
--primary-color:#2e7d32;
--primary-text:#2e7d32;
--ch-signal:#2e7d32;
--ch-cyan:#006b88;
--ch-magenta:#b01a99;
--ch-amber:#a56a00;
--ch-coral:#d8392e;
--ch-violet:#5b4fd0;
}
*{box-sizing:border-box;margin:0;padding:0;}
html,body{background:var(--bg-page);color:var(--ink);font-family:var(--font-body);min-height:100vh;}
body{padding:48px 32px 80px;-webkit-font-smoothing:antialiased;}
/* =================== Page chrome (demo only) =================== */
.page{max-width:1280px;margin:0 auto;}
.page-head{
display:flex;align-items:flex-end;justify-content:space-between;gap:24px;
padding-bottom:28px;margin-bottom:36px;
border-bottom:1px solid var(--line);
position:relative;
}
.page-head::after{
content:'';position:absolute;left:0;bottom:-1px;width:120px;height:2px;
background:linear-gradient(90deg,var(--ch-signal),transparent);
}
.eyebrow{
font-family:var(--font-mono);font-size:.7rem;letter-spacing:.32em;text-transform:uppercase;
color:var(--ink-mute);margin-bottom:10px;
display:flex;align-items:center;gap:10px;
}
.eyebrow::before{
content:'';width:6px;height:6px;border-radius:50%;background:var(--ch-signal);
box-shadow:0 0 0 3px color-mix(in srgb, var(--ch-signal) 20%, transparent);
animation:pulse 2s ease-in-out infinite;
}
@keyframes pulse{50%{box-shadow:0 0 0 5px color-mix(in srgb, var(--ch-signal) 0%, transparent);}}
h1{
font-family:var(--font-display);
font-size:clamp(2.4rem,5vw,4rem);font-weight:800;
letter-spacing:-.02em;line-height:.92;
color:var(--ink);
}
h1 em{
font-style:normal;color:var(--ch-signal);
font-family:var(--font-display);
}
.lede{
color:var(--ink-dim);max-width:46ch;font-size:.95rem;line-height:1.55;
margin-top:14px;
}
.theme-toggle{
display:inline-flex;align-items:center;gap:0;font-family:var(--font-mono);
font-size:.7rem;letter-spacing:.2em;text-transform:uppercase;
border:1px solid var(--line-bold);border-radius:999px;padding:0;
background:var(--bg-card);overflow:hidden;
}
.theme-toggle button{
appearance:none;background:none;border:none;color:var(--ink-mute);
padding:8px 16px;cursor:pointer;font:inherit;letter-spacing:inherit;
transition:color .2s,background .2s;
}
.theme-toggle button.is-active{
background:var(--ink);color:var(--bg-card);
}
/* =================== Section heading =================== */
.section-title{
font-family:var(--font-mono);font-size:.72rem;letter-spacing:.3em;text-transform:uppercase;
color:var(--ink-mute);margin:48px 0 16px;
display:flex;align-items:center;gap:14px;
}
.section-title::after{content:'';flex:1;height:1px;background:var(--line);}
.section-title .num{
font-family:var(--font-display);font-size:1.6rem;font-weight:800;
color:var(--ink);letter-spacing:0;
border-right:1px solid var(--line);padding-right:14px;
}
/* =================== Card grid =================== */
.grid{
display:grid;
grid-template-columns:repeat(auto-fill,minmax(340px,1fr));
gap:22px;
}
/* =====================================================================
CARD — patchbay module
===================================================================== */
.card{
--ch:var(--ch-signal);
--ch-mix-12:color-mix(in srgb, var(--ch) 12%, transparent);
--ch-mix-22:color-mix(in srgb, var(--ch) 22%, transparent);
--ch-mix-40:color-mix(in srgb, var(--ch) 40%, transparent);
--ch-mix-65:color-mix(in srgb, var(--ch) 65%, transparent);
position:relative;
background:var(--bg-card);
border:1px solid var(--line);
border-radius:var(--lux-r-md);
padding:0;
display:flex;flex-direction:column;
overflow:hidden;
transition:transform var(--duration) var(--ease),
box-shadow var(--duration) var(--ease),
border-color var(--duration) var(--ease);
isolation:isolate;
}
/* paper-grain overlay — subtle, only one decorative layer */
.card::before{
content:'';position:absolute;inset:0;pointer-events:none;
background-image:var(--grain);
background-size:180px 180px;
mix-blend-mode:overlay;opacity:.6;
z-index:0;
}
/* hover: lift, brighten border, intensify channel rail */
.card:hover{
transform:translateY(-3px);
border-color:color-mix(in srgb, var(--ch) 26%, var(--line-bold));
box-shadow:var(--shadow);
}
.card:hover .rail{opacity:1;}
.card:hover .rail__bar{transform:scaleX(1);}
/* ───────── CHANNEL RAIL — top edge, always on at low intensity ───────── */
.rail{
position:relative;
height:32px;
padding:0 14px 0 16px;
display:flex;align-items:center;justify-content:space-between;
background:linear-gradient(180deg,
color-mix(in srgb, var(--ch) 8%, transparent) 0%,
transparent 100%);
border-bottom:1px solid var(--line-soft);
opacity:.85;
transition:opacity var(--duration) var(--ease);
z-index:1;
}
/* the actual channel signal line — sits along the top edge */
.rail__bar{
position:absolute;left:0;top:0;height:2px;width:100%;
background:linear-gradient(90deg,
var(--ch) 0%,
var(--ch-mix-40) 60%,
transparent 100%);
transform:scaleX(.65);transform-origin:left;
transition:transform var(--duration) var(--ease), opacity var(--duration) var(--ease);
box-shadow:0 0 12px var(--ch-mix-40);
}
/* type label — silkscreen panel text */
.rail__label{
font-family:var(--font-display);
font-size:.78rem;font-weight:800;letter-spacing:.22em;text-transform:uppercase;
color:var(--ch);
display:inline-flex;align-items:center;gap:8px;
text-shadow:0 0 12px var(--ch-mix-22);
}
.rail__label .id{
font-family:var(--font-mono);font-size:.62rem;font-weight:500;
color:var(--ink-faint);letter-spacing:.18em;
}
/* tiny notch on the rail's right side — patchpoint */
.rail__notch{
width:14px;height:14px;border-radius:50%;
background:var(--bg-recess);
border:1px solid var(--line-bold);
position:relative;
box-shadow:inset 0 1px 1px rgba(0,0,0,.4);
}
.rail__notch::after{
content:'';position:absolute;inset:3px;border-radius:50%;
background:var(--ch);opacity:.32;
transition:opacity var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
}
/* ───────── BODY ───────── */
.body{
position:relative;z-index:1;
padding:16px 18px 14px;
display:flex;flex-direction:column;gap:12px;
flex:1;
}
.title-row{display:flex;align-items:center;gap:10px;min-width:0;}
.title{
font-family:var(--font-body);
font-size:1.1rem;font-weight:700;letter-spacing:-.01em;
color:var(--ink);
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
flex:1;min-width:0;
}
/* status LED */
.led{
width:9px;height:9px;border-radius:50%;
flex-shrink:0;position:relative;
background:var(--ink-faint);
box-shadow:inset 0 0 0 1px rgba(0,0,0,.3);
}
.led--on{
background:var(--ch-signal);
box-shadow:0 0 0 0 var(--ch-mix-40),
0 0 10px var(--ch-mix-65),
inset 0 0 2px rgba(255,255,255,.4);
animation:ledPulse 2.4s ease-in-out infinite;
}
@keyframes ledPulse{
50%{box-shadow:0 0 0 4px transparent,0 0 14px var(--ch-mix-65),inset 0 0 2px rgba(255,255,255,.4);}
}
.led--off{background:var(--ch-coral);box-shadow:0 0 8px color-mix(in srgb, var(--ch-coral) 50%, transparent);}
.led--unknown{background:var(--ch-amber);box-shadow:0 0 8px color-mix(in srgb, var(--ch-amber) 35%, transparent);}
/* address pill — short identifier next to title */
.address{
font-family:var(--font-mono);
font-size:.62rem;font-weight:500;letter-spacing:.08em;
color:var(--ink-dim);
background:var(--bg-recess);
border:1px solid var(--line-soft);
padding:3px 8px;border-radius:3px;
flex-shrink:1;min-width:0;
text-overflow:ellipsis;overflow:hidden;
box-shadow:inset 0 1px 0 rgba(0,0,0,.2);
}
[data-theme="light"] .address{box-shadow:inset 0 1px 0 rgba(15,20,25,.04);}
/* ───────── READOUT — LCD-style metric strip ───────── */
.readout{
display:flex;align-items:stretch;gap:0;
background:var(--bg-recess);
border:1px solid var(--line-soft);
border-radius:var(--lux-r-sm);
padding:0;overflow:hidden;
box-shadow:inset 0 1px 2px rgba(0,0,0,.4);
}
[data-theme="light"] .readout{box-shadow:inset 0 1px 2px rgba(15,20,25,.06);}
.readout__cell{
flex:1;padding:7px 10px;
border-right:1px solid var(--line-soft);
display:flex;flex-direction:column;gap:2px;min-width:0;
}
.readout__cell:last-child{border-right:none;}
.readout__k{
font-family:var(--font-mono);font-size:.58rem;letter-spacing:.18em;text-transform:uppercase;
color:var(--ink-mute);
}
.readout__v{
font-family:var(--font-mono);font-size:.92rem;font-weight:700;
color:var(--ink);font-variant-numeric:tabular-nums;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.readout__v--accent{color:var(--ch);text-shadow:0 0 12px var(--ch-mix-40);}
.readout__v--dim{color:var(--ink-mute);font-weight:500;}
/* ───────── PROPERTY CHIPS — secondary metadata ───────── */
.props{display:flex;flex-wrap:wrap;gap:6px;}
.chip{
display:inline-flex;align-items:center;gap:6px;
font-family:var(--font-mono);font-size:.66rem;font-weight:500;
letter-spacing:.04em;
color:var(--ink-dim);
background:transparent;
border:1px solid var(--line);
padding:3px 9px;border-radius:999px;
transition:color .15s, border-color .15s, background .15s;
cursor:default;
}
.chip svg{width:11px;height:11px;flex-shrink:0;color:var(--ch);}
.chip--link{cursor:pointer;}
.chip--link:hover{
color:var(--ink);
border-color:var(--ch-mix-40);
background:var(--ch-mix-12);
}
.chip--tag{
color:var(--ch);
border-color:var(--ch-mix-22);
background:var(--ch-mix-12);
}
/* ───────── BRIGHTNESS / SLIDER (LED devices) ───────── */
.fader{
display:flex;align-items:center;gap:12px;
padding:8px 10px;
background:var(--bg-recess);
border:1px solid var(--line-soft);
border-radius:var(--lux-r-sm);
}
.fader__label{
font-family:var(--font-mono);font-size:.6rem;letter-spacing:.2em;text-transform:uppercase;
color:var(--ink-mute);min-width:42px;
}
.fader__track{
position:relative;flex:1;height:6px;border-radius:99px;
background:linear-gradient(90deg,
color-mix(in srgb, var(--ch) 0%, transparent),
color-mix(in srgb, var(--ch) 30%, transparent));
overflow:hidden;
box-shadow:inset 0 1px 2px rgba(0,0,0,.4);
}
[data-theme="light"] .fader__track{box-shadow:inset 0 1px 2px rgba(15,20,25,.08);}
.fader__fill{
position:absolute;left:0;top:0;bottom:0;
background:linear-gradient(90deg,var(--ch-mix-40),var(--ch));
box-shadow:0 0 8px var(--ch-mix-65);
}
.fader__val{
font-family:var(--font-mono);font-size:.8rem;font-weight:700;
color:var(--ink);min-width:38px;text-align:right;
font-variant-numeric:tabular-nums;
}
/* ───────── FOOTER (action row) ───────── */
.footer{
position:relative;z-index:1;
margin-top:auto;
padding:10px 14px 12px;
display:flex;align-items:center;gap:6px;
background:linear-gradient(180deg,transparent,var(--bg-recess));
border-top:1px solid var(--line-soft);
}
/* fastener dots — tiny silkscreened rivets at the corners of the seam */
.footer::before,.footer::after{
content:'';position:absolute;top:-3px;width:5px;height:5px;border-radius:50%;
background:var(--bg-page);
box-shadow:inset 0 0 0 1px var(--line-bold), 0 0 0 1px var(--bg-card);
}
.footer::before{left:8px;}
.footer::after{right:8px;}
.btn{
appearance:none;background:transparent;border:1px solid var(--line);
color:var(--ink-dim);
width:30px;height:30px;border-radius:var(--lux-r-sm);
display:inline-flex;align-items:center;justify-content:center;
cursor:pointer;transition:.15s;
flex-shrink:0;
}
.btn:hover{color:var(--ink);border-color:var(--ch-mix-40);background:var(--ch-mix-12);}
.btn svg{width:14px;height:14px;}
.btn--danger:hover{color:var(--ch-coral);border-color:color-mix(in srgb, var(--ch-coral) 40%, transparent);background:color-mix(in srgb, var(--ch-coral) 10%, transparent);}
.btn--primary{
color:var(--bg-card);background:var(--ch);border-color:var(--ch);
margin-left:auto;
}
.btn--primary:hover{filter:brightness(1.1);}
.swatch{
width:18px;height:18px;border-radius:50%;
background:conic-gradient(red,orange,yellow,green,cyan,blue,magenta,red);
border:1px solid var(--line-bold);cursor:pointer;
margin-left:auto;
box-shadow:inset 0 0 0 2px var(--bg-card);
transition:transform .2s var(--ease);
}
.swatch:hover{transform:scale(1.15) rotate(45deg);}
/* =================== RUNNING / ACTIVE state =================== */
.card--running{
border-color:var(--ch-mix-40);
box-shadow:
0 0 0 1px var(--ch-mix-22),
var(--shadow-flat);
}
.card--running .rail__bar{transform:scaleX(1);}
.card--running .rail{opacity:1;}
.card--running .rail__notch::after{
opacity:1;
box-shadow:0 0 8px var(--ch),0 0 14px var(--ch-mix-65);
}
/* signal-flow at the bottom edge */
.card--running::after{
content:'';position:absolute;left:0;right:0;bottom:0;height:2px;
background:linear-gradient(90deg,
transparent 0%,
var(--ch) 50%,
transparent 100%);
background-size:35% 100%;background-repeat:no-repeat;
animation:signalFlow 2.4s linear infinite;
z-index:2;
}
@keyframes signalFlow{
0%{background-position:-35% 0;}
100%{background-position:135% 0;}
}
/* =================== OFFLINE state =================== */
.card--offline{opacity:.78;}
.card--offline .rail__bar{
background:linear-gradient(90deg,
var(--ch-coral) 0%,
color-mix(in srgb, var(--ch-coral) 30%, transparent) 60%,
transparent 100%);
box-shadow:none;
transform:scaleX(.4);
animation:offlineFlicker 4.5s ease-in-out infinite;
}
@keyframes offlineFlicker{
0%,40%,42%,100%{transform:scaleX(.4);}
41%{transform:scaleX(.55);}
}
/* =================== DRAG handle (always visible, low key) =================== */
.drag{
position:absolute;left:6px;top:8px;
width:10px;height:18px;cursor:grab;z-index:3;
display:flex;flex-direction:column;justify-content:space-between;
opacity:.35;transition:opacity .15s;
}
.card:hover .drag{opacity:.85;}
.drag span{display:block;width:100%;height:1px;background:var(--ink-mute);}
/* =================== CHANNEL ASSIGNMENTS =================== */
.card[data-ch="signal"] {--ch:var(--ch-signal);}
.card[data-ch="cyan"] {--ch:var(--ch-cyan);}
.card[data-ch="magenta"] {--ch:var(--ch-magenta);}
.card[data-ch="amber"] {--ch:var(--ch-amber);}
.card[data-ch="coral"] {--ch:var(--ch-coral);}
.card[data-ch="violet"] {--ch:var(--ch-violet);}
/* =================== Notes block =================== */
.notes{
margin-top:64px;padding:28px 32px;
background:var(--bg-card);border:1px solid var(--line);border-radius:var(--lux-r-md);
position:relative;overflow:hidden;
}
.notes::before{
content:'';position:absolute;left:0;top:0;bottom:0;width:3px;
background:linear-gradient(180deg,var(--ch-signal),var(--ch-cyan),var(--ch-magenta),var(--ch-violet));
}
.notes h2{
font-family:var(--font-display);font-size:1.6rem;font-weight:800;
letter-spacing:-.01em;margin-bottom:18px;
}
.notes ul{list-style:none;display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px 28px;}
.notes li{
font-size:.88rem;color:var(--ink-dim);line-height:1.55;
padding-left:18px;position:relative;
}
.notes li::before{
content:'';position:absolute;left:0;top:.65em;width:8px;height:1px;background:var(--ch-signal);
}
.notes strong{color:var(--ink);font-weight:700;}
.notes code{
font-family:var(--font-mono);font-size:.78rem;
background:var(--bg-recess);border:1px solid var(--line-soft);
padding:1px 5px;border-radius:3px;color:var(--ch-cyan);
}
</style>
</head>
<body>
<div class="page">
<header class="page-head">
<div>
<div class="eyebrow">PROPOSAL · v1 · Patchbay</div>
<h1>Entity card,<br>treated as <em>hardware</em>.</h1>
<p class="lede">Every entity is a module in a virtual rack. The channel system you already built — dormant on most cards today — becomes the primary identity signal: type, status, and "live" state read at a glance.</p>
</div>
<div class="theme-toggle" role="group" aria-label="Theme">
<button class="is-active" data-theme-set="dark">DARK</button>
<button data-theme-set="light">LIGHT</button>
</div>
</header>
<div class="section-title"><span class="num">01</span> Output targets · LED devices</div>
<div class="grid">
<!-- LED device, online + running -->
<article class="card card--running" data-ch="signal">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">LED · OUT <span class="id">// CH-01</span></div>
<div class="rail__notch" title="Patch point"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Online"></span>
<span class="title">Living Room Strip</span>
<span class="address">192.168.1.42</span>
</div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Pixels</div>
<div class="readout__v">144</div>
</div>
<div class="readout__cell">
<div class="readout__k">FPS</div>
<div class="readout__v readout__v--accent">59.7</div>
</div>
<div class="readout__cell">
<div class="readout__k">Lat</div>
<div class="readout__v">8<span class="readout__v--dim">ms</span></div>
</div>
</div>
<div class="props">
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg> WLED · v0.14</span>
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg> RGB</span>
<span class="chip chip--link" title="Jump to source"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/></svg> Display 2</span>
</div>
<div class="fader">
<span class="fader__label">Bright</span>
<div class="fader__track"><div class="fader__fill" style="width:78%"></div></div>
<span class="fader__val">198</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Refresh"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></svg></button>
<button class="btn" title="Clone"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
<button class="btn" title="Settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
<span class="swatch" title="Channel color"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg></button>
</div>
</article>
<!-- LED device, offline -->
<article class="card card--offline" data-ch="signal">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">LED · OUT <span class="id">// CH-02</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--off" title="Offline"></span>
<span class="title">Bedroom Halo</span>
<span class="address">10.0.4.18</span>
</div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Pixels</div>
<div class="readout__v readout__v--dim">60</div>
</div>
<div class="readout__cell">
<div class="readout__k">FPS</div>
<div class="readout__v readout__v--dim">— —</div>
</div>
<div class="readout__cell">
<div class="readout__k">Last</div>
<div class="readout__v readout__v--dim">2h 14m</div>
</div>
</div>
<div class="props">
<span class="chip" style="--ch:var(--ch-coral);color:var(--ch-coral);border-color:color-mix(in srgb, var(--ch-coral) 30%, transparent);background:color-mix(in srgb, var(--ch-coral) 10%, transparent);"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg> Connection refused</span>
<span class="chip">Adalight · 921k</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Retry"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/></svg></button>
<button class="btn" title="Settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- Audio source, running -->
<article class="card card--running" data-ch="magenta">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">FFT · IN <span class="id">// 48 kHz</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Streaming"></span>
<span class="title">Spotify Loopback</span>
</div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Bands</div>
<div class="readout__v">32</div>
</div>
<div class="readout__cell">
<div class="readout__k">Peak</div>
<div class="readout__v readout__v--accent">-6.2 dB</div>
</div>
<div class="readout__cell">
<div class="readout__k">CPU</div>
<div class="readout__v">3.1<span class="readout__v--dim">%</span></div>
</div>
</div>
<div class="props">
<span class="chip">WASAPI · stereo</span>
<span class="chip chip--tag">Bass · Mids · Highs</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Stop"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="1"/></svg></button>
<button class="btn" title="Clone"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
<button class="btn" title="Settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
</div>
<div class="section-title"><span class="num">02</span> Sources & integrations</div>
<div class="grid">
<!-- Screen capture source -->
<article class="card" data-ch="cyan">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">SCREEN · IN <span class="id">// DISP-2</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--unknown" title="Idle"></span>
<span class="title">Cinematic Capture</span>
</div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Region</div>
<div class="readout__v">3840×1080</div>
</div>
<div class="readout__cell">
<div class="readout__k">Tgt</div>
<div class="readout__v">60fps</div>
</div>
</div>
<div class="props">
<span class="chip">Mss · BGRA</span>
<span class="chip chip--link">Pre-process · CSPT</span>
<span class="chip">Letterbox crop</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Test"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg></button>
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- Automation -->
<article class="card" data-ch="violet">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">SCENE · LOGIC <span class="id">// AUTO-07</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Armed"></span>
<span class="title">Movie Night</span>
</div>
<div class="props">
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg> 21:00 — 23:30</span>
<span class="chip chip--link">When · Plex playing</span>
<span class="chip chip--link">→ Living + Bedroom</span>
<span class="chip chip--tag">cinema</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Run now"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg></button>
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- Scene preset -->
<article class="card" data-ch="violet">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">SCENE · PRESET <span class="id">// SCN-04</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--unknown" title="Idle"></span>
<span class="title">Sunset Warmth</span>
</div>
<p style="font-size:.82rem;color:var(--ink-dim);line-height:1.4;margin:-2px 0 -2px;">Captured 3 days ago — warm tungsten cast on every fixture for a 19:00 unwind.</p>
<div class="props">
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg> 4 targets</span>
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/></svg> Used by 2 automations</span>
<span class="chip">Updated 21 Apr</span>
</div>
</div>
<div class="footer">
<button class="btn btn--primary" title="Activate"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg></button>
<button class="btn" title="Recapture"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="13" r="3"/><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/></svg></button>
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- Sync clock -->
<article class="card card--running" data-ch="amber">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">CLK · GEN <span class="id">// 110 BPM</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Ticking"></span>
<span class="title">Master Tempo</span>
</div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">BPM</div>
<div class="readout__v readout__v--accent">110.00</div>
</div>
<div class="readout__cell">
<div class="readout__k">Phase</div>
<div class="readout__v">0.42</div>
</div>
<div class="readout__cell">
<div class="readout__k">Sub</div>
<div class="readout__v">1/16</div>
</div>
</div>
<div class="props">
<span class="chip">Drift · ±0.3ms</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Tap"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="4"/></svg></button>
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
</div>
<div class="section-title"><span class="num">03</span> Templates &amp; assets</div>
<div class="grid">
<!-- Capture template with filter chain (config) -->
<article class="card" data-ch="cyan">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">TPL · CAPTURE <span class="id">// MSS</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--unknown" title="Template"></span>
<span class="title">Desktop · 60 fps · region</span>
</div>
<p style="font-size:.82rem;color:var(--ink-dim);line-height:1.4;margin:-2px 0 -2px;">Reusable capture preset for screen sources. 3 engine options pre-configured.</p>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Engine</div>
<div class="readout__v readout__v--accent">MSS</div>
</div>
<div class="readout__cell">
<div class="readout__k">Bind</div>
<div class="readout__v">3 keys</div>
</div>
<div class="readout__cell">
<div class="readout__k">Used by</div>
<div class="readout__v">5 sources</div>
</div>
</div>
<!-- Filter chain replaces the patterned config table — flow arrows -->
<div class="props" style="align-items:center;">
<span class="chip">crop</span>
<span style="color:var(--ch);opacity:.7;"></span>
<span class="chip">downsample</span>
<span style="color:var(--ch);opacity:.7;"></span>
<span class="chip">gamma</span>
<span style="color:var(--ch);opacity:.7;"></span>
<span class="chip">color-correct</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Test"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg></button>
<button class="btn" title="Clone"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- Gradient — color preview is the headline -->
<article class="card" data-ch="signal">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">PALETTE <span class="id">// G-08</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--unknown" title="Stored"></span>
<span class="title">Aurora</span>
<span class="address" style="background:transparent;border-color:var(--ch-mix-22);color:var(--ch);">BUILTIN</span>
</div>
<!-- the gradient itself becomes the readout -->
<div style="height:32px;border-radius:var(--lux-r-sm);background:linear-gradient(90deg,#003e6b 0%,#0085c4 25%,#22d3ee 55%,#a855f7 80%,#ec4899 100%);box-shadow:inset 0 0 0 1px var(--line-soft),inset 0 1px 6px rgba(0,0,0,.4);position:relative;">
<span style="position:absolute;left:6px;bottom:4px;font-family:var(--font-mono);font-size:.55rem;letter-spacing:.18em;color:#fff;text-shadow:0 1px 2px #000;">5 STOPS · LOOP</span>
</div>
<div class="props">
<span class="chip">Used in 3 strips</span>
<span class="chip">HSL space</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Clone"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
<span class="swatch"></span>
</div>
</article>
<!-- Asset -->
<article class="card" data-ch="cyan">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">ASSET · IMG <span class="id">// PNG</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Available"></span>
<span class="title">cosmic-loop-001.png</span>
</div>
<!-- Image preview placeholder with ratio strip -->
<div style="height:64px;border-radius:var(--lux-r-sm);background:
radial-gradient(circle at 30% 40%,#a855f7 0,transparent 35%),
radial-gradient(circle at 70% 60%,#22d3ee 0,transparent 30%),
#07090e;
box-shadow:inset 0 0 0 1px var(--line-soft);"></div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Size</div>
<div class="readout__v">1920×1080</div>
</div>
<div class="readout__cell">
<div class="readout__k">Bytes</div>
<div class="readout__v">412 KB</div>
</div>
</div>
</div>
<div class="footer">
<button class="btn" title="Download"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></button>
<button class="btn" title="Replace"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- Color strip (composite layered) -->
<article class="card card--running" data-ch="signal">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">STRIP · MAPPED <span class="id">// 144 PX</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Mapping live"></span>
<span class="title">Cinema map · 144 px</span>
</div>
<!-- Live LED preview strip (per-pixel) -->
<div style="height:18px;border-radius:2px;background:linear-gradient(90deg,
#200035 0%, #5a1ca8 8%, #c14b8e 18%, #ff7a59 30%,
#ffd56b 40%, #95e7c9 50%, #00d8ff 65%, #2d6cdf 78%,
#1c2554 90%, #050816 100%);
box-shadow:inset 0 0 0 1px var(--line-soft);"></div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Source</div>
<div class="readout__v readout__v--accent">Cinematic</div>
</div>
<div class="readout__cell">
<div class="readout__k">Map</div>
<div class="readout__v">Letterbox</div>
</div>
<div class="readout__cell">
<div class="readout__k">Smooth</div>
<div class="readout__v">0.35</div>
</div>
</div>
<div class="props">
<span class="chip chip--link" title="Picture source"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/></svg> Display 2</span>
<span class="chip chip--link" title="CSPT chain">post-fx · 4 filters</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Test"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg></button>
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- Game integration -->
<article class="card" data-ch="amber">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">GAME · CSGO <span class="id">// GSI</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Listening"></span>
<span class="title">Counter-Strike 2</span>
</div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Port</div>
<div class="readout__v">3456</div>
</div>
<div class="readout__cell">
<div class="readout__k">Events</div>
<div class="readout__v">12 mapped</div>
</div>
<div class="readout__cell">
<div class="readout__k">Last</div>
<div class="readout__v">12s</div>
</div>
</div>
<div class="props">
<span class="chip chip--tag">flash</span>
<span class="chip chip--tag">defuse</span>
<span class="chip chip--tag">round-end</span>
<span class="chip">+9 more</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Test event"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg></button>
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
<!-- HA / value source -->
<article class="card" data-ch="cyan">
<div class="drag" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
<div class="rail">
<div class="rail__bar"></div>
<div class="rail__label">VALUE · HA <span class="id">// SENSOR</span></div>
<div class="rail__notch"></div>
</div>
<div class="body">
<div class="title-row">
<span class="led led--on" title="Updating"></span>
<span class="title">Outdoor temp</span>
<span class="address">sensor.outdoor_temp</span>
</div>
<div class="readout">
<div class="readout__cell">
<div class="readout__k">Now</div>
<div class="readout__v readout__v--accent">14.7°</div>
</div>
<div class="readout__cell">
<div class="readout__k">Range</div>
<div class="readout__v">8 — 22</div>
</div>
<div class="readout__cell">
<div class="readout__k">Tick</div>
<div class="readout__v">2s</div>
</div>
</div>
<div class="props">
<span class="chip">Bound to: brightness</span>
<span class="chip">Linear · clamped</span>
</div>
</div>
<div class="footer">
<button class="btn" title="Edit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
<span class="swatch"></span>
<button class="btn btn--danger" title="Delete"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
</div>
</article>
</div>
<section class="notes">
<h2>Coverage matrix — every card type</h2>
<ul style="grid-template-columns:repeat(auto-fit,minmax(240px,1fr));font-size:.82rem;">
<li><strong>Devices</strong> <code>data-device-id</code> · signal · rail <code>LED · OUT</code></li>
<li><strong>LED targets</strong> <code>data-target-id</code> · signal · rail <code>TARGET · OUT</code></li>
<li><strong>HA light targets</strong> <code>data-ha-target-id</code> · signal · rail <code>HA · OUT</code></li>
<li><strong>Color strips</strong> <code>data-css-id</code> · signal · rail <code>STRIP · MAPPED/COMPOSITE/MATH/EVENT</code></li>
<li><strong>CSPT</strong> <code>data-cspt-id</code> · signal · rail <code>TPL · STRIP</code></li>
<li><strong>Pattern templates</strong> <code>data-pattern-template-id</code> · signal · rail <code>TPL · PATTERN</code></li>
<li><strong>Picture sources</strong> <code>data-stream-id</code> · cyan · rail <code>SCREEN/IMG/VIDEO · IN</code></li>
<li><strong>Capture templates</strong> <code>data-template-id</code> · cyan · rail <code>TPL · CAPTURE</code></li>
<li><strong>PP templates</strong> <code>data-pp-template-id</code> · cyan · rail <code>TPL · POST-FX</code></li>
<li><strong>HA sources</strong> <code>data-id</code> · cyan · rail <code>HA · IN</code></li>
<li><strong>MQTT sources</strong> <code>data-id</code> · cyan · rail <code>MQTT · IN</code></li>
<li><strong>Weather sources</strong> <code>data-id</code> · cyan · rail <code>WX · IN</code></li>
<li><strong>Value sources</strong> <code>data-id</code> · cyan · rail <code>VALUE · HA/MQTT/MATH/SCHEDULE</code></li>
<li><strong>Audio capture sources</strong> <code>data-id</code> · magenta · rail <code>AUDIO · IN</code></li>
<li><strong>Audio processed sources</strong> <code>data-id</code> · magenta · rail <code>FFT · IN</code></li>
<li><strong>Audio templates</strong> <code>data-audio-template-id</code> · magenta · rail <code>TPL · AUDIO</code></li>
<li><strong>Audio processing tpls</strong> <code>data-apt-id</code> · magenta · rail <code>TPL · FFT</code></li>
<li><strong>Automations</strong> <code>data-automation-id</code> · violet · rail <code>SCENE · LOGIC</code></li>
<li><strong>Scene presets</strong> <code>data-scene-id</code> · violet · rail <code>SCENE · PRESET</code></li>
<li><strong>Sync clocks</strong> <code>data-sync-clock-id</code> · violet · rail <code>CLK · GEN</code></li>
<li><strong>Game integrations</strong> <code>data-gi-id</code> · amber · rail <code>GAME · &lt;type&gt;</code></li>
<li><strong>Gradients</strong> <code>data-id</code> · signal · rail <code>PALETTE</code></li>
<li><strong>Assets</strong> <code>data-id</code> · cyan · rail <code>ASSET · IMG/AUD/VID</code></li>
</ul>
<h2 style="margin-top:42px;">What changes — and why</h2>
<ul>
<li><strong>Channel rail is always on.</strong> The dormant <code>--ch-*</code> palette becomes a top-edge bar with a silkscreened type label (<code>LED · OUT</code>, <code>FFT · IN</code>). Every card carries its identity; running cards just <em>brighten</em> rather than reveal.</li>
<li><strong>Status LED is honest.</strong> A 9px dot pulses for online, holds for offline (coral), warns for unknown (amber). The hand-rolled <code>health-dot</code> can be reused — only its glow needs lifting.</li>
<li><strong>LCD readout strip</strong> replaces ad-hoc <code>card-meta</code> badges. Three recessed cells with <em>Pixels / FPS / Lat</em> — tabular-nums, bordered hairline, faint inner shadow. Scannable in one fixation.</li>
<li><strong>Property chips are pill-shaped.</strong> Single chip system (<code>.chip</code>, <code>.chip--link</code>, <code>.chip--tag</code>) replaces the parallel <code>.card-meta</code> + <code>.stream-card-prop</code> styles. Crosslinks tint to the channel color on hover instead of generic gray.</li>
<li><strong>Drag handle and trash are persistent.</strong> 35% opacity at rest, 85% on hover. Touch and keyboard users see the affordances; ghost-buttons no longer require a mouse to discover.</li>
<li><strong>Footer seam.</strong> Rivet-style fastener dots and an inner gradient give the action row a "panel screwed onto the module" feel — separating controls from data without a hard rule.</li>
<li><strong>Brightness is a fader.</strong> Channel-tinted fill, recessed track, value as monospace readout — fits the metric language instead of looking like a generic <code>&lt;input type="range"&gt;</code>.</li>
<li><strong>Subtle grain.</strong> One <code>feTurbulence</code> SVG inlined; 45% opacity. Ends the "every dark UI is a flat black rectangle" feeling without costing performance.</li>
<li><strong>Light theme adapts cleanly.</strong> Channel hex values are already darkened in the existing tokens — the new design uses them unchanged.</li>
</ul>
</section>
</div>
<script>
document.querySelectorAll('.theme-toggle button').forEach(b=>{
b.addEventListener('click',()=>{
document.documentElement.dataset.theme=b.dataset.themeSet;
document.querySelectorAll('.theme-toggle button').forEach(x=>x.classList.toggle('is-active',x===b));
});
});
</script>
</body>
</html>