/* gello-pi touch console
   Target: 1280×720 (Pi 5 Touch Display 2 landscape, rotated 90°).
   No vertical scroll anywhere — content area is sized to exactly fit.
   Dark base (lab-friendly low-light) with Centific brand accents.
   Typography: DM Sans (web-native equivalent of Centific Arial). */

:root {
  /* --- Centific brand tokens --- */
  --c-magenta:    #CD128A;
  --c-pink:       #EF43B3;
  --c-violet:     #A12AD3;
  --c-blush:      #FACAE9;
  --c-lavender:   #EDE7F6;
  --c-cool-white: #F2F5FB;
  --c-silver:     #A3A4A6;
  --c-gray:       #707070;

  /* --- Dark-theme surfaces (lab UI, not slide deck) --- */
  --bg:           #0e1014;
  --panel:        #1a1d24;
  --panel-2:      #232730;
  --border:       #303644;
  --text:         #e6e7ea;
  --text-dim:     #8a8e98;

  /* --- Semantic --- */
  --accent:       var(--c-pink);
  --accent-strong:var(--c-magenta);
  --stat:         var(--c-violet);
  --ok:           #4caf50;
  --warn:         #ffb300;
  --bad:          #e63946;
  --rec:          var(--c-magenta);

  /* --- Layout (1280×720) --- */
  --topbar-h:     56px;
  --content-h:    calc(720px - var(--topbar-h));

  --radius: 4px;
}

* { box-sizing: border-box; }
html, body {
    margin: 0; padding: 0;
    width: 1280px; height: 720px;
    overflow: hidden;
    font-family: 'DM Sans', Arial, sans-serif;
    background: var(--bg);
    color: var(--text);
    font-size: 18px;             /* bumped from 16 */
    -webkit-font-smoothing: antialiased;
}

/* ---------- scrollbars (wider, easier to grab on touchscreen) ---------- */
*::-webkit-scrollbar              { width: 16px; height: 16px; }
*::-webkit-scrollbar-track        { background: var(--bg); }
*::-webkit-scrollbar-thumb        { background: #4a5060; border: 2px solid var(--bg); border-radius: 8px; }
*::-webkit-scrollbar-thumb:hover  { background: var(--c-pink); }
* { scrollbar-color: #4a5060 var(--bg); scrollbar-width: auto; }

/* ---------- top bar ---------- */
.topbar {
    height: var(--topbar-h);
    display: flex; align-items: center;
    background: var(--panel);
    border-bottom: 1px solid var(--border);
    /* extra right padding so the ⟳ refresh button isn't clipped by the
       1280px DSI display's rightmost pixels (was 12px → tip of the icon
       was getting eaten). */
    padding: 0 22px 0 12px;
    gap: 14px;
}
.brand { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.brand img { display: block; }
.rig-host {
    padding: 3px 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 3px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
    white-space: nowrap;        /* never wrap to 2 lines */
    flex-shrink: 0;
}

.tabs { display: flex; gap: 4px; flex: 1; justify-content: center; }
.tab {
    padding: 8px 18px;
    border-radius: 3px;
    color: var(--text-dim);
    font-size: 18px;
    font-weight: 500;
    text-decoration: none;
    border: 1px solid transparent;
}
.tab.active {
    color: var(--text);
    background: var(--panel-2);
    border-color: var(--border);
    box-shadow: inset 0 -2px 0 0 var(--c-pink);
}

.topright { display: flex; align-items: center; gap: 8px; }
.connbar { display: flex; gap: 4px; align-items: center; }
.chip-group { display: flex; gap: 1px; padding: 1px; border-radius: 3px; background: rgba(255,255,255,0.03); }
.chip {
    padding: 3px 6px;
    border-radius: 2px;
    background: #3a3f4a;
    color: #c4c8d2;
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    border: 1px solid transparent;
    white-space: nowrap;
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
}
.chip.ok    { background: rgba(76,175,80,0.22); color: #7fda84; border-color: rgba(76,175,80,0.45); }
.chip.bad   { background: rgba(230,57,70,0.22); color: #ff7e88; border-color: rgba(230,57,70,0.45); }
.chip.estop.ok  { background: rgba(76,175,80,0.22); color: #7fda84; border-color: rgba(76,175,80,0.45); }
.chip.estop.bad { background: var(--bad); color: #fff; border-color: var(--bad); animation: pulse 1s infinite; font-weight: 700; }
.chip.chip-button { cursor: pointer; font-family: inherit; }
.chip.chip-button:hover  { background: var(--bad); color: #fff; border-color: var(--bad); }
.chip.chip-button:active { transform: translateY(1px); }
.chip.chip-button.estop-fired { background: #fff !important; color: var(--bad) !important; border-color: var(--bad) !important; }
@keyframes pulse { 50% { opacity: 0.5; } }

.clock {
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
    font-size: 13px; color: var(--text-dim);
    min-width: 64px; text-align: right;
}

.iconbtn {
    width: 36px; height: 36px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--text);
    font-size: 18px;
    cursor: pointer;
}
.iconbtn:active { background: var(--c-pink); }

/* ---------- content area ---------- */
.content {
    height: var(--content-h);
    padding: 12px;
    overflow: hidden;
    display: grid;
    gap: 12px;
}
.content[data-tab="teleop"] {
    padding-bottom: 0;   /* let the cam-banner sit flush at the bottom */
}
/* teleop tab: bottom row reserved for full-width cameras. Row 2 is
   pulled flush to the screen edges via negative margin on the cameras
   panel — see .cam-banner below. */
/* Teleop: 50/50 split top row (task list | task detail), a thin auto-sized
   controls strip under the task list, and the camera banner across the
   bottom. Task detail spans rows 1+2 so steps have room to breathe. */
.content[data-tab="teleop"]     { grid-template-columns: 3fr 2fr; grid-template-rows: 1fr auto 300px; }
.content[data-tab="policy"]     { grid-template-columns: 3fr 1fr; grid-template-rows: 1fr 300px; padding-bottom: 0; }
.content[data-tab="recordings"] { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
/* Two columns, single row spanning full content height. Each column is its
   own flex-column container (.col-stack) so the panels inside size to their
   content without being stretched by the OTHER column's content height —
   that was leaving empty gaps below System/Calibration when col 1's Storage
   + GPU was taller. */
.content[data-tab="settings"]   { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr; }
.content[data-tab="settings"] .col-stack {
    display: flex; flex-direction: column; gap: 12px;
    min-height: 0; min-width: 0; height: 100%;
}
/* Settings tab — bumped fonts for readability. */
.content[data-tab="settings"] .panel h2 { font-size: 13px; }

.panel {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 8px 10px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-height: 0;
}
.panel h2 {
    margin: 0 0 6px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--c-silver);
}
.panel-body { flex: 1; min-height: 0; overflow: auto; }

/* ---------- buttons ---------- */
.big-row { display: flex; gap: 8px; flex-wrap: wrap; }
.big {
    flex: 1;
    min-width: 110px;
    min-height: 48px;
    padding: 8px 12px;
    border-radius: 4px;
    border: 1px solid var(--border);
    background: var(--panel-2);
    color: var(--text);
    font-family: inherit;
    font-size: 17px;
    font-weight: 500;
    cursor: pointer;
}
/* big tall variant — used in the teleop controls column for stacked
   easy-to-tap buttons */
.big.tall { min-height: 70px; font-size: 18px; }
.big:active   { transform: translateY(1px); background: var(--c-pink); }
.big.selected { background: var(--c-pink); border-color: var(--c-magenta); color:#fff; }
.big.danger   { background: #4a2a2a; border-color: #6a3a3a; }
.big.danger:active { background: var(--bad); }
.big.rec      { background: #4a1f1f; border-color: var(--rec); color: #fff; }
.big.rec:active { background: var(--rec); }
.big.estop-btn { background: var(--bad); border-color: #ff5057; min-height: 60px; font-size: 16px; font-weight: 700; }
.big.estop-btn.estop-fired { background: #fff; color: var(--bad); border-color: var(--bad); }
.big.go       { background: var(--c-magenta); border-color: var(--c-magenta); color: #fff; }
.big.go:active{ background: var(--c-pink); }

/* ---- toggle buttons (start↔stop) ---- */
.big.toggle-teleop { font-size: 22px; font-weight: 700; transition: background .15s, border-color .15s; }
.big.toggle-teleop.teleop-off { background: #1f7a3a; border-color: #2da14e; color: #fff; }
.big.toggle-teleop.teleop-off:active { background: #2da14e; }
.big.toggle-teleop.teleop-on  { background: var(--bad); border-color: #ff5057; color: #fff; }
.big.toggle-teleop.teleop-on:active   { background: #ff5057; }

.big.toggle-rec { font-size: 22px; font-weight: 700; transition: background .15s, border-color .15s; }
.big.toggle-rec.rec-off { background: #5a2424; border-color: #7a2828; color: #ffb3b3; }
.big.toggle-rec.rec-off:active { background: var(--rec); color: #fff; }
.big.toggle-rec.rec-on  {
    background: var(--rec); border-color: #ff3030; color: #fff;
    animation: rec-pulse 1.2s infinite;
}
.big.toggle-rec.rec-on:active { background: #ff3030; }
@keyframes rec-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(214, 40, 40, 0.45); }
    50%      { box-shadow: 0 0 0 6px rgba(214, 40, 40, 0.0); }
}

.big.toggle-policy { font-size: 18px; font-weight: 700; min-height: 64px;
                     transition: background .15s, border-color .15s; }
.big.toggle-policy.policy-off { background: var(--c-violet); border-color: var(--c-violet); color: #fff; }
.big.toggle-policy.policy-off:active { background: var(--c-pink); }
.big.toggle-policy.policy-on  { background: var(--bad); border-color: #ff5057; color: #fff; }
.big.toggle-policy.policy-on:active   { background: #ff5057; }

.small {
    padding: 5px 10px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 3px;
    color: var(--text);
    font-family: inherit;
    font-size: 11px;
    cursor: pointer;
}
.small:active { background: var(--c-pink); }
.small.danger { background: #4a2a2a; border-color: #6a3a3a; }
.small.go     { background: var(--c-magenta); border-color: var(--c-magenta); color: #fff; }
.small.reject { background: #4a2a2a; border-color: var(--bad); }

/* ---------- teleop tab ---------- */
.task-filters { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; }
.task-input, .task-select {
    min-height: 34px;
    padding: 5px 10px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 3px;
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
}
.task-input { flex: 1; min-width: 80px; }
.checkbox { font-size: 11px; color: var(--text-dim); display: flex; gap: 4px; align-items: center; white-space: nowrap; }
.checkbox input { width: 16px; height: 16px; }

.task-list {
    flex: 1; min-height: 0;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 3px;
    background: var(--bg);
    padding: 4px;
}
.task-cat-block { margin-bottom: 6px; }
.task-cat {
    background: #1f3550;
    padding: 5px 12px;
    font-size: 14px;
    font-weight: 700;
    border-radius: 2px;
    color: #c4d4e8;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-bottom: 3px;
}
/* Task card is a 3-column grid:
     [ name (fixed 200px) | omni-model focus (flex) | badges (auto) ]
   Header row above the list uses the same template for column alignment. */
.task-list-head, .task-card {
    display: grid;
    grid-template-columns: 200px 1fr auto;
    gap: 10px;
    width: 100%;
    align-items: center;
}
.task-list-head {
    padding: 4px 10px 6px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-silver);
    border-bottom: 1px solid var(--border);
    margin: 0 0 4px;
}
.task-card {
    padding: 8px 12px; margin: 3px 0;
    background: var(--panel-2);
    border: 1px solid transparent;
    border-radius: 2px;
    color: var(--text);
    font-family: inherit;
    font-size: 16px;
    text-align: left;
    cursor: pointer;
}
.task-focus {
    font-size: 14px;
    color: var(--text-dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.task-badges { display: flex; gap: 4px; justify-content: flex-end; }
.task-card:hover .task-focus { color: var(--text); white-space: normal; }
.task-card.selected .task-focus { color: rgba(255,255,255,0.85); }
.task-card:active   { background: var(--c-pink); }
.task-card.selected { background: var(--c-magenta); border-color: var(--c-pink); color:#fff; }
.task-name { flex: 1; line-height: 1.2; }
.badge {
    padding: 1px 6px;
    border-radius: 8px;
    font-size: 9px; font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: #555; color: #fff; white-space: nowrap;
}
.badge.priority   { background: var(--warn); color: #000; }
.badge.diff-easy  { background: #2e7d32; }
.badge.diff-modrate, .badge.diff-moderate { background: #f57c00; }
.badge.diff-hard  { background: #c62828; }
.dim { color: var(--text-dim); }

.selected-line {
    margin-top: 6px;
    padding: 6px 8px;
    background: var(--bg);
    border-radius: 3px;
    font-size: 11px;
    color: var(--text-dim);
    border-left: 2px solid var(--c-pink);
    max-height: 38px; overflow: hidden;
}
.selected-line strong { color: var(--text); font-weight: 500; }

/* Task detail panel lives in its own column on the teleop tab.
   No height cap — the column has all the vertical room it needs for steps. */
.task-detail-panel { min-width: 0; }
.task-detail {
    padding: 10px 12px;
    background: var(--bg);
    border-radius: 4px;
    border-left: 3px solid var(--c-pink);
    font-size: 15px;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}
.task-detail.empty .task-detail-body { display: none; }
.task-detail-header {
    display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
    margin-bottom: 2px;
}
.task-detail-header strong { color: var(--text); font-weight: 500; font-size: 18px; }
.task-detail-actions-btns {
    margin-left: auto; display: inline-flex; gap: 6px;
}
.task-action-btn {
    background: var(--panel-2); color: var(--text);
    border: 1px solid var(--border); border-radius: 3px;
    padding: 2px 7px; font-size: 11px; cursor: pointer;
    min-height: 24px;
}
.task-action-btn:hover:not(:disabled) { background: var(--c-pink); color: #fff; border-color: var(--c-pink); }
.task-action-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.task-action-btn.starred { color: var(--c-cyan, #4dd0e1); border-color: var(--c-cyan, #4dd0e1); }
.task-action-btn.skip { color: var(--warn, #ffb74d); }
.task-action-btn.skip:hover:not(:disabled) { background: var(--warn, #ffb74d); color: #000; border-color: var(--warn, #ffb74d); }
.task-detail-badges { display: inline-flex; gap: 4px; }
.task-detail-desc {
    font-size: 14px; color: var(--text-dim); font-style: italic;
    margin-bottom: 6px; line-height: 1.35;
}
.task-detail-body {
    display: grid;
    grid-template-columns: 70px 1fr;
    gap: 4px 10px;
    align-items: start;
    margin-top: 4px;
}
.task-detail-row { display: contents; }
.task-detail-label {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-dim); padding-top: 4px;
}
.task-detail-val { font-size: 14px; color: var(--text); }
.task-detail-val.dim { color: var(--text-dim); font-style: italic; }
.task-detail-steps {
    margin: 0; padding-left: 20px; font-size: 14px; color: var(--text);
    list-style: decimal;
}
.task-detail-steps li { margin: 4px 0; line-height: 1.45; }
.task-detail-actions { display: flex; flex-wrap: wrap; gap: 4px; }
.task-detail-actions .chip-action {
    display: inline-block; padding: 3px 10px; font-size: 12px;
    background: var(--panel-2); color: var(--text);
    border: 1px solid var(--border); border-radius: 12px;
    font-family: 'JetBrains Mono', monospace;
}

/* Horizontal controls strip — sits between the task list and the cameras.
   Operator dropdown + 2 toggles + compact ep-info status, all in one row. */
.teleop-controls-strip {
    flex-direction: row;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
}
.teleop-controls-strip .teleop-operator {
    flex: 1 1 auto; min-width: 200px; height: 44px; padding: 4px 10px;
}
.teleop-controls-strip .big { min-height: 44px; padding: 8px 18px; flex: 0 0 auto; }
.teleop-controls-strip .toggle-teleop,
.teleop-controls-strip .toggle-rec { font-size: 16px; }
.ep-info-strip {
    flex: 1; min-width: 0; padding-left: 12px;
    font-family: 'JetBrains Mono', monospace; font-size: 13px;
    color: var(--text-dim);
    border-left: 1px solid var(--border);
    margin-left: 4px;
    display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
.ep-info-strip .k { color: var(--text); }

/* ---------- camera column (teleop) ----------
   D405 native: 1280×720 (16:9). Containers lock to 16:9 so placeholders
   match the real frame shape — no jumpy resize when capture goes live. */
.cam-stack { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.cam-row   { display: flex; gap: 6px; width: 100%; }
/* cam-col: fill all available vertical space (legacy 3-col layout). */
.cam-col   { display: flex; flex-direction: column; gap: 6px; flex: 1; min-height: 0; }
.cam-col .cam { flex: 1; min-height: 0; width: auto; max-width: 100%; align-self: center; }

/* cam-banner: edge-to-edge 3-camera row at the bottom of the Teleop tab.
   Breaks out of the .content padding via negative margins so it spans the
   full 1280px screen width. No panel border, no gaps between cameras —
   one continuous strip of live video. */
.cam-banner {
    display: flex;
    gap: 0;
    height: 100%;
    width: 1280px;
    margin: 0 -12px;         /* cancel the .content padding (12px) */
    padding: 0;
    background: #000;
    border: none;
    border-radius: 0;
}
.cam-banner .cam {
    flex: 1 1 0;             /* equal thirds */
    height: 100%;
    border-radius: 0;
    border: none;
    overflow: hidden;
}
/* legacy 3-camera-with-aspect layout (kept for reference) */
.cam-row-full { display: flex; gap: 8px; height: 100%; justify-content: center; }
.cam-row-full .cam {
    height: 100%;
    aspect-ratio: 16 / 9;
    width: auto;
    flex: 0 0 auto;
}
.cam {
    background: #000;
    border-radius: 3px;
    overflow: hidden;
    position: relative;
    aspect-ratio: 16 / 9;     /* D405 / Arducam capture aspect */
    width: 100%;
    max-height: 100%;
}
.cam-row .cam { width: calc(50% - 3px); }   /* two wrist cams side-by-side */
/* Layer img + placeholder on top of each other inside the .cam (which is
   position: relative). Without absolute positioning they stacked vertically
   and the placeholder got clipped by overflow: hidden — placeholder visible
   only when img is hidden via onerror handler. */
.cam img, .cam .placeholder {
    position: absolute; top: 0; left: 0; right: 0; bottom: 0;
    width: 100%; height: 100%;
    object-fit: cover; display: block;
}
.cam .placeholder {
    display: flex; align-items: center; justify-content: center;
    background:
      linear-gradient(45deg, #111 25%, transparent 25%) 0 0 / 12px 12px,
      linear-gradient(-45deg, #111 25%, transparent 25%) 0 0 / 12px 12px,
      #050505;
    color: #444; font-family: 'JetBrains Mono', monospace; font-size: 11px;
}
.cam-label {
    position: absolute; top: 5px; left: 8px;
    padding: 3px 8px;
    background: rgba(0,0,0,0.7);
    color: #fff; font-size: 11px;
    border-radius: 2px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-family: 'JetBrains Mono', monospace;
}
.cam-res {
    position: absolute; top: 4px; right: 6px;
    padding: 2px 6px;
    background: rgba(0,0,0,0.7);
    color: var(--c-silver); font-size: 9px;
    border-radius: 2px;
    font-family: 'JetBrains Mono', monospace;
}

/* ---------- episode info row ---------- */
.ep-info {
    margin-top: 6px;
    padding: 8px 10px;
    background: var(--bg);
    border-radius: 3px;
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
    font-size: 11px;
    color: var(--text-dim);
    display: flex; flex-direction: column; gap: 3px;
    border-left: 2px solid var(--c-pink);
}
.ep-info .k { color: var(--text); }

/* ---------- recordings tab ---------- */
.queue-row {
    display: flex; align-items: stretch; gap: 12px;
}
.queue-stats {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 6px;
    flex: 1;
}
.queue-filters {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding-left: 12px;
    border-left: 1px solid var(--border);
    flex-shrink: 0;
}
.queue-task-select {
    height: 38px; min-width: 200px; padding: 2px 8px; font-size: 14px;
}
.queue-filters .checkbox { font-size: 14px; white-space: nowrap; }
.qstat {
    padding: 3px 6px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 3px;
    text-align: center;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    min-height: 48px;
}
.qstat .qlabel { display:block; font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; line-height: 1.1; }
.qstat .qval { display:block; font-size: 24px; font-weight: 400; color: var(--stat); margin-top: 2px; line-height: 1; }
.qstat.ok  { border-color: var(--ok); }
.qstat.bad { border-color: var(--bad); }
.qstat.clickable { cursor: pointer; transition: background 0.15s, border-color 0.15s, transform 0.05s; }
.qstat.clickable:hover { background: var(--panel-2); border-color: var(--c-cyan, #4dd0e1); }
.qstat.clickable:active { transform: scale(0.98); }
.qstat.clickable.active {
    background: rgba(77, 208, 225, 0.12);
    border-color: var(--c-cyan, #4dd0e1);
    box-shadow: 0 0 0 2px rgba(77, 208, 225, 0.3) inset;
}
.qstat.clickable.active .qval { color: var(--c-cyan, #4dd0e1); }

/* Playback top row — each camera AND the joint chart is one equal-width
   column locked to 16:9 aspect. With 3 cameras + chart that's 4 tiles
   per row, all identical W×H. */
.play-top { display: flex; gap: 10px; align-items: stretch; }
.play-top > .play-cam,
.play-top > .joint-chart-wrap { flex: 1 1 0; min-width: 0; aspect-ratio: 16 / 9; margin-top: 0; }

/* Joint-state mini-chart (inside .rec-playback). Sized 16:9 by the
   .play-top > .joint-chart-wrap rule above so it matches the cameras
   tile-for-tile. SVG inside fills 100% × 100%. */
.joint-chart-wrap { background: var(--panel-1); border-radius: 4px; padding: 4px 4px 4px 4px; min-width: 0; display: flex; flex-direction: column; }
.joint-chart { width: 100%; height: 100%; display: block; flex: 1; min-height: 0; }
.joint-strip-bg { fill: rgba(255,255,255,0.04); }
.joint-trace-qpos { fill: none; stroke-width: 1.5; }
.joint-trace-action { fill: none; stroke-width: 1.2; stroke-dasharray: 3 2; opacity: 0.85; }
.joint-label { fill: var(--text); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
.joint-range { fill: var(--text-dim); font-family: 'JetBrains Mono', monospace; font-size: 10px; opacity: 0.7; }
.joint-cursor { stroke: #fff; stroke-width: 1; opacity: 0.55; pointer-events: none; }
.joint-legend {
    display: flex; gap: 16px; align-items: center; padding: 4px 8px;
    font-size: 12px; color: var(--text-dim); flex-wrap: wrap;
}
.joint-legend .swatch {
    display: inline-block; width: 14px; height: 3px; margin-right: 6px;
    vertical-align: middle; border-radius: 2px;
}
.play-no-cams { padding: 6px 8px; font-size: 13px; }

.rec-list { display: flex; flex-direction: column; gap: 6px; }
.rec-row {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 4px;
}
.rec-row .meta { flex: 1; min-width: 0; }
.rec-thumb {
    flex: 0 0 auto;
    width: 96px; height: 54px;       /* 16:9 */
    object-fit: cover;
    background: #000;
    border-radius: 3px;
    border: 1px solid #222;
}
.rec-thumb-empty {
    background:
      linear-gradient(45deg, #111 25%, transparent 25%) 0 0 / 8px 8px,
      linear-gradient(-45deg, #111 25%, transparent 25%) 0 0 / 8px 8px,
      #050505;
}
.rec-row .name { font-size: 18px; font-weight: 500; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rec-row .sub  { font-size: 14px; color: var(--text-dim); font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace; margin-top: 3px; }
.rec-row .sub.bad { color: #ff6b75; }
.rec-row .sub a { color: var(--c-cyan, #4dd0e1); text-decoration: none; }
.rec-row .sub a:hover { text-decoration: underline; }
.rec-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.rec-actions .small {
    min-height: 36px; padding: 6px 12px; font-size: 14px;
    background: var(--panel-1); color: var(--text); border: 1px solid var(--border);
    border-radius: 4px; cursor: pointer;
}
.rec-actions .small.go     { background: var(--ok); color: #000; border-color: var(--ok); }
.rec-actions .small.reject { background: var(--warn); color: #000; border-color: var(--warn); }
.rec-actions .small.danger { background: var(--bad); color: #fff; border-color: var(--bad); }

/* Upload progress bar */
.upload-bar { height: 5px; background: var(--panel-1); border-radius: 3px; margin-top: 4px; overflow: hidden; max-width: 420px; }
.upload-bar-fill { height: 100%; background: var(--c-cyan, #4dd0e1); transition: width 0.3s ease; }

/* Inline playback panel — sits between the episode row and the next row */
.rec-playback {
    background: var(--panel-1);
    border: 1px solid var(--border);
    border-radius: 4px;
    margin-top: -2px;
    padding: 12px;
}
/* .play-cams is no longer used (cameras live directly in .play-top now)
   but keep the rule for any stale templates / cached views. */
.play-cams { display: flex; gap: 8px; }
/* Each camera tile is 16:9 (overridden inside .play-top via the
   .play-top > .play-cam rule above). 4:3 camera frames are letterboxed
   inside via object-fit: contain on the inner <img>. */
.play-cam { position: relative; background: #000; border-radius: 4px; overflow: hidden; }
.play-cam-label {
    position: absolute; top: 6px; left: 8px;
    font-size: 12px; padding: 2px 6px;
    background: rgba(0,0,0,0.55); color: #fff; border-radius: 3px;
    font-family: 'JetBrains Mono', monospace; z-index: 1;
}
/* Letterbox 4:3 frames inside the 16:9 tile so the operator sees the
   full frame without cropping. Black bars top/bottom are intentional. */
.play-img {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100%; object-fit: contain; background: #000;
}
/* Two-row layout: RGB row on top, depth row below. Each row is a flex
   of cam tiles that grow equally; the joint chart sits below both rows
   (still inside .play-top which stacks them vertically). */
.play-top { flex-direction: column; }
.play-row { display: flex; gap: 10px; align-items: stretch; }
.play-row > .play-cam { flex: 1 1 0; min-width: 0; aspect-ratio: 16 / 9; }
.play-row-depth .play-cam-label { background: rgba(40,10,50,0.65); }

.play-controls {
    display: flex; align-items: center; gap: 10px;
    margin: 4px 0 10px;            /* tight to row above, breathing room above the camera grid */
    flex-wrap: wrap;
}
.play-controls .play-slider { flex: 1; height: 28px; cursor: pointer; min-width: 200px; }
.play-counter { font-family: 'JetBrains Mono', monospace; font-size: 14px; white-space: nowrap; }
.play-btn { min-width: 44px; min-height: 36px; font-size: 16px; }

/* RGB / Depth / Both segmented toggle in the playback controls bar.
   Hidden entirely when the recording has no depth channel. */
.play-view-toggle {
    display: inline-flex; gap: 0;
    border: 1px solid var(--border); border-radius: 4px; overflow: hidden;
}
.play-view-btn {
    min-width: 56px; min-height: 30px; font-size: 12px;
    border: none; border-radius: 0;
    background: var(--panel-2); color: var(--text-dim);
    border-right: 1px solid var(--border);
    font-family: 'JetBrains Mono', monospace;
}
.play-view-btn:last-child { border-right: none; }
.play-view-btn.active {
    background: var(--c-pink); color: #fff; font-weight: 500;
}
.play-view-btn:hover { color: #fff; }

/* Episode notes — free-text QA hint persisted to meta.yaml and surfaced
   in DataCanvas metadata on upload. Save fires on blur OR Cmd/Ctrl+Enter. */
.play-notes-row {
    display: flex; align-items: flex-start; gap: 10px; margin-top: 8px;
}
.play-notes-row label { margin-top: 4px; font-size: 12px; min-width: 40px; }
.play-notes {
    flex: 1; min-height: 36px; padding: 6px 8px;
    background: #0a0a0a; color: #ddd; border: 1px solid #333;
    border-radius: 3px; font-family: 'DM Sans', sans-serif; font-size: 13px;
    resize: vertical;
}
.play-notes:focus { border-color: var(--c-pink); outline: none; }
.play-notes-status { min-width: 70px; font-size: 11px; align-self: center; }

.badge.state-recording { background: var(--c-magenta); }
.badge.state-done      { background: var(--warn); color: #000; }    /* waiting for QA */
.badge.state-approved  { background: var(--c-violet); }
.badge.state-rejected  { background: #555; }
.badge.state-queued    { background: #2196f3; }
.badge.state-uploading { background: var(--c-pink); }
.badge.state-uploaded  { background: var(--ok); }
.badge.state-partial   { background: #b85; color: #000; }   /* writer crashed mid-record */
.badge.state-failed    { background: var(--bad); }
.badge.state-pinned    { background: #6a5acd; }

/* Episode provenance badges — distinguish teleop demonstrations from
   autonomous policy evaluation runs. */
.badge.type-teleop     { background: var(--c-pink); color: #fff; }
.badge.type-evaluation { background: var(--c-violet); color: #fff; }

/* ---------- settings ---------- */
.srv-form { display: flex; flex-direction: column; gap: 4px; }
.srv-row { display: grid; grid-template-columns: 110px 1fr; gap: 8px; align-items: center; }
/* Variant for "label + input + save button" packed on one line (used by
   the Hostname rename row). Uses flex so the trailing button hugs the input
   instead of wrapping to a third grid row. */
.srv-row-inline { display: flex; gap: 8px; align-items: center; }
.srv-row-inline > label { flex: 0 0 110px; }
.srv-row-inline > .srv-input { flex: 1; min-width: 0; }

/* Calibration buttons — single row of 5. Compact font + nowrap so the
   longest label ("Regen YAM Cfg") stays on one line in column 2's width. */
.calib-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.calib-grid .big { font-size: 13px; padding: 4px 6px; white-space: nowrap; }

/* ---- modal dialog -------------------------------------------------------
   Full-screen overlay + centered card. Used by calibration buttons (pose
   instructions w/ SVG diagrams) and any other action that benefits from a
   bigger dialog than browser confirm() can offer. */
.modal-overlay {
    position: fixed; inset: 0; z-index: 1000;
    background: rgba(0,0,0,0.75);
    display: flex; align-items: center; justify-content: center;
}
.modal-card {
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 12px 48px rgba(0,0,0,0.6);
    padding: 22px 26px;
    width: min(880px, 92vw);
    max-height: 92vh;
    overflow-y: auto;
    display: flex; flex-direction: column; gap: 14px;
}
.modal-title { font-size: 22px; font-weight: 700; color: var(--text); }
.modal-body  { font-size: 16px; line-height: 1.5; }
.modal-body svg { display: block; margin: 8px auto; max-width: 100%; height: auto; }
.modal-body ul, .modal-body ol { margin: 6px 0; padding-left: 22px; }
.modal-body li { margin: 4px 0; }
.modal-body .warn { color: var(--warn, #ffb74d); font-weight: 600; }
.modal-body .bad  { color: var(--bad, #ff6b75); font-weight: 600; }
.modal-body code  { background: var(--panel-2); padding: 1px 6px; border-radius: 3px;
                    font-family: 'JetBrains Mono', monospace; font-size: 13px; }
.modal-actions    { display: flex; justify-content: flex-end; gap: 12px; margin-top: 4px; }
.calib-extra-row {
    display: flex; align-items: center; gap: 12px; margin-top: 4px; flex-wrap: wrap;
}
.calib-extra-row .big { font-size: 13px; padding: 4px 10px; }

/* Arm-scope tri-state — three buttons styled as a single segmented control.
   Click cycles through Left / Right / Both; the active one is highlighted
   in accent colour. */
.arm-mode-row { display: inline-flex; gap: 0; border: 1px solid var(--border);
    border-radius: 5px; overflow: hidden; }
.arm-mode-btn {
    appearance: none; background: var(--panel-2); color: var(--text-dim);
    border: none; border-right: 1px solid var(--border);
    padding: 5px 14px; font-size: 13px; cursor: pointer; min-width: 56px;
    font-family: inherit;
}
.arm-mode-btn:last-child { border-right: none; }
.arm-mode-btn:hover { background: var(--panel); color: var(--text); }
.arm-mode-btn.active {
    background: var(--accent); color: #fff; font-weight: 600;
}

/* Power / network action rows — fixed grid (vs flex-wrap) so button widths
   stay predictable and labels never wrap to a second line. Compact font for
   the same reason. */
.power-grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 6px; }
.power-grid .big { font-size: 13px; padding: 4px 8px; white-space: nowrap; }

/* GPU profile pubkey panel — shown after Generate Key. Wraps long ed25519
   strings (typically one line ≈ 100 chars) into a readable block. */
.gpu-pubkey {
    margin-top: 6px;
    padding: 6px 8px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 3px;
}
.gpu-pubkey textarea {
    width: 100%;
    min-height: 60px;
    padding: 6px 8px;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 3px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    resize: vertical;
    word-break: break-all;
}
.gpu-pubkey code { background: rgba(255,255,255,0.06); padding: 1px 4px; border-radius: 2px; }
/* Stacked label-above-input (label takes its own row, input goes full width). */
.srv-stack { display: flex; flex-direction: column; gap: 4px; }
.srv-label-stack {
    font-size: 13px;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.srv-row label { font-size: 13px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; }
.srv-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
.srv-row-3 label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; display: block; margin-bottom: 2px; }
.srv-input {
    padding: 8px 12px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 3px;
    color: var(--text);
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
    font-size: 16px;
    min-height: 38px;
    width: 100%;
    box-sizing: border-box;
}
.srv-input:focus { outline: 1px solid var(--c-pink); }
.srv-actions { display: flex; gap: 6px; margin-top: 2px; }
.srv-msg { font-size: 13px; min-height: 18px; padding-left: 2px; }
.srv-msg.ok  { color: #7fda84; }
.srv-msg.bad { color: #ff6b75; }

.net-current { font-size: 14px; font-family: 'JetBrains Mono', monospace; display: flex; flex-direction: column; gap: 4px; }
.net-current .k { color: var(--text); }
.wifi-list {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: 4px;
    overflow-y: auto;
    flex: 1;
    min-height: 0;
}
.wifi-row {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 10px;
    align-items: center;
    width: 100%;
    padding: 8px 10px;
    margin: 2px 0;
    background: var(--panel-2);
    border: 1px solid transparent;
    border-radius: 2px;
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}
.wifi-row:active { background: var(--c-pink); }
.wifi-ssid { font-weight: 500; overflow: hidden; text-overflow: ellipsis; }
.wifi-sec, .wifi-sig { font-size: 12px; font-family: 'JetBrains Mono', monospace; color: var(--text-dim); }

.sys-info {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 4px 8px;
    font-size: 13px;
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
}
/* Single row of Pi telemetry chips. Flex-wraps to two rows on the
   720-px portrait kiosk, stays one row on a wider desktop browser.
   Each chip is a small box: dim label, bold value, dim unit. */
.sys-metrics-row {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 6px;
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
    font-size: 12px;
}
.sys-metrics-row > div {
    flex: 1 1 0;
    min-width: 70px;
    padding: 5px 7px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 3px;
    display: flex; align-items: baseline; gap: 4px;
    white-space: nowrap;
}
.sys-metrics-row .lbl { color: var(--text-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; }
.sys-metrics-row .k   { color: #fff; font-weight: 600; font-size: 13px; }
.sys-metrics-row .u   { color: var(--text-dim); font-size: 10px; }
.log-pane {
    flex: 1; min-height: 0;
    overflow-y: auto;
    background: #0a0c10;
    padding: 10px;
    border-radius: 3px;
    font-size: 12px;
    font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
    color: var(--text-dim);
    white-space: pre-wrap;
    margin: 0;
}

/* ---------- policy ---------- */
.policy-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 6px;
    flex: 1; min-height: 0;
    overflow-y: auto;
}
.policy-card {
    padding: 10px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 3px;
    cursor: pointer;
    font-size: 14px;
}
.policy-card.selected { background: var(--c-magenta); border-color: var(--c-pink); color: #fff; }
.policy-card .name { font-weight: 500; font-size: 16px; }
.policy-card .meta { font-size: 12px; color: var(--text-dim); margin-top: 4px; font-family: 'JetBrains Mono', monospace; }
