dragonflight/services/web-ui/public/styles-rest.css
Zac Gaetano 5882c68217 feat(schedule): EPG stylesheet + impeccable context (PRODUCT/DESIGN.md)
The EPG JSX components in screens-ingest.jsx ship with the YouTube branch
but the matching stylesheet got lost during the parallel-branch shuffle.
This adds the missing .epg-* block to styles-rest.css and replaces the
dead .cal-* (month-calendar) rules left over from the previous design.

What the styles cover:
- .epg-page / .epg-toolbar — top-level flex layout + date nav row
- .epg-status — sticky "on air" strip with pulse halo on the live dot
- .epg / .epg-corner / .epg-gutter / .epg-canvas-head / .epg-canvas —
  the 2x2 sticky grid (top ruler + left gutter both sticky)
- .epg-ruler / .epg-ruler-tick — hour ticks
- .epg-row + .epg-block + .epg-block.live/.failed/.past — event blocks
  with project-color 4px inner bar (no side-stripes; impeccable ban)
- .epg-now / .epg-now-pip — vertical hot-red now-line with broadcast glow
- .epg-week + .epg-week-day — stacked 7-day sections for week view
- .epg-empty — recorder-less / loading empty state

Also adds PRODUCT.md and DESIGN.md so future design passes have the
context files the impeccable skill requires. Both drafted from the
existing codebase (tokens, screen patterns) rather than synthesised
from a prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 16:19:25 -04:00

1339 lines
35 KiB
CSS

/* ========== Projects ========== */
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 14px;
}
.project-card {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
overflow: hidden;
cursor: pointer;
transition: transform 120ms, border 80ms;
}
.project-card:hover {
transform: translateY(-2px);
border-color: var(--border-stronger);
}
.project-thumb-grid {
display: grid;
grid-template-columns: 2fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 2px;
aspect-ratio: 16 / 9;
background: var(--bg-2);
}
.project-thumb-grid .project-thumb-cell:first-child { grid-row: span 2; }
.project-thumb-cell { position: relative; overflow: hidden; background: var(--bg-2); }
.project-thumb-cell .thumb-svg { width: 100%; height: 100%; }
.project-card-body { padding: 12px 14px; }
.project-meta {
display: flex; gap: 6px;
font-size: 11.5px; color: var(--text-3);
margin-top: 4px;
}
.project-bar {
margin-top: 10px;
display: flex;
height: 4px;
border-radius: 99px;
overflow: hidden;
background: var(--bg-3);
gap: 1px;
}
/* ========== Upload ========== */
.field-label {
font-size: 10.5px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-4);
display: block;
margin-bottom: 6px;
}
.select-faux, .field-input {
height: 34px;
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-sm);
padding: 0 10px;
display: flex; align-items: center; gap: 8px;
color: var(--text-1);
font-size: 12.5px;
width: 100%;
cursor: pointer;
}
.field-input { font-size: 12.5px; outline: 0; }
.field-input:focus { border-color: var(--accent); background: var(--bg-2); }
.field-input.select { justify-content: space-between; }
.dropzone {
background: var(--bg-1);
border: 1.5px dashed var(--border-stronger);
border-radius: var(--r-lg);
padding: 40px 24px;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
cursor: pointer;
transition: background 120ms, border 120ms;
}
.dropzone:hover { background: var(--bg-2); border-color: var(--accent); }
.dropzone-formats { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; justify-content: center; }
.upload-row {
display: flex; align-items: center; gap: 12px;
padding: 12px 14px;
border-bottom: 1px solid var(--border);
}
.upload-row:last-child { border-bottom: 0; }
/* ========== Recorders ========== */
.recorders-list { display: flex; flex-direction: column; gap: 10px; }
.recorder-row {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
padding: 12px;
display: grid;
grid-template-columns: 220px 1fr 380px auto;
align-items: center;
gap: 16px;
transition: border 80ms;
}
.recorder-row:hover { border-color: var(--border-stronger); }
.recorder-row.recording { border-color: rgba(255,59,48,0.25); }
.recorder-row.error { border-color: rgba(255,91,91,0.25); }
.recorder-preview {
height: 56px;
border-radius: var(--r-sm);
background: var(--bg-2);
overflow: hidden;
position: relative;
}
.recorder-empty {
height: 100%;
display: grid; place-items: center;
color: var(--text-3);
}
.recorder-audio-prev {
height: 100%;
display: flex; align-items: center; gap: 10px;
padding: 0 12px;
}
.recorder-audio-prev .waveform { flex: 1; height: 80%; }
.recorder-info { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.recorder-sub {
font-size: 11.5px;
color: var(--text-3);
display: flex; gap: 6px;
}
.recorder-sub.mono { font-family: var(--font-mono); font-size: 11px; }
.recorder-stats {
display: grid;
grid-template-columns: 90px 90px 1fr;
gap: 12px;
}
.recorder-stat .stat-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 600;
color: var(--text-4);
margin-bottom: 3px;
}
.recorder-stat .stat-val { font-size: 12.5px; }
.recorder-actions { display: flex; align-items: center; gap: 4px; }
.rec-dot {
width: 8px; height: 8px;
border-radius: 50%;
background: currentColor;
}
/* ========== Capture / DeckLink ========== */
.decklink-card {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
overflow: hidden;
}
.decklink-head {
padding: 14px 16px;
display: flex; justify-content: space-between; align-items: center;
border-bottom: 1px solid var(--border);
}
.decklink-body {
padding: 24px;
display: grid;
grid-template-columns: 60px 1fr;
gap: 20px;
align-items: stretch;
background:
radial-gradient(ellipse at top, rgba(91,124,250,0.05), transparent 60%),
var(--bg-0);
}
.decklink-card-face {
background: linear-gradient(180deg, #1c1f28, #0d0e13);
border: 1px solid var(--border-strong);
border-radius: 6px;
display: flex; flex-direction: column;
justify-content: space-between;
padding: 12px 6px;
position: relative;
}
.decklink-label {
writing-mode: vertical-rl;
transform: rotate(180deg);
font-family: var(--font-mono);
font-size: 8.5px;
letter-spacing: 0.12em;
color: var(--text-3);
text-align: center;
margin: 0 auto;
}
.decklink-leds {
display: flex; gap: 6px;
justify-content: center;
}
.decklink-led {
width: 6px; height: 6px;
border-radius: 50%;
background: rgba(255,255,255,0.1);
}
.decklink-led.on {
background: var(--success);
box-shadow: 0 0 6px var(--success);
}
.bnc-ports { display: flex; flex-direction: column; gap: 10px; }
.bnc-port {
display: grid;
grid-template-columns: 32px 1fr auto;
align-items: center;
gap: 12px;
padding: 10px;
background: var(--bg-2);
border: 1px solid var(--border);
border-radius: var(--r-sm);
text-align: left;
cursor: pointer;
position: relative;
transition: background 80ms, border 80ms;
}
.bnc-port:hover { border-color: var(--border-stronger); }
.bnc-port.active { background: var(--bg-3); }
.bnc-port.active.live::before {
content: "";
position: absolute;
left: 0; top: 0; bottom: 0;
width: 2px;
background: var(--success);
}
.bnc-port.recording::before { background: var(--live); }
.bnc-port.active { box-shadow: inset 0 0 0 1px var(--accent); border-color: var(--accent); }
.bnc-connector {
width: 32px; height: 32px;
border-radius: 50%;
background: radial-gradient(circle, #1a1d24, #0a0c11);
display: grid; place-items: center;
border: 1.5px solid var(--border-stronger);
position: relative;
}
.bnc-pin {
width: 6px; height: 6px;
background: linear-gradient(135deg, #6b7280, #2a2f3a);
border-radius: 50%;
}
.bnc-ring {
position: absolute;
inset: 4px;
border: 1px dashed rgba(255,255,255,0.08);
border-radius: 50%;
}
.bnc-port.live .bnc-pin { background: linear-gradient(135deg, #FFC857, #C8862D); box-shadow: 0 0 8px rgba(255,200,87,0.4); }
.bnc-port.recording .bnc-pin { background: linear-gradient(135deg, #FF5C5C, #C8362D); box-shadow: 0 0 8px rgba(255,92,92,0.5); }
.bnc-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bnc-num { font-size: 12.5px; font-weight: 600; }
.bnc-label { font-size: 11px; color: var(--text-2); }
.bnc-sig { font-size: 10.5px; color: var(--text-3); }
.bnc-port.live .bnc-sig { color: var(--success); }
.bnc-rec {
width: 10px; height: 10px;
border-radius: 50%;
background: var(--live);
animation: pulse 1.2s ease-in-out infinite;
}
.bnc-signal-bar {
grid-column: 2 / 4;
height: 3px;
background: var(--bg-3);
border-radius: 99px;
overflow: hidden;
margin-top: 4px;
}
.bnc-signal-fill {
height: 100%;
background: linear-gradient(90deg, var(--success), var(--warning), var(--danger));
background-size: 200% 100%;
background-position: 0% 0;
transition: width 200ms;
}
.decklink-foot {
padding: 10px 16px;
border-top: 1px solid var(--border);
display: flex; gap: 20px;
font-size: 11.5px;
color: var(--text-3);
}
.decklink-foot strong { color: var(--text-1); font-weight: 600; }
.capture-detail {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
padding: 16px;
}
.capture-detail.empty {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
padding: 80px 24px;
color: var(--text-3);
}
.capture-preview {
position: relative;
aspect-ratio: 16 / 9;
border-radius: var(--r-md);
overflow: hidden;
background: #000;
}
.capture-overlay-meters {
position: absolute;
right: 8px; top: 8px;
display: flex; gap: 4px;
}
.capture-tc {
position: absolute;
bottom: 8px; right: 8px;
background: rgba(0,0,0,0.7);
padding: 3px 8px;
border-radius: 3px;
font-family: var(--font-mono);
font-size: 11px;
color: white;
}
.capture-stats {
margin-top: 16px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.capture-stat {
background: var(--bg-2);
border: 1px solid var(--border);
border-radius: var(--r-sm);
padding: 8px 10px;
}
.capture-stat-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 600;
color: var(--text-4);
}
.capture-stat-value { font-size: 13px; margin-top: 2px; }
/* ========== Monitors ========== */
.monitors-grid {
display: grid;
gap: 10px;
background: var(--bg-2);
border-radius: var(--r-lg);
padding: 10px;
}
.monitor-tile {
position: relative;
aspect-ratio: 16 / 9;
background: #000;
border-radius: var(--r-sm);
overflow: hidden;
border: 1px solid var(--border);
display: flex; flex-direction: column;
cursor: pointer;
}
.monitor-tile.audio { background: linear-gradient(135deg, hsl(180 30% 12%), hsl(200 25% 6%)); }
.monitor-tile-label {
position: absolute;
bottom: 0; left: 0; right: 0;
padding: 8px 10px;
background: linear-gradient(180deg, transparent, rgba(0,0,0,0.8));
display: flex; align-items: center; gap: 6px;
}
.monitor-tile-label .name {
color: white;
font-size: 11.5px;
font-weight: 500;
}
.monitor-tile-label .time {
margin-left: auto;
color: white;
font-family: var(--font-mono);
font-size: 10.5px;
}
/* ========== Jobs ========== */
.jobs-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.job-row {
display: grid;
grid-template-columns: 20px 110px 1fr 90px 200px 180px 80px 90px;
align-items: center;
gap: 12px;
padding: 10px 16px;
border-bottom: 1px solid var(--border);
font-size: 12.5px;
}
.job-row:last-child { border-bottom: 0; }
.job-row.head {
font-size: 10.5px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-4);
}
.job-progress-wrap {
display: flex; align-items: center; gap: 8px;
}
.job-progress-bar {
flex: 1;
height: 5px;
background: var(--bg-3);
border-radius: 99px;
overflow: hidden;
position: relative;
}
.job-progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), #7C9EFF);
background-size: 200% 100%;
animation: shimmer 2s linear infinite;
transition: width 300ms;
}
/* ========== Editor ========== */
.editor-shell {
display: flex; flex-direction: column;
height: 100%;
background: var(--bg-0);
}
.editor-topbar {
height: 44px;
border-bottom: 1px solid var(--border);
padding: 0 14px;
display: flex; align-items: center; gap: 10px;
}
.editor-body {
flex: 1;
display: grid;
grid-template-columns: 240px 1fr 240px;
min-height: 0;
}
.editor-bins, .editor-insp { background: var(--bg-1); border-right: 1px solid var(--border); overflow-y: auto; }
.editor-insp { border-right: 0; border-left: 1px solid var(--border); }
.editor-bin-item {
display: flex; align-items: center; gap: 8px;
padding: 6px;
border-radius: var(--r-sm);
cursor: pointer;
}
.editor-bin-item:hover { background: var(--hover); }
.editor-bin-thumb { width: 48px; aspect-ratio: 16/9; border-radius: 3px; overflow: hidden; background: var(--bg-2); flex-shrink: 0; }
.editor-viewer { background: #000; display: flex; flex-direction: column; }
.editor-canvas {
flex: 1;
position: relative;
background: #050608;
display: grid; place-items: center;
overflow: hidden;
max-height: 50vh;
}
.editor-canvas .thumb-svg { width: 100%; height: 100%; }
.editor-transport {
height: 44px;
display: flex; align-items: center; gap: 8px;
padding: 0 14px;
background: var(--bg-1);
border-top: 1px solid var(--border);
}
.editor-timeline {
background: var(--bg-0);
border-top: 1px solid var(--border);
height: 240px;
display: flex; flex-direction: column;
position: relative;
overflow: hidden;
}
.editor-timeline-head {
height: 32px;
display: flex; align-items: center;
padding: 0 12px;
gap: 8px;
border-bottom: 1px solid var(--border);
}
.timeline-ruler {
height: 22px;
display: flex;
padding-left: 40px;
border-bottom: 1px solid var(--border);
background: var(--bg-1);
}
.ruler-tick {
flex: 1;
border-left: 1px solid var(--border);
font-size: 9.5px;
color: var(--text-4);
padding: 4px 4px 0;
}
.timeline-track {
display: grid;
grid-template-columns: 40px 1fr;
align-items: stretch;
height: 42px;
border-bottom: 1px solid var(--border);
}
.timeline-track-label {
background: var(--bg-1);
display: grid; place-items: center;
font-size: 10.5px;
font-family: var(--font-mono);
font-weight: 600;
color: var(--text-3);
border-right: 1px solid var(--border);
}
.timeline-track-lane {
position: relative;
background:
repeating-linear-gradient(90deg, transparent 0 49px, rgba(255,255,255,0.02) 49px 50px);
}
.clip {
position: absolute;
top: 4px; bottom: 4px;
border-radius: 3px;
overflow: hidden;
display: flex; align-items: center;
cursor: pointer;
border: 1px solid rgba(0,0,0,0.3);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.15);
}
.clip-label {
position: absolute;
left: 6px; top: 3px;
font-size: 10px;
font-weight: 600;
color: white;
z-index: 2;
text-shadow: 0 1px 1px rgba(0,0,0,0.4);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.clip-film { display: flex; flex: 1; height: 100%; }
.clip-film-cell { flex: 1; overflow: hidden; }
.clip-film-cell .thumb-svg { width: 100%; height: 100%; }
.clip.audio { background: var(--success) !important; }
.clip-wave { width: 100%; height: 100%; padding: 4px; opacity: 0.6; }
.clip-wave .waveform { width: 100%; height: 100%; }
.timeline-playhead {
position: absolute;
top: 32px; bottom: 0;
width: 1.5px;
background: var(--live);
pointer-events: none;
z-index: 5;
}
.timeline-playhead::before {
content: "";
position: absolute;
top: -4px; left: 50%;
width: 10px; height: 10px;
background: var(--live);
transform: translateX(-50%) rotate(45deg);
}
/* ========== Admin tables ========== */
/* ── Row popover menu (Users, etc.) ────────────────────────────────── */
.row-menu {
position: absolute;
right: 0;
top: calc(100% + 4px);
z-index: 20;
min-width: 180px;
background: var(--bg-1);
border: 1px solid var(--border-stronger);
border-radius: 6px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
padding: 4px;
display: flex;
flex-direction: column;
}
.row-menu button {
display: flex;
align-items: center;
gap: 8px;
background: transparent;
border: 0;
color: var(--text-1);
font-size: 12.5px;
padding: 7px 10px;
border-radius: 4px;
cursor: pointer;
text-align: left;
}
.row-menu button:hover { background: var(--bg-3); }
.row-menu button.danger:hover { background: var(--danger-soft); color: var(--danger); }
.user-row, .token-row, .container-row, .schedule-row {
display: grid;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
font-size: 12.5px;
}
.user-row { grid-template-columns: 1.5fr 100px 1.5fr 120px 40px; }
.token-row { grid-template-columns: 1.4fr 1.4fr 110px 110px 100px 40px; }
.container-row { grid-template-columns: 1.4fr 1.4fr 140px 140px 100px 1.4fr 110px; }
.schedule-row { grid-template-columns: 1.6fr 1.2fr 1.2fr 90px 110px 110px 150px; }
.user-row.head, .token-row.head, .container-row.head, .schedule-row.head {
font-size: 10.5px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-4);
}
.user-row:last-child, .token-row:last-child, .container-row:last-child, .schedule-row:last-child { border-bottom: 0; }
.token-row.revoked { opacity: 0.5; }
/* ========== Schedule (EPG timeline) ==========
Broadcast-control-room schedule. Recorders are rows, time is the horizontal
axis. The single scrollable .epg container uses a 2-column / 2-row grid
so the gutter (left) and ruler (top) stay sticky during scroll. */
.epg-page {
display: flex; flex-direction: column;
height: 100%;
min-height: 0;
--epg-pph: 88px; /* pixels per hour, overridden inline per view */
--epg-row-h: 60px;
--epg-gutter-w: 220px;
--epg-ruler-h: 32px;
}
/* ---- Status strip (always on top of the schedule screen) -------------- */
.epg-status {
padding: 10px 20px 12px;
background: linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 100%);
border-bottom: 1px solid var(--border);
}
.epg-status-row {
display: flex; align-items: center; gap: 10px;
min-height: 22px;
font-size: 13px;
}
.epg-status-row.sub { margin-top: 2px; font-size: 12px; color: var(--text-3); }
.epg-status-dot {
width: 8px; height: 8px; border-radius: 50%;
flex-shrink: 0;
}
.epg-status-dot.live {
background: var(--live);
box-shadow: 0 0 0 4px rgba(255, 59, 48, 0.16);
animation: _epg_live_pulse 1.6s ease-out infinite;
}
.epg-status-dot.idle { background: var(--text-4); }
.epg-status-label {
font-weight: 600; letter-spacing: 0.02em;
text-transform: uppercase; font-size: 10.5px;
color: var(--text-2);
}
.epg-status-label.muted { color: var(--text-4); }
.epg-status-active {
display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
flex: 1; min-width: 0;
}
.epg-status-pill {
display: inline-flex; align-items: center; gap: 8px;
padding: 2px 10px 2px 0;
background: var(--bg-2);
border: 1px solid var(--border-strong);
border-radius: 6px;
font-size: 12px;
overflow: hidden;
}
.epg-status-pill-bar {
width: 3px; align-self: stretch;
background: var(--text-3);
}
.epg-status-pill-name { font-weight: 600; padding-left: 8px; }
.epg-status-pill-rec { color: var(--text-3); font-size: 11px; padding-left: 8px; border-left: 1px solid var(--border); }
.epg-status-pill-time { color: var(--text-2); font-size: 11px; padding-left: 8px; border-left: 1px solid var(--border); }
.epg-status-next { font-weight: 500; color: var(--text-1); }
.epg-status-next-rec { color: var(--text-3); padding-left: 6px; }
.epg-status-next-time { color: var(--accent-text); padding-left: 6px; }
@keyframes _epg_live_pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 59, 48, 0.55); }
70% { box-shadow: 0 0 0 10px rgba(255, 59, 48, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 59, 48, 0); }
}
/* ---- Toolbar (date nav + view tabs + CTA) ------------------------------ */
.epg-toolbar {
display: flex; align-items: center; gap: 8px;
padding: 10px 20px;
border-bottom: 1px solid var(--border);
background: var(--bg-1);
}
.epg-toolbar .spacer { flex: 1; }
.epg-date {
display: flex; align-items: center; gap: 4px;
}
.epg-date-label {
font-size: 15px; font-weight: 600;
min-width: 240px; padding: 0 6px;
letter-spacing: -0.01em;
}
/* ---- Today: scrollable timeline ---------------------------------------- */
.epg {
flex: 1; min-height: 0;
display: grid;
grid-template-columns: var(--epg-gutter-w) 1fr;
grid-template-rows: var(--epg-ruler-h) 1fr;
overflow: auto;
background: var(--bg-0);
position: relative;
}
.epg-corner {
position: sticky; top: 0; left: 0;
grid-row: 1; grid-column: 1;
z-index: 4;
background: var(--bg-1);
border-right: 1px solid var(--border);
border-bottom: 1px solid var(--border);
display: flex; align-items: center;
padding: 0 14px;
font-size: 11px; color: var(--text-3);
letter-spacing: 0.02em;
}
.epg-gutter {
position: sticky; left: 0;
grid-row: 2; grid-column: 1;
z-index: 2;
background: var(--bg-1);
border-right: 1px solid var(--border);
}
.epg-gutter-rows { display: flex; flex-direction: column; }
.epg-gutter-row {
display: flex; align-items: center; gap: 10px;
height: var(--epg-row-h);
padding: 0 14px;
border-bottom: 1px solid var(--border);
position: relative;
}
.epg-gutter-row:last-child { border-bottom: 0; }
.epg-gutter-status {
width: 8px; height: 8px; border-radius: 50%;
flex-shrink: 0;
background: var(--text-4);
}
.epg-gutter-status.live {
background: var(--live);
box-shadow: 0 0 0 3px rgba(255, 59, 48, 0.18);
}
.epg-gutter-status.err { background: var(--danger); }
.epg-gutter-status.idle { background: var(--text-4); }
.epg-gutter-meta { display: flex; flex-direction: column; min-width: 0; }
.epg-gutter-name {
font-size: 12.5px; font-weight: 600;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
color: var(--text-1);
}
.epg-gutter-sub {
display: flex; align-items: center; gap: 6px;
font-size: 10.5px; color: var(--text-3);
letter-spacing: 0.06em; text-transform: uppercase;
}
.epg-gutter-dot {
display: inline-block;
width: 7px; height: 7px; border-radius: 50%;
}
.epg-canvas-head {
position: sticky; top: 0;
grid-row: 1; grid-column: 2;
z-index: 3;
background: var(--bg-1);
border-bottom: 1px solid var(--border);
}
.epg-canvas {
grid-row: 2; grid-column: 2;
position: relative;
background:
/* hour-band rhythm — alternating subtle stripe every other hour */
repeating-linear-gradient(
to right,
transparent 0,
transparent var(--epg-pph),
rgba(255,255,255,0.012) var(--epg-pph),
rgba(255,255,255,0.012) calc(var(--epg-pph) * 2)
),
/* hour separator lines every hour */
repeating-linear-gradient(
to right,
var(--border) 0,
var(--border) 1px,
transparent 1px,
transparent var(--epg-pph)
);
}
/* Hour ruler */
.epg-ruler {
position: relative;
height: var(--epg-ruler-h);
}
.epg-ruler-tick {
position: absolute; top: 0; bottom: 0;
padding-left: 8px;
display: flex; align-items: center;
font-size: 10.5px; font-weight: 600;
letter-spacing: 0.04em;
color: var(--text-3);
border-left: 1px solid var(--border);
}
.epg-ruler-tick.end { border-left: 0; }
/* Recorder rows + event blocks */
.epg-rows { display: flex; flex-direction: column; position: relative; }
.epg-row {
position: relative;
height: var(--epg-row-h);
border-bottom: 1px solid var(--border);
cursor: copy;
}
.epg-row:last-child { border-bottom: 0; }
.epg-block {
position: absolute;
top: 8px;
height: calc(var(--epg-row-h) - 16px);
display: flex; align-items: center; gap: 8px;
padding: 0 10px 0 14px;
background: var(--bg-2);
border: 1px solid var(--border-strong);
border-radius: 5px;
font-size: 11.5px;
text-align: left;
color: var(--text-1);
cursor: pointer;
overflow: hidden;
transition: background 80ms, border-color 80ms, transform 80ms;
}
.epg-block:hover {
background: var(--bg-3);
border-color: var(--border-stronger);
transform: translateY(-1px);
z-index: 1;
}
.epg-block-bar {
position: absolute;
left: 0; top: 0; bottom: 0;
width: 4px;
background: var(--epg-block-color, var(--accent));
}
.epg-block-name {
flex: 1; min-width: 0;
font-weight: 600;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.epg-block-time {
font-size: 10px; color: var(--text-3);
flex-shrink: 0;
}
.epg-block-glyph {
display: inline-flex; align-items: center; justify-content: center;
flex-shrink: 0;
width: 14px; height: 14px;
border-radius: 3px;
font-size: 9px; font-weight: 700;
font-family: var(--font-mono);
}
.epg-block-glyph.live { color: var(--live); background: rgba(255, 59, 48, 0.16); }
.epg-block-glyph.failed { color: var(--danger); background: var(--danger-soft); }
.epg-block.live {
background: linear-gradient(180deg, rgba(255,59,48,0.16) 0%, rgba(255,59,48,0.08) 100%);
border-color: var(--live);
box-shadow: 0 0 0 1px rgba(255, 59, 48, 0.25);
}
.epg-block.failed {
background: var(--danger-soft);
border-color: var(--danger);
}
.epg-block.failed .epg-block-bar { background: var(--danger); }
.epg-block.past { opacity: 0.55; }
.epg-block.past:hover { opacity: 0.85; }
/* Now-line: vertical hot-red line that ticks across the timeline */
.epg-now {
position: absolute;
top: 0; bottom: 0;
width: 1px;
background: var(--live);
pointer-events: none;
z-index: 2;
box-shadow: 0 0 6px rgba(255, 59, 48, 0.45);
}
.epg-now-pip {
position: absolute;
top: -3px; left: -4px;
width: 9px; height: 9px;
border-radius: 50%;
background: var(--live);
box-shadow: 0 0 6px rgba(255, 59, 48, 0.7);
}
/* ---- Week view: 7 day-sections stacked vertically --------------------- */
.epg-week {
flex: 1; min-height: 0;
overflow: auto;
background: var(--bg-0);
padding: 0 0 24px;
}
.epg-week-day {
border-bottom: 1px solid var(--border);
}
.epg-week-day:last-child { border-bottom: 0; }
.epg-week-day.today { background: linear-gradient(180deg, rgba(91,124,250,0.04) 0%, transparent 80%); }
.epg-week-dayhead {
display: flex; align-items: baseline; gap: 10px;
padding: 10px 20px 6px;
font-size: 12px; font-weight: 600;
color: var(--text-2);
position: sticky; left: 0;
z-index: 1;
}
.epg-week-dayname { letter-spacing: 0.02em; }
.epg-week-todaypip {
font-size: 9.5px; font-weight: 700;
text-transform: uppercase; letter-spacing: 0.08em;
color: var(--accent-text);
background: var(--accent-soft);
padding: 1px 6px; border-radius: 99px;
}
.epg-week-row-wrap {
position: relative;
padding: 0 0 8px 20px;
background:
repeating-linear-gradient(
to right,
transparent 0,
transparent var(--epg-pph),
rgba(255,255,255,0.012) var(--epg-pph),
rgba(255,255,255,0.012) calc(var(--epg-pph) * 2)
);
}
/* ---- List view (panel reuses .schedule-row from the row-tables block) - */
.epg-list { padding: 20px; flex: 1; min-height: 0; overflow: auto; }
/* ---- Empty states ----------------------------------------------------- */
.epg-empty {
flex: 1;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 10px; padding: 80px 20px;
color: var(--text-3);
}
.epg-empty-title {
font-size: 15px; font-weight: 600;
color: var(--text-2);
}
.epg-empty-sub {
font-size: 12.5px;
}
/* ========== Cluster ========== */
.cluster-canvas {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
overflow: hidden;
}
/* ========== Settings ========== */
.settings-nav {
display: flex; flex-direction: column; gap: 2px;
position: sticky; top: 0;
}
.settings-nav-item {
display: flex; align-items: center; gap: 8px;
padding: 0 10px;
height: 32px;
border-radius: var(--r-sm);
color: var(--text-2);
font-size: 12.5px;
cursor: pointer;
}
.settings-nav-item:hover { background: var(--hover); color: var(--text-1); }
.settings-nav-item.active { background: var(--accent-soft); color: var(--accent-text); }
.settings-card {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
overflow: hidden;
}
.settings-card-head {
padding: 16px;
display: flex; align-items: flex-start; gap: 12px;
border-bottom: 1px solid var(--border);
}
.settings-card-icon {
width: 36px; height: 36px;
border-radius: var(--r-sm);
background: var(--accent-soft);
color: var(--accent);
display: grid; place-items: center;
flex-shrink: 0;
}
.settings-card-body { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.field { display: flex; flex-direction: column; gap: 4px; }
.field-input-wrap { display: flex; gap: 6px; align-items: center; }
.checkbox-row {
display: flex; align-items: center; gap: 8px;
font-size: 12.5px; color: var(--text-1);
cursor: pointer;
}
.checkbox-row input { accent-color: var(--accent); }
/* ========== Tokens parody ========== */
.token-hero {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 14px;
margin-bottom: 16px;
}
.token-burn-card, .token-actual-card {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
padding: 18px 20px;
position: relative;
overflow: hidden;
}
.token-burn-card {
background:
radial-gradient(ellipse at top right, rgba(255,91,91,0.12), transparent 60%),
var(--bg-1);
border-color: rgba(255,91,91,0.18);
}
.token-actual-card {
background:
radial-gradient(ellipse at bottom left, rgba(45,212,168,0.10), transparent 60%),
var(--bg-1);
border-color: rgba(45,212,168,0.18);
}
.token-card-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 700;
color: var(--text-4);
margin-bottom: 8px;
}
.token-counter {
display: flex; align-items: baseline; gap: 12px;
}
.token-flame { font-size: 32px; line-height: 1; }
.token-big {
font-size: 48px;
font-weight: 700;
letter-spacing: -0.03em;
font-variant-numeric: tabular-nums;
background: linear-gradient(180deg, #FF9D6B, #FF5B5B);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
line-height: 1.05;
}
.token-rate {
font-size: 12px;
display: flex; align-items: baseline;
font-family: var(--font-mono);
}
.token-actual-amount {
display: flex; align-items: baseline; gap: 0;
color: var(--success);
margin-bottom: 8px;
}
.token-comparison {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
padding: 0 0 12px;
margin-bottom: 16px;
overflow: hidden;
}
.token-compare-chart { padding: 0 16px; }
.token-compare-legend {
display: flex; gap: 24px;
padding: 8px 0 0;
font-size: 12px;
color: var(--text-2);
}
.token-compare-legend .dot {
display: inline-block;
width: 10px; height: 10px;
border-radius: 50%;
margin-right: 6px;
vertical-align: middle;
}
.token-grid {
display: grid;
grid-template-columns: 1fr 1.4fr;
gap: 14px;
}
.token-event {
display: flex; align-items: center; gap: 12px;
padding: 10px 14px;
border-bottom: 1px solid var(--border);
}
.token-event:last-child { border-bottom: 0; }
.token-event.fresh { animation: tokenFresh 800ms ease; }
@keyframes tokenFresh {
0% { background: rgba(255,91,91,0.15); transform: translateX(-4px); }
100% { background: transparent; transform: none; }
}
.token-tiers {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.token-tier {
background: var(--bg-1);
border: 1px solid var(--border);
border-radius: var(--r-lg);
padding: 16px 14px;
display: flex; flex-direction: column;
gap: 6px;
position: relative;
}
.token-tier.popular {
border-color: var(--accent);
background: linear-gradient(180deg, var(--accent-soft) 0%, var(--bg-1) 40%);
box-shadow: 0 0 24px rgba(91,124,250,0.10);
}
.token-tier-badge {
position: absolute;
top: -8px; right: 12px;
background: var(--accent);
color: white;
font-size: 9.5px;
font-weight: 700;
letter-spacing: 0.06em;
padding: 3px 8px;
border-radius: 99px;
}
.token-tier-name { font-size: 13px; font-weight: 700; letter-spacing: -0.01em; }
.token-tier-desc { font-size: 11px; color: var(--text-3); min-height: 32px; line-height: 1.4; }
.token-tier-price { margin-top: 6px; display: flex; align-items: baseline; }
.token-tier-tokens { font-size: 11px; color: var(--text-3); padding-top: 4px; border-top: 1px solid var(--border); }
.token-footnote {
margin-top: 20px;
padding: 14px 16px;
background: var(--warning-soft);
border: 1px solid rgba(245,166,35,0.2);
border-radius: var(--r-md);
display: flex; gap: 12px;
font-size: 12.5px;
color: var(--text-2);
line-height: 1.55;
}
.token-footnote svg { color: var(--warning); flex-shrink: 0; margin-top: 2px; }
.token-footnote strong { color: var(--warning); }
/* ========== Global Search ========== */
.search-wrap {
position: relative;
flex-shrink: 0;
}
.search-wrap .search {
width: 280px;
}
.search-wrap .search.is-open {
border-color: var(--accent);
background: var(--bg-2);
}
.search-results {
position: absolute;
top: calc(100% + 6px);
right: 0;
width: 440px;
max-width: 90vw;
max-height: 460px;
overflow-y: auto;
background: var(--bg-1);
border: 1px solid var(--border-stronger);
border-radius: var(--r-md);
box-shadow: var(--shadow-pop);
padding: 4px;
z-index: 60;
}
.search-empty {
padding: 18px 12px;
text-align: center;
font-size: 12px;
color: var(--text-3);
}
.search-result {
display: grid;
grid-template-columns: 24px 1fr auto;
align-items: center;
gap: 10px;
padding: 8px 10px;
border-radius: var(--r-sm);
cursor: pointer;
color: var(--text-1);
}
.search-result.active {
background: var(--accent-soft);
}
.search-result-icon {
width: 24px; height: 24px;
border-radius: 5px;
background: var(--bg-3);
color: var(--text-2);
display: grid; place-items: center;
flex-shrink: 0;
}
.search-result.active .search-result-icon {
background: var(--accent-soft-2);
color: var(--accent-text);
}
.search-result-text {
min-width: 0;
display: flex;
flex-direction: column;
gap: 1px;
}
.search-result-label {
font-size: 12.5px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.search-result-sub {
font-size: 11px;
color: var(--text-3);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.search-result-kind {
font-family: var(--font-mono);
font-size: 9.5px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-4);
background: var(--bg-2);
border: 1px solid var(--border);
padding: 2px 6px;
border-radius: 99px;
flex-shrink: 0;
}
.search-result-kind.kind-asset { color: #B4C3FF; border-color: rgba(91,124,250,0.25); }
.search-result-kind.kind-project { color: #FFD89B; border-color: rgba(245,166,35,0.25); }
.search-result-kind.kind-recorder { color: #FFAFAF; border-color: rgba(255,91,91,0.25); }
.search-result-kind.kind-job { color: #9EE7D2; border-color: rgba(45,212,168,0.25); }
.search-result-kind.kind-user { color: #D5B8FF; border-color: rgba(181,124,250,0.25); }
.search-result-kind.kind-nav { color: var(--text-2); }
/* ========== Asset right-click menu ========== */
.ctx-menu {
position: fixed;
z-index: 80;
min-width: 220px;
max-width: 280px;
background: var(--bg-1);
border: 1px solid var(--border-stronger);
border-radius: var(--r-md);
box-shadow: var(--shadow-pop);
padding: 4px;
display: flex;
flex-direction: column;
font-size: 12.5px;
}
.ctx-menu .ctx-header {
padding: 8px 10px 6px;
font-size: 11px;
font-weight: 600;
color: var(--text-3);
border-bottom: 1px solid var(--border);
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ctx-menu .ctx-divider {
height: 1px;
background: var(--border);
margin: 4px 2px;
}
.ctx-menu .ctx-section-label {
font-size: 9.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-4);
padding: 6px 10px 4px;
}
.ctx-menu .ctx-empty {
padding: 6px 10px 10px;
font-size: 11.5px;
color: var(--text-3);
font-style: italic;
}
.ctx-menu button {
display: flex;
align-items: center;
gap: 8px;
background: transparent;
border: 0;
color: var(--text-1);
font-size: 12.5px;
padding: 7px 10px;
border-radius: 4px;
cursor: pointer;
text-align: left;
width: 100%;
}
.ctx-menu button svg { color: var(--text-3); flex-shrink: 0; }
.ctx-menu button:hover:not(:disabled) { background: var(--bg-3); }
.ctx-menu button:hover:not(:disabled) svg { color: var(--text-1); }
.ctx-menu button:disabled {
opacity: 0.5;
cursor: default;
}
.ctx-menu button.danger { color: var(--danger); }
.ctx-menu button.danger svg { color: var(--danger); }
.ctx-menu button.danger:hover:not(:disabled) {
background: var(--danger-soft);
}