/* ============================================================
   JUPITER · Home Command Console
   Hand written stylesheet. No framework, no build step.
   Theme system: html[data-theme="..."] swaps the token block.
   Effects:      html[data-fx~="stars|grid|scan"] gates layers.
   ============================================================ */

/* ---------- design tokens (default: Jovian Storm) ---------- */
:root,
html[data-theme="jovian"] {
  --bg0: #0a0714;
  --bg1: #120c22;
  --surface: rgba(22, 16, 40, 0.66);
  --surface2: rgba(34, 25, 60, 0.55);
  --line: rgba(255, 179, 71, 0.14);
  --line-strong: rgba(255, 179, 71, 0.34);
  --text: #ede7f8;
  --text-dim: #9b92b8;
  --accent: #ffb347;
  --accent2: #67e8f9;
  --good: #4ade80;
  --warn: #facc15;
  --bad: #f87171;
  --orb-base: #2a1f3f;
  --grid-color: rgba(255, 179, 71, 0.05);
  --star-color: 255, 220, 180;
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
}

html[data-theme="nebula"] {
  --bg0: #0b0716;
  --bg1: #160e2b;
  --surface: rgba(28, 18, 52, 0.66);
  --surface2: rgba(42, 28, 76, 0.55);
  --line: rgba(167, 139, 250, 0.16);
  --line-strong: rgba(167, 139, 250, 0.38);
  --text: #efeafd;
  --text-dim: #a39bc4;
  --accent: #a78bfa;
  --accent2: #f472b6;
  --good: #34d399;
  --warn: #fbbf24;
  --bad: #fb7185;
  --orb-base: #241742;
  --grid-color: rgba(167, 139, 250, 0.05);
  --star-color: 220, 200, 255;
}

html[data-theme="cyber"] {
  --bg0: #04070d;
  --bg1: #081120;
  --surface: rgba(10, 22, 40, 0.68);
  --surface2: rgba(14, 32, 56, 0.58);
  --line: rgba(34, 211, 238, 0.16);
  --line-strong: rgba(34, 211, 238, 0.4);
  --text: #e6f6ff;
  --text-dim: #84a3bd;
  --accent: #22d3ee;
  --accent2: #f0abfc;
  --good: #4ade80;
  --warn: #fde047;
  --bad: #fb7185;
  --orb-base: #0c2236;
  --grid-color: rgba(34, 211, 238, 0.06);
  --star-color: 170, 230, 255;
}

html[data-theme="matrix"] {
  --bg0: #020806;
  --bg1: #06140e;
  --surface: rgba(8, 26, 18, 0.7);
  --surface2: rgba(12, 38, 26, 0.6);
  --line: rgba(52, 211, 153, 0.16);
  --line-strong: rgba(52, 211, 153, 0.4);
  --text: #e4fff4;
  --text-dim: #7faa94;
  --accent: #34d399;
  --accent2: #a3e635;
  --good: #34d399;
  --warn: #fbbf24;
  --bad: #f87171;
  --orb-base: #0a241a;
  --grid-color: rgba(52, 211, 153, 0.06);
  --star-color: 170, 255, 210;
}

html[data-theme="ice"] {
  --bg0: #eef4fb;
  --bg1: #ffffff;
  --surface: rgba(255, 255, 255, 0.78);
  --surface2: rgba(240, 246, 252, 0.85);
  --line: rgba(2, 132, 199, 0.18);
  --line-strong: rgba(2, 132, 199, 0.4);
  --text: #14253b;
  --text-dim: #5a708c;
  --accent: #0284c7;
  --accent2: #7c3aed;
  --good: #059669;
  --warn: #d97706;
  --bad: #dc2626;
  --orb-base: #cfe4f5;
  --grid-color: rgba(2, 132, 199, 0.06);
  --star-color: 2, 132, 199;
  --shadow: 0 14px 40px rgba(20, 50, 90, 0.12);
}

html[data-theme="ember"] {
  --bg0: #140607;
  --bg1: #220b0c;
  --surface: rgba(44, 16, 16, 0.62);
  --surface2: rgba(62, 24, 22, 0.52);
  --line: rgba(248, 113, 113, 0.16);
  --line-strong: rgba(248, 113, 113, 0.4);
  --text: #fdeee8;
  --text-dim: #c09a8e;
  --accent: #f87171;
  --accent2: #fbbf24;
  --good: #4ade80;
  --warn: #fbbf24;
  --bad: #f87171;
  --orb-base: #3a1413;
  --grid-color: rgba(248, 113, 113, 0.05);
  --star-color: 255, 190, 160;
}

/* 1982: black arena, light-cycle cyan, MCP orange */
html[data-theme="tron"] {
  --bg0: #010409;
  --bg1: #041018;
  --surface: rgba(5, 22, 36, 0.7);
  --surface2: rgba(8, 32, 50, 0.6);
  --line: rgba(111, 195, 223, 0.24);
  --line-strong: rgba(111, 195, 223, 0.55);
  --text: #d8f6ff;
  --text-dim: #7da2b8;
  --accent: #6fc3df;
  --accent2: #ff8d3a;
  --good: #5eead4;
  --warn: #ffb454;
  --bad: #ff5c5c;
  --orb-base: #06202e;
  --grid-color: rgba(111, 195, 223, 0.12);
  --star-color: 160, 225, 255;
  /* every panel edged in light: the ring+glow live in the token so all
     var(--shadow) consumers (cards, modals, popovers, lib-case) compose it */
  --shadow: 0 0 0 1px rgba(111, 195, 223, 0.1), 0 0 30px rgba(111, 195, 223, 0.12), 0 18px 50px rgba(0, 0, 0, 0.6);
}

/* RobCo Industries phosphor CRT */
html[data-theme="pipboy"] {
  --bg0: #051105;
  --bg1: #0a1c0c;
  --surface: rgba(10, 30, 13, 0.72);
  --surface2: rgba(14, 42, 18, 0.62);
  --line: rgba(65, 255, 115, 0.2);
  --line-strong: rgba(65, 255, 115, 0.45);
  --text: #b5ffb2;
  --text-dim: #5fae66;
  --accent: #41ff70;
  --accent2: #ffd24a;
  --good: #41ff70;
  --warn: #ffd24a;
  --bad: #ff6b5e;
  --orb-base: #0c2410;
  --grid-color: rgba(65, 255, 115, 0.08);
  --star-color: 150, 255, 160;
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}

:root {
  --font-display: 'Orbitron', 'Segoe UI', sans-serif;
  --font-body: 'Rajdhani', 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', Consolas, monospace;
  --radius: 14px;
  --radius-sm: 9px;
  --dock-w: 240px;
  --dock-w-min: 76px;
  --glow: color-mix(in srgb, var(--accent) 32%, transparent);
  /* flight-deck instrument tokens: hairlines, chamfer cut, shared motion curve */
  --hud: color-mix(in srgb, var(--accent) 30%, transparent);
  --cut: 7px;
  --ease: cubic-bezier(0.22, 0.9, 0.3, 1);
}

/* ---------- theme character (tron / pipboy go beyond token swaps) ---------- */
/* Tron: hard angles, a floor grid rising to the horizon, panel edges as light lines */
html[data-theme="tron"] {
  --radius: 3px;
  --radius-sm: 2px;
}
html[data-theme="tron"] body {
  background:
    radial-gradient(1200px 460px at 50% 118%, rgba(111, 195, 223, 0.14), transparent 62%),
    radial-gradient(900px 500px at 85% -12%, rgba(255, 141, 58, 0.06), transparent 55%),
    linear-gradient(180deg, var(--bg1), var(--bg0) 55%);
  background-attachment: fixed;
}
/* the game grid is a floor: fade it up from the bottom, not down from the top */
html[data-theme="tron"] .fx-grid {
  mask-image: radial-gradient(ellipse 100% 90% at 50% 100%, black 25%, transparent 85%);
  -webkit-mask-image: radial-gradient(ellipse 100% 90% at 50% 100%, black 25%, transparent 85%);
}

/* Pip-Boy: monochrome phosphor terminal — mono type, boxy bezels, CRT furniture */
html[data-theme="pipboy"] {
  --font-display: 'VT323', 'JetBrains Mono', Consolas, monospace;
  --font-body: 'Share Tech Mono', 'JetBrains Mono', Consolas, monospace;
  --font-mono: 'Share Tech Mono', Consolas, monospace;
  --radius: 4px;
  --radius-sm: 3px;
}
html[data-theme="pipboy"] body { text-shadow: 0 0 8px rgba(65, 255, 115, 0.22); }
html[data-theme="pipboy"] h1,
html[data-theme="pipboy"] h2,
html[data-theme="pipboy"] h3 {
  letter-spacing: 0.02em;
  text-transform: uppercase;
  font-weight: 400; /* VT323 ships 400 only; synthetic bold smears the pixels */
}
/* tube glass: scanlines + vignette, always on for this theme (independent of the fx-scan
   toggle so the effects checkboxes keep meaning what they say). Same z stratum as .fx-scan
   so modals (z 80+) stay above it. */
html[data-theme="pipboy"] body::after {
  content: "";
  position: fixed; inset: 0;
  z-index: 60; pointer-events: none;
  background:
    repeating-linear-gradient(to bottom,
      rgba(0, 0, 0, 0) 0px, rgba(0, 0, 0, 0) 3px, rgba(1, 8, 2, 0.16) 4px),
    radial-gradient(ellipse 130% 110% at 50% 40%, transparent 60%, rgba(1, 10, 3, 0.55) 100%);
}

/* ---------- base ---------- */
* { box-sizing: border-box; }

/* The hidden attribute must always win: author display rules (flex/grid on a class)
   otherwise override the UA's [hidden]{display:none} and "hidden" elements stay on
   screen (this bit the palette, dropzone, upload bar, notes editor and plex panes). */
[hidden] { display: none !important; }

html { font-size: 16px; }

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-body);
  font-size: 1.02rem;
  line-height: 1.45;
  color: var(--text);
  background:
    radial-gradient(1100px 600px at 85% -10%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%),
    radial-gradient(900px 700px at -10% 110%, color-mix(in srgb, var(--accent2) 8%, transparent), transparent 55%),
    linear-gradient(180deg, var(--bg1), var(--bg0) 55%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { font-family: var(--font-display); letter-spacing: 0.04em; margin: 0; }

.mono { font-family: var(--font-mono); font-size: 0.92em; }
.dim { color: var(--text-dim); }
.small { font-size: 0.86rem; }
.good { color: var(--good); }
.warn { color: var(--warn); }
.bad { color: var(--bad); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
/* chamfered (clip-path) controls clip an outset outline away — give them an inset ring */
.chip:focus-visible, .tab:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent);
}

::selection { background: color-mix(in srgb, var(--accent) 35%, transparent); }

/* scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 9px; }
*::-webkit-scrollbar-track { background: transparent; }

/* ---------- background effect layers ---------- */
#starfield {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
  display: none;
}
html[data-fx~="stars"] #starfield { display: block; }

.fx-grid {
  position: fixed; inset: 0; z-index: 0; pointer-events: none; display: none;
  background-image:
    linear-gradient(var(--grid-color) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(ellipse 90% 80% at 50% 0%, black 30%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 0%, black 30%, transparent 80%);
}
html[data-fx~="grid"] .fx-grid { display: block; }

.fx-scan {
  position: fixed; inset: 0; z-index: 60; pointer-events: none; display: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0px,
    rgba(0, 0, 0, 0) 3px,
    rgba(0, 0, 0, 0.08) 4px
  );
  mix-blend-mode: multiply;
  opacity: 0.5;
}
html[data-fx~="scan"] .fx-scan { display: block; }
html[data-theme="ice"] .fx-scan { opacity: 0.12; }

/* ---------- shell layout ---------- */
.shell { position: relative; z-index: 1; }

.dock {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: var(--dock-w);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 12px;
  background: color-mix(in srgb, var(--bg0) 72%, transparent);
  border-right: 1px solid var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  z-index: 40;
  transition: width 0.25s ease;
  overflow: hidden;
}

html[data-dock="min"] .dock { width: var(--dock-w-min); }
html[data-dock="min"] .dock-label,
html[data-dock="min"] .dock-logo-text { display: none; }
/* scoped to the collapse button: Edit/Reset share .dock-collapse but their glyphs must not flip */
html[data-dock="min"] #dock-collapse .icon { transform: rotate(180deg); }

.dock-logo {
  display: flex; align-items: center; gap: 12px;
  padding: 4px 8px 14px;
  text-decoration: none !important;
  border-bottom: 1px solid var(--line);
  margin-bottom: 6px;
}
.dock-logo-text {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.15rem;
  letter-spacing: 0.18em;
  color: var(--text);
  text-shadow: 0 0 18px var(--glow);
  white-space: nowrap;
}

/* The nav scrolls within the dock so every deck stays reachable in short viewports. */
.dock-nav { display: flex; flex-direction: column; gap: 4px; flex: 1; min-height: 0; overflow-y: auto; }

.dock-link {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 12px;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-size: 0.86rem;
  border: 1px solid transparent;
  text-decoration: none !important;
  white-space: nowrap;
  transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
/* Section heading between dock links (e.g. "Management") — a button that folds its group.
   When the dock collapses to the icon rail the word can't fit, so it degrades to a thin
   divider line (and folding is a full-dock affordance only). */
.dock-group-title {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  margin: 12px 0 2px;
  padding: 2px 12px;
  background: none; border: none; cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-dim);
  opacity: 0.7;
  white-space: nowrap;
}
.dock-group-title:hover { color: var(--text); opacity: 1; }
.dock-group-title .chev {
  width: 12px; height: 12px;
  margin-left: auto;
  transform: rotate(-90deg);           /* pointing down = open */
  transition: transform 0.15s ease;
}
.dock-group-title.closed .chev { transform: rotate(180deg); }  /* pointing right = folded */
.dock-group-title.closed.has-active { color: var(--accent); opacity: 1; }

/* The wrapper is display:contents so the links stay direct flex items of .dock-nav in
   every dock mode; folding just hides them. */
.dock-group { display: contents; }
.dock-group.closed .dock-link { display: none; }

html[data-dock="min"] .dock-group-title {
  height: 0;
  overflow: hidden;
  padding: 0;
  margin: 6px 10px;
  border-top: 1px solid var(--line);
}
/* Icon rail has no headings to click, so folded groups stay visible there. */
html[data-dock="min"] .dock-group.closed .dock-link { display: flex; }

.dock-link:hover { color: var(--text); background: var(--surface2); }
.dock-link.active {
  position: relative;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
/* live edge: the active deck's marker carries a slow energy current */
.dock-link.active::before {
  content: "";
  position: absolute; left: 0; top: 18%; bottom: 18%; width: 3px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--accent2), var(--accent) 45%, var(--accent2));
  background-size: 100% 260%;
  box-shadow: 0 0 9px var(--glow);
  animation: dock-live 3s linear infinite;
}
/* the current only flows while the dock is under the pointer — no idle repaint loop */
.dock:not(:hover) .dock-link.active::before { animation-play-state: paused; }
@keyframes dock-live { to { background-position: 0 -260%; } }

.dock-foot { border-top: 1px solid var(--line); padding-top: 8px; }
.dock-collapse {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 10px 12px;
  background: none; border: none;
  color: var(--text-dim);
  font-family: var(--font-body); font-weight: 600; font-size: 0.84rem;
  letter-spacing: 0.05em; text-transform: uppercase;
  border-radius: var(--radius-sm);
  cursor: pointer; white-space: nowrap;
}
.dock-collapse:hover { color: var(--text); background: var(--surface2); }
.dock-collapse .icon { transition: transform 0.25s ease; }

/* ---------- dock nav editing (drag to arrange, eye to hide) ---------- */
/* hidden decks use a class, not [hidden]: .dock-link's display:flex would win over the attribute.
   !important: the folded-group visibility overrides (icon rail (0,4,1), phone bar (0,3,0)) must
   never resurrect a deck the user hid. */
.dock-link.nav-hidden { display: none !important; }
html[data-navedit] .dock-link.nav-hidden { display: flex !important; opacity: 0.4; }

#dock-reset, #dock-newfolder { display: none; }   /* both only offer themselves while editing */
html[data-navedit] #dock-reset,
html[data-navedit] #dock-newfolder { display: flex; }
html[data-navedit] #dock-edit {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

html[data-navedit] .dock-link {
  cursor: grab;
  border-style: dashed;
  border-color: color-mix(in srgb, var(--text-dim) 35%, transparent);
}
html[data-navedit] .dock-link.dragging { opacity: 0.35; }
/* insertion indicator: the same inset-edge idiom as the investigations table rows */
html[data-navedit] .dock-link.drag-over { box-shadow: inset 0 2px 0 var(--accent); }
html[data-navedit] .dock-group-title.drag-over {
  color: var(--accent);
  opacity: 1;
  box-shadow: inset 0 -2px 0 var(--accent);    /* bottom edge: the drop appends to this group */
}

/* Row controls — move up/down then hide — ride at the tail of the link while editing. */
.dock-ctl { margin-left: auto; flex: none; display: flex; align-items: center; gap: 2px; }

.dock-eye, .dock-move, .dock-tool {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0;
  background: none; border: none;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  cursor: pointer;
}
.dock-eye:hover, .dock-move:hover, .dock-tool:hover { color: var(--text); background: var(--surface2); }
.dock-eye .icon, .dock-tool .icon { width: 15px; height: 15px; }
.dock-move .icon { width: 13px; height: 13px; }
.dock-tool[data-act="delete"]:hover { color: var(--bad); }
html[data-dock="min"] .dock-ctl,
html[data-dock="min"] .dock-tools { display: none; }  /* no room on the icon rail */

/* Section controls live at the head of the group body; .dock-group is display:contents, so
   the row is a flex child of the nav exactly like the links it sits above. */
.dock-tools { display: flex; align-items: center; gap: 2px; padding: 0 8px 2px; }

/* A folder the user made reads as theirs, not as shipped structure. */
.dock-group-title.dock-folder { color: var(--accent); opacity: 0.85; }
/* An empty folder is a heading pointing at nothing — show it only where it can be filled.
   Progressive: browsers without :has() simply keep the stray heading. */
.dock-group-title.dock-folder:has(+ .dock-group:empty) { display: none; }
html[data-navedit] .dock-group-title.dock-folder:has(+ .dock-group:empty) { display: flex; }

.main {
  margin-left: var(--dock-w);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: margin-left 0.25s ease;
}
html[data-dock="min"] .main { margin-left: var(--dock-w-min); }

/* ---------- topbar ---------- */
.topbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 22px;
  padding: 14px 28px;
  background: color-mix(in srgb, var(--bg0) 62%, transparent);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
/* power-on sweep: the topbar's lower rule draws itself once per page load */
.topbar::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent) 22%, var(--accent2) 50%, var(--accent) 78%, transparent);
  opacity: 0.5;
  transform-origin: left;
  animation: hud-poweron 1.1s var(--ease) 0.1s backwards;
  pointer-events: none;
}
@keyframes hud-poweron { from { transform: scaleX(0); opacity: 0; } }

.topbar-title { flex: 1; min-width: 0; }
.topbar-title h1 { font-size: 1.32rem; font-weight: 700; text-transform: uppercase; }

.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  opacity: 0.85;
}

.topbar-clock { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.15; }
.clock-time { font-size: 1.08rem; color: var(--accent2); text-shadow: 0 0 14px color-mix(in srgb, var(--accent2) 40%, transparent); }
.clock-date { font-size: 0.78rem; color: var(--text-dim); letter-spacing: 0.08em; text-transform: uppercase; }

.topbar-actions { display: flex; align-items: center; gap: 10px; }

.leds { display: flex; gap: 8px; padding: 0 4px; }
.led {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-dim); opacity: 0.5;
}
.led.ok { background: var(--good); opacity: 1; box-shadow: 0 0 10px var(--good); animation: pulse 2.4s ease-in-out infinite; }
.led.bad { background: var(--bad); opacity: 1; box-shadow: 0 0 10px var(--bad); }
@keyframes pulse { 50% { opacity: 0.55; } }

.user-menu { position: relative; }
.user-menu summary { list-style: none; cursor: pointer; }
.user-menu summary::-webkit-details-marker { display: none; }
.avatar {
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent2));
  color: var(--bg0);
  font-family: var(--font-display); font-weight: 700; font-size: 0.86rem;
}
.user-pop {
  position: absolute; right: 0; top: calc(100% + 10px);
  min-width: 220px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  padding: 8px;
  z-index: 50;
}
.user-name { padding: 8px 10px; font-weight: 700; border-bottom: 1px solid var(--line); margin-bottom: 6px; }
.user-pop a {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: 7px;
  color: var(--text); text-decoration: none !important;
}
.user-pop a:hover { background: var(--surface2); color: var(--accent); }

/* ---------- content ---------- */
.content {
  padding: 26px 28px 56px;
  max-width: 1500px;
  width: 100%;
  margin: 0 auto;
  display: flex; flex-direction: column; gap: 20px;
}
/* deck materialisation: sections rise in sequence on load (and once when a hidden
   section is first revealed). Reduced motion zeroes every duration globally.
   :where keeps specificity at the old (0,1,0) level; fixed overlays that are toggled
   open repeatedly (dossier, people links map) are excluded or every open would replay
   an invisible click-blocking hold. */
.content > :where(:not(.dossier-overlay):not(.people-map)) { animation: deck-in 0.5s var(--ease) backwards; }
.content > *:nth-child(1) { animation-delay: 0.03s; }
.content > *:nth-child(2) { animation-delay: 0.07s; }
.content > *:nth-child(3) { animation-delay: 0.11s; }
.content > *:nth-child(4) { animation-delay: 0.15s; }
.content > *:nth-child(5) { animation-delay: 0.19s; }
.content > *:nth-child(6) { animation-delay: 0.23s; }
.content > *:nth-child(7) { animation-delay: 0.27s; }
.content > *:nth-child(8) { animation-delay: 0.31s; }
@keyframes deck-in { from { opacity: 0; transform: translateY(14px); } }

/* ---------- cards ---------- */
.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: var(--shadow, 0 14px 40px rgba(0, 0, 0, 0.35));
  transition: border-color 0.22s ease, box-shadow 0.22s ease;
}
/* targeting-reticle corner brackets, revealed on hover. Excluded: skeletons (shimmer
   ::after), person cards (light-sweep ::after) and the dossier panel (own HUD frame) —
   any future card subtype that owns its ::after must join this exclusion list. */
.card:not(.skeleton-card):not(.person-card):not(.dossier-panel)::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0;
  transform: scale(1.012);
  transition: opacity 0.22s ease, transform 0.22s ease;
  --tick: color-mix(in srgb, var(--accent) 55%, transparent);
  background:
    linear-gradient(var(--tick), var(--tick)) 9px 9px / 12px 1px,
    linear-gradient(var(--tick), var(--tick)) 9px 9px / 1px 12px,
    linear-gradient(var(--tick), var(--tick)) calc(100% - 9px) 9px / 12px 1px,
    linear-gradient(var(--tick), var(--tick)) calc(100% - 9px) 9px / 1px 12px,
    linear-gradient(var(--tick), var(--tick)) 9px calc(100% - 9px) / 12px 1px,
    linear-gradient(var(--tick), var(--tick)) 9px calc(100% - 9px) / 1px 12px,
    linear-gradient(var(--tick), var(--tick)) calc(100% - 9px) calc(100% - 9px) / 12px 1px,
    linear-gradient(var(--tick), var(--tick)) calc(100% - 9px) calc(100% - 9px) / 1px 12px;
  background-repeat: no-repeat;
}
.card:not(.skeleton-card):not(.person-card):not(.dossier-panel):hover::after { opacity: 1; transform: none; }
.card::before {
  content: "";
  position: absolute; top: 0; left: 14px; right: 14px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-strong), transparent);
}

.card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-bottom: 14px; flex-wrap: wrap;
}
.card-head h3 {
  display: flex; align-items: center; gap: 9px;
  font-size: 0.92rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
}
.card-head h3 .icon { color: var(--accent); }
.card-link {
  font-family: var(--font-mono); font-size: 0.74rem;
  letter-spacing: 0.1em; text-transform: uppercase;
}

/* min(Npx, 100%) lets every grid collapse below its preferred card width
   instead of overflowing on narrow screens. */
.grid { display: grid; gap: 18px; }
.dash-grid { grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr)); }
.lib-grid { grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr)); }
.vault-grid { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }
.markets-grid { grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); }
.pihole-grid { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.pihole-grid .span2 { grid-column: span 2; }

/* ---------- deck headers and sections ---------- */
.deck-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
}
.deck-sub { margin: 4px 0 0; color: var(--text-dim); }
.deck-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.deck-section { display: flex; flex-direction: column; gap: 12px; }
.deck-title {
  font-size: 0.9rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-dim);
  display: flex; align-items: center; gap: 10px;
}

/* ---------- buttons and fields ---------- */
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px;
  font-family: var(--font-body); font-weight: 700; font-size: 0.86rem;
  letter-spacing: 0.08em; text-transform: uppercase;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface2);
  color: var(--text);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn.primary {
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 60%, var(--accent2)));
  color: var(--bg0);
  border-color: transparent;
  box-shadow: 0 6px 22px var(--glow);
}
/* charge sweep across the primary action on hover */
.btn.primary::after {
  content: "";
  position: absolute; top: 0; bottom: 0; left: -70%; width: 55%;
  transform: skewX(-18deg);
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, 0.38), transparent);
  opacity: 0;
  pointer-events: none;
}
.btn.primary:hover::after { animation: btn-sweep 0.55s ease; }
@keyframes btn-sweep { 0% { left: -70%; opacity: 1; } 100% { left: 125%; opacity: 1; } }
.btn.ghost { background: transparent; }
.btn.danger { border-color: color-mix(in srgb, var(--bad) 55%, transparent); color: var(--bad); background: transparent; }
.btn.danger:hover { background: color-mix(in srgb, var(--bad) 12%, transparent); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.btn-icon:hover { color: var(--accent); border-color: var(--line-strong); background: var(--surface2); }
.btn-icon.danger:hover { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 50%, transparent); }
.btn-icon.on { color: var(--accent); border-color: var(--accent); box-shadow: 0 0 12px var(--glow); }

.field {
  padding: 9px 13px;
  font-family: var(--font-body); font-size: 0.96rem;
  color: var(--text);
  background: color-mix(in srgb, var(--bg0) 55%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.field:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--glow); }
.field::placeholder { color: var(--text-dim); opacity: 0.8; }
.field.title { font-size: 1.25rem; font-weight: 700; flex: 1; min-width: 0; }
.field.area {
  width: 100%; min-height: 380px; resize: vertical;
  font-family: var(--font-mono); font-size: 0.92rem; line-height: 1.6;
}
.field.grow { flex: 1; min-width: 160px; }

input[type="search"].field { min-width: 200px; }
input[type="color"] {
  width: 52px; height: 38px; padding: 3px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface2); cursor: pointer;
}
input[type="range"] { accent-color: var(--accent); width: 260px; max-width: 100%; }
input[type="checkbox"] { accent-color: var(--accent); width: 16px; height: 16px; }

.row { display: flex; align-items: center; gap: 12px; }
.row.wrap { flex-wrap: wrap; }
.check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; color: var(--text); }

.icon { width: 18px; height: 18px; flex: none; }
.icon.big { width: 40px; height: 40px; color: var(--text-dim); }

/* ---------- chips, badges, tabs ---------- */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 11px;
  /* chamfered instrument-readout silhouette (cut corners, not pills) */
  border-radius: 0;
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
  border: 1px solid var(--line-strong);
  background: var(--surface2);
  font-family: var(--font-mono); font-size: 0.74rem;
  letter-spacing: 0.06em;
  color: var(--text);
  white-space: nowrap;
}
.chip.good { border-color: color-mix(in srgb, var(--good) 55%, transparent); color: var(--good); }
.chip.bad { border-color: color-mix(in srgb, var(--bad) 55%, transparent); color: var(--bad); }
.chip.warn { border-color: color-mix(in srgb, var(--warn) 55%, transparent); color: var(--warn); }
.chip.accent { border-color: var(--accent); color: var(--accent); }

.badge-change { font-family: var(--font-mono); font-weight: 600; font-size: 0.86rem; }
.badge-change.up { color: var(--good); }
.badge-change.down { color: var(--bad); }

.tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.tab {
  padding: 7px 13px;
  font-family: var(--font-body); font-weight: 600; font-size: 0.82rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  background: transparent; color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 0;
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
  cursor: pointer;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }

/* ---------- tables ---------- */
.table-wrap { overflow: auto; -webkit-overflow-scrolling: touch; }
.table-wrap.tall { max-height: 460px; }
.table { width: 100%; border-collapse: collapse; font-size: 0.94rem; }
/* Wide tables keep a sensible minimum and scroll sideways on narrow screens
   instead of crushing their columns. */
.net-table { min-width: 780px; }
#query-table { min-width: 600px; }
#files-table { min-width: 460px; }
.table th {
  position: sticky; top: 0;
  text-align: left;
  font-family: var(--font-mono); font-size: 0.7rem;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-dim);
  padding: 8px 12px;
  background: color-mix(in srgb, var(--bg0) 86%, transparent);
  border-bottom: 1px solid var(--line-strong);
  z-index: 1;
}
.table td { padding: 9px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tbody tr:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.table .num { text-align: right; }
.table td.num { font-family: var(--font-mono); font-size: 0.86rem; }
.table .actions { width: 1%; white-space: nowrap; text-align: right; }
.table .actions .btn-icon { width: 30px; height: 30px; }
.table .actions .icon { width: 15px; height: 15px; }

.row-name { display: flex; align-items: center; gap: 10px; }
.row-name .icon { color: var(--accent); }
.row-name a, .row-name button.linkish {
  background: none; border: none; padding: 0;
  color: var(--text); font: inherit; cursor: pointer; text-align: left;
}
.row-name a:hover, .row-name button.linkish:hover { color: var(--accent); text-decoration: none; }

/* ---------- ranked lists (pihole tops) ---------- */
.rank-list { display: flex; flex-direction: column; gap: 7px; }
.rank-row { position: relative; padding: 7px 10px; border-radius: 7px; overflow: hidden; }
.rank-row .bar {
  position: absolute; inset: 0;
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  transform-origin: left;
}
.rank-row.bad .bar { background: color-mix(in srgb, var(--bad) 14%, transparent); }
.rank-row .lbl {
  position: relative; display: flex; justify-content: space-between; gap: 12px;
  font-family: var(--font-mono); font-size: 0.84rem;
}
.rank-row .lbl .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-row .lbl .val { color: var(--text-dim); flex: none; }

/* ---------- key value grid ---------- */
.kv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: 12px;
}
.kv {
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg0) 35%, transparent);
}
.kv .k {
  font-family: var(--font-mono); font-size: 0.68rem;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 3px;
}
.kv .v { font-weight: 600; word-break: break-word; }

/* ---------- hero ---------- */
.hero {
  display: flex; align-items: center; justify-content: space-between;
  gap: 26px; padding: 30px 34px;
  overflow: hidden;
}
.hero-title {
  font-size: clamp(2.1rem, 4.5vw, 3.4rem);
  font-weight: 900;
  letter-spacing: 0.22em;
  background: linear-gradient(95deg, var(--text), var(--accent) 55%, var(--accent2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: 0 0 38px var(--glow);
}
.hero-sub { color: var(--text-dim); margin: 8px 0 14px; font-size: 1.08rem; }
.hero-pills { display: flex; gap: 8px; flex-wrap: wrap; }
.hero-orb { flex: none; }
.orb { width: 64px; height: 64px; }
.orb.big { width: 190px; height: 190px; filter: drop-shadow(0 0 32px var(--glow)); }
.orb-ring { stroke: var(--line-strong); animation: spin 60s linear infinite; transform-origin: 36px 36px; }
.orb-ring.slow { animation-duration: 95s; animation-direction: reverse; }
.orb-moons { animation: spin 16s linear infinite; transform-origin: 36px 36px; }
.orb-moons.reverse { animation: spin 24s linear infinite reverse; }
@keyframes spin { to { transform: rotate(360deg); } }

/* 3D logo canvases (orb3d.js). Hidden until the module confirms WebGL, so the inline
   SVG orb shows through as a graceful fallback. */
.orb3d { display: none; flex: none; }
.orb3d[data-active] { display: block; }
.dock-logo .orb3d { width: 58px; height: 58px; }
.orb3d-hero { width: 300px; height: 300px; max-width: 44vw; filter: drop-shadow(0 0 30px var(--glow)); }
@media (max-width: 980px) { .orb3d-hero { width: 220px; height: 220px; } }
@media (max-width: 560px) { .orb3d-hero { width: 168px; height: 168px; } }

/* Settings · per-body image pickers for the 3D logo / solar system */
.bodies-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 12px; margin-top: 6px; }
.body-card { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px 10px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface2); }
.body-thumb {
  display: block;
  width: 72px; height: 72px; border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: radial-gradient(circle at 35% 30%, var(--surface2), var(--bg0));
  background-size: cover; background-position: center;
  box-shadow: inset 0 0 18px rgba(0, 0, 0, 0.55);
  transition: transform 0.15s ease;
}
.body-thumb.has { border-color: var(--accent); }
/* the thumbnail doubles as "make this body the dock logo" (every body except the Sun) */
.body-pick { padding: 0; border: 0; background: none; line-height: 0; border-radius: 50%; cursor: pointer; }
.body-pick:hover .body-thumb { transform: scale(1.07); }
.body-card.active { border-color: var(--accent); }
.body-card.active .b-name::after {
  content: ' · LOGO';
  color: var(--accent); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em;
}
.body-card .b-name { font-weight: 600; font-size: 0.92rem; letter-spacing: 0.02em; }
.body-actions { display: flex; gap: 6px; }
.body-actions .btn { padding: 5px 10px; font-size: 0.8rem; }

/* ---------- ticker ---------- */
.ticker { padding: 10px 0; overflow: hidden; white-space: nowrap; }
.ticker-track {
  display: inline-flex; gap: 38px;
  padding-left: 20px;
  animation: ticker 55s linear infinite;
  will-change: transform;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
@keyframes ticker { to { transform: translateX(-50%); } }
.tick { display: inline-flex; align-items: center; gap: 9px; font-family: var(--font-mono); font-size: 0.88rem; }
.tick .sym { color: var(--text-dim); letter-spacing: 0.06em; }

/* ---------- widgets ---------- */
.widget .card-body { display: flex; flex-direction: column; gap: 10px; min-height: 120px; }
.stat-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.stat-big { font-family: var(--font-display); font-size: 1.7rem; font-weight: 700; color: var(--accent); text-shadow: 0 0 18px var(--glow); }
.stat-label { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.mini-list { display: flex; flex-direction: column; gap: 6px; font-size: 0.92rem; }
.mini-list .mini-row { display: flex; justify-content: space-between; gap: 12px; }
.mini-list .mini-row span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-list .mini-row span:last-child {
  color: var(--text-dim); font-family: var(--font-mono); font-size: 0.84rem;
  flex: none; max-width: 55%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- progress bars and meters ---------- */
.meter {
  position: relative;
  height: 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--text-dim) 18%, transparent);
  overflow: hidden;
}
.meter > i {
  position: absolute; inset: 0 auto 0 0;
  display: block;
  background: linear-gradient(90deg, var(--accent), var(--accent2));
  border-radius: 999px;
}
.meter.thin { height: 4px; }

/* ---------- sparklines and charts ---------- */
.spark { width: 130px; height: 38px; overflow: visible; }
.spark .spark-line { fill: none; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; }
.spark .spark-area { stroke: none; opacity: 0.16; }
.spark.up .spark-line { stroke: var(--good); }
.spark.up .spark-area { fill: var(--good); }
.spark.down .spark-line { stroke: var(--bad); }
.spark.down .spark-area { fill: var(--bad); }
.spark.flat .spark-line { stroke: var(--accent2); }
.spark.flat .spark-area { fill: var(--accent2); }

.chart-wrap { width: 100%; }
.chart-wrap svg { width: 100%; height: 180px; display: block; }
.hbar { fill: var(--accent); opacity: 0.75; }
.hbar.blocked { fill: var(--bad); opacity: 0.85; }
.chart-legend { display: flex; gap: 18px; margin-top: 8px; font-size: 0.82rem; color: var(--text-dim); }
.chart-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }

.donut-wrap { display: flex; align-items: center; gap: 16px; }
.donut { transform: rotate(-90deg); }
.donut .track { fill: none; stroke: color-mix(in srgb, var(--text-dim) 22%, transparent); }
.donut .value { fill: none; stroke: var(--accent); stroke-linecap: round; transition: stroke-dasharray 0.6s ease; }
.donut-label { font-family: var(--font-display); font-size: 1.3rem; color: var(--accent); }

/* ---------- pihole specifics ---------- */
.pihole-status .status-line { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.blocking-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.stat-strip { display: flex; gap: 26px; flex-wrap: wrap; margin-top: 16px; }
.stat-strip .stat { min-width: 110px; }
.qstatus { font-family: var(--font-mono); font-size: 0.78rem; }
.qstatus.blocked { color: var(--bad); }
.qstatus.ok { color: var(--good); }
.qstatus.cached { color: var(--accent2); }
.domain-list { display: flex; flex-direction: column; margin-top: 10px; max-height: 380px; overflow: auto; }
.domain-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 6px;
  border-bottom: 1px solid var(--line);
}
.domain-row .d { font-family: var(--font-mono); font-size: 0.88rem; word-break: break-all; }
.domain-row .c { color: var(--text-dim); font-size: 0.84rem; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.domain-row .btn-icon { margin-left: auto; flex: none; }
.list-add { margin-top: 4px; flex-wrap: wrap; }

/* ---------- plex specifics ---------- */
.server-line { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.server-line .name { font-family: var(--font-display); font-size: 1.15rem; }
.sessions { display: flex; flex-direction: column; gap: 14px; }
.session-card { display: flex; gap: 16px; }
.session-art {
  width: 72px; height: 105px; flex: none;
  border-radius: 8px;
  object-fit: cover;
  border: 1px solid var(--line-strong);
  background: var(--surface2);
}
.session-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.session-title { font-weight: 700; font-size: 1.04rem; }
.session-meta { display: flex; gap: 8px; flex-wrap: wrap; }
/* Device/player names come from Plex clients and can be long; let the chip wrap its
   text instead of overflowing the session card on narrow screens. */
.session-meta .chip { max-width: 100%; white-space: normal; word-break: break-word; }
.session-actions { flex: none; display: flex; align-items: flex-start; }
.lib-card { display: flex; flex-direction: column; gap: 8px; }
.lib-card .lib-count { font-family: var(--font-display); font-size: 1.6rem; color: var(--accent); }
.lib-card .lib-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.poster-rail { display: flex; gap: 14px; overflow-x: auto; padding-bottom: 8px; }
.poster {
  flex: none; width: 124px;
  text-decoration: none !important; color: var(--text);
}
.poster img {
  width: 124px; height: 184px;
  object-fit: cover;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: var(--surface2);
  transition: transform 0.16s ease, border-color 0.16s ease;
}
.poster:hover img { transform: translateY(-3px); border-color: var(--accent); }
.poster .p-title { font-size: 0.84rem; font-weight: 600; margin-top: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.poster .p-sub { font-size: 0.76rem; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.activity-row { display: flex; flex-direction: column; gap: 6px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.activity-row:last-child { border-bottom: none; }

/* ---------- markets specifics ---------- */
.mkt-card { display: flex; flex-direction: column; gap: 10px; }
.mkt-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.mkt-name { display: flex; align-items: center; gap: 9px; font-weight: 700; min-width: 0; }
.mkt-name .flag { font-size: 1.25rem; }
.mkt-name img { width: 22px; height: 22px; border-radius: 50%; }
.mkt-name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mkt-sym { font-family: var(--font-mono); font-size: 0.74rem; color: var(--text-dim); }
.mkt-price { font-family: var(--font-display); font-size: 1.45rem; }
.mkt-bottom { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.mkt-card .spark { width: 100%; height: 44px; }

/* ============================================================
   Mail + Calendar decks · Microsoft Graph mailbox
   ============================================================ */
.ml-layout { display: grid; grid-template-columns: 250px 1fr; gap: 16px; align-items: start; }
.ml-folders { display: flex; flex-direction: column; gap: 3px; padding: 6px 0; }
.ml-folder {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 10px; border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--text); text-align: left; cursor: pointer; font-size: 0.92rem;
}
.ml-folder:hover { background: var(--surface2); }
.ml-folder.on { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.ml-folder-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ml-unread {
  font-family: var(--font-mono); font-size: 0.72rem; font-weight: 700;
  padding: 1px 7px; border-radius: 999px; flex: none;
  background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent);
}
.ml-list { display: flex; flex-direction: column; gap: 6px; }
.ml-row {
  padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius-sm); cursor: pointer;
}
.ml-row:hover { border-color: var(--line-strong); background: var(--surface2); }
.ml-row:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
.ml-row-top { display: flex; justify-content: space-between; gap: 10px; }
.ml-from { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ml-row.unread .ml-from, .ml-row.unread .ml-subject { font-weight: 700; }
.ml-row.unread { border-left: 3px solid var(--accent); }
.ml-row-mid { display: flex; align-items: center; gap: 7px; min-width: 0; }
.ml-subject { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ml-imp { color: var(--bad); font-weight: 800; flex: none; }
.ml-clip { width: 13px; height: 13px; color: var(--text-dim); flex: none; }
.ml-preview { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }

.ml-read-panel { max-width: 760px; width: 100%; }
.ml-meta { display: flex; flex-direction: column; gap: 3px; margin-bottom: 12px; }
.ml-atts { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.ml-att { cursor: pointer; text-decoration: none; }
/* plain text by design: pre-wrap keeps the author's line breaks without any HTML rendering */
.ml-body {
  white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.95rem;
  border-top: 1px solid var(--line); padding-top: 12px; max-height: 46vh; overflow-y: auto;
}
.ml-replybox { margin-top: 12px; }
.ml-replybox textarea, .ml-compose textarea { width: 100%; resize: vertical; }
.ml-compose { display: flex; flex-direction: column; gap: 8px; }
.ml-compose .field { width: 100%; }

.ca-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; margin-bottom: 14px;
}
.ca-nav { display: flex; align-items: center; gap: 6px; min-width: 0; }
.ca-nav .btn { padding: 6px 12px; }
.ca-title { margin: 0 0 0 8px; font-size: 1.05rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ca-views { display: flex; gap: 5px; flex-wrap: wrap; }
.ca-form-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.ca-allday { padding-bottom: 9px; }
.ca-cal { padding: 12px 14px; }

/* time grid: a header band, then a scrolling 24h canvas. --ca-cols (1/5/7) is set on
   #ca-timegrid by the deck; the shared 56px first track is the hour gutter. */
.ca-heads, .ca-alldayrow { display: grid; grid-template-columns: 56px repeat(var(--ca-cols, 7), minmax(0, 1fr)); }
.ca-head {
  border: 0; background: none; color: var(--text); cursor: pointer;
  padding: 6px 4px; text-align: center; font-size: 0.85rem; border-radius: var(--radius-sm);
}
.ca-head span { font-family: var(--font-display); font-size: 1.05rem; margin-left: 4px; }
.ca-head:hover { background: var(--surface2); }
.ca-head.today { color: var(--accent); }
.ca-alldayrow { border-top: 1px solid var(--line); padding: 4px 0; }
.ca-alldaycell { display: flex; flex-direction: column; gap: 3px; padding: 2px 3px; min-width: 0; }
.ca-gutterhead { align-self: center; text-align: right; padding-right: 8px; }

.ca-scroll { max-height: 64vh; overflow-y: auto; border-top: 1px solid var(--line); }
.ca-grid {
  position: relative;
  display: grid; grid-template-columns: 56px repeat(var(--ca-cols, 7), minmax(0, 1fr));
  /* one faint line per hour, drawn once on the container */
  background: repeating-linear-gradient(to bottom, var(--line) 0 1px, transparent 1px 44px);
}
.ca-gutter { position: relative; }
.ca-hourlabel {
  position: absolute; right: 8px; transform: translateY(-50%);
  font-family: var(--font-mono); font-size: 0.68rem; color: var(--text-dim);
}
.ca-col { position: relative; border-left: 1px solid var(--line); min-width: 0; }
.ca-col:hover { background: color-mix(in srgb, var(--accent) 3%, transparent); cursor: copy; }
.ca-block {
  position: absolute; overflow: hidden; text-align: left; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  border-radius: 6px; padding: 2px 6px; font-size: 0.78rem; line-height: 1.25;
  color: var(--text);
  background: color-mix(in srgb, var(--accent) 16%, var(--surface));
}
.ca-block:hover { background: color-mix(in srgb, var(--accent) 26%, var(--surface)); z-index: 2; }
.ca-block-time { font-family: var(--font-mono); font-size: 0.68rem; color: var(--accent); }
.ca-now {
  position: absolute; left: 0; right: 0; height: 2px; pointer-events: none; z-index: 1;
  background: var(--bad);
  box-shadow: 0 0 6px color-mix(in srgb, var(--bad) 60%, transparent);
}

/* month grid */
.ca-mgrid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border-top: 1px solid var(--line); }
.ca-mhead {
  padding: 6px 4px; text-align: center; font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim);
}
.ca-mcell {
  min-height: 96px; padding: 4px; border-left: 1px solid var(--line); border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 3px; min-width: 0; cursor: copy;
}
.ca-mcell:hover { background: color-mix(in srgb, var(--accent) 4%, transparent); }
.ca-mcell.other .ca-mday { opacity: 0.35; }
.ca-mday { font-family: var(--font-display); font-size: 0.9rem; align-self: flex-end; padding: 0 3px; }
.ca-mcell.today .ca-mday {
  color: var(--bg0); background: var(--accent); border-radius: 999px;
  min-width: 22px; text-align: center;
}
.ca-chipev {
  display: block; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border: 0; text-align: left; cursor: pointer; border-radius: 5px; padding: 2px 6px;
  font-size: 0.74rem; color: var(--text);
  background: color-mix(in srgb, var(--accent) 16%, var(--surface));
}
.ca-chipev:hover { background: color-mix(in srgb, var(--accent) 28%, var(--surface)); }
.ca-chipev.allday { border-left: 3px solid var(--accent); }
.ca-more {
  border: 0; background: none; color: var(--accent); cursor: pointer;
  font-size: 0.72rem; text-align: left; padding: 0 6px;
}

@media (max-width: 820px) {
  .ml-layout { grid-template-columns: 1fr; }
  .ca-mcell { min-height: 64px; }
  .ca-hourlabel { right: 4px; }
  .ca-heads, .ca-alldayrow, .ca-grid { grid-template-columns: 42px repeat(var(--ca-cols, 7), minmax(0, 1fr)); }
}

/* every card is a detail button now */
.mkt-card[data-id] { cursor: pointer; }
.mkt-card[data-id]:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }

/* the remove control sits inside the card, so it must win the click before the card does — the
   card's ::after reticle is pointer-events:none, so it cannot swallow this either */
.mkt-watched .mkt-unwatch { position: absolute; top: 8px; right: 8px; opacity: 0; z-index: 1; }
.mkt-watched:hover .mkt-unwatch,
.mkt-watched:focus-within .mkt-unwatch { opacity: 1; }

/* search: results stay in normal flow rather than an absolute dropdown, so no ancestor's
   clip-path or overflow can crop them */
.mkt-search-card { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.mkt-search-label {
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-dim);
}
.mkt-results { display: flex; flex-direction: column; gap: 6px; max-height: 340px; overflow-y: auto; }
.mkt-hit {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.mkt-hit:hover { border-color: var(--line-strong); background: var(--surface2); }
.mkt-hit-main { display: flex; align-items: center; gap: 10px; min-width: 0; }
.mkt-hit-main img { width: 24px; height: 24px; border-radius: 50%; flex: none; }
.mkt-hit-text { min-width: 0; }
.mkt-hit-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mkt-hit-acts { display: flex; gap: 6px; flex: none; }
.mkt-hit-acts .btn { padding: 5px 11px; font-size: 0.82rem; }

/* detail panel */
.mkt-detail-panel { max-width: 760px; width: 100%; }
.mkt-detail-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 14px; }
.mkt-detail-img { width: 40px; height: 40px; border-radius: 50%; }
.mkt-ranges { display: flex; gap: 5px; margin-left: auto; flex-wrap: wrap; }
.mkt-range { cursor: pointer; }
.mkt-range.on { border-color: var(--accent); color: var(--accent); }
.mkt-chart { position: relative; padding: 0 62px 18px 0; }
.mkt-chart .spark { width: 100%; height: 200px; }
.mkt-axis-y {
  position: absolute; top: 0; right: 0; bottom: 18px; width: 58px;
  display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end;
  font-family: var(--font-mono); font-size: 0.7rem; color: var(--text-dim);
}
.mkt-axis-x {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: 0.7rem; color: var(--text-dim);
}
.mkt-stats {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px 18px;
  margin-top: 16px; border-top: 1px solid var(--line); padding-top: 14px;
}
.mkt-stat { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }

@media (max-width: 640px) {
  .mkt-ranges { margin-left: 0; }
  .mkt-chart { padding-right: 0; }
  .mkt-axis-y { display: none; }
}

/* ---------- files specifics ---------- */
@keyframes file-in {
  from { opacity: 0; transform: translateY(8px); }
}
@keyframes ants {
  to { background-position: 24px 0, -24px 100%, 0 -24px, 100% 24px; }
}
@keyframes meter-stripe { to { background-position: 22.6px 0; } }

/* terminal-style path bar */
.crumbs { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; padding: 10px 16px; font-family: var(--font-mono); font-size: 0.86rem; }
.crumbs::before { content: "\276F"; color: var(--accent); font-weight: 700; margin-right: 3px; }
.crumbs a, .crumbs button {
  background: none; border: none; padding: 3px 7px;
  color: var(--text-dim); font: inherit; cursor: pointer; border-radius: 5px;
}
.crumbs a:hover, .crumbs button:hover { color: var(--accent); background: var(--surface2); text-decoration: none; }
.crumbs .sep { color: var(--line-strong); }
.crumbs .here {
  color: var(--accent); padding: 3px 7px; border-radius: 5px;
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  text-shadow: 0 0 10px var(--glow);
}
.files-card { position: relative; min-height: 200px; }
/* marching-ants drop target: four animated edge strips over the tinted fill */
.dropzone {
  position: absolute; inset: 8px;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  border: none;
  border-radius: var(--radius);
  background:
    /* 24px tiles that repeat along their edge, so the 24px keyframe shift wraps seamlessly */
    linear-gradient(90deg, var(--accent) 0 12px, transparent 12px 24px) 0 0 / 24px 2px repeat-x,
    linear-gradient(90deg, var(--accent) 0 12px, transparent 12px 24px) 0 100% / 24px 2px repeat-x,
    linear-gradient(180deg, var(--accent) 0 12px, transparent 12px 24px) 0 0 / 2px 24px repeat-y,
    linear-gradient(180deg, var(--accent) 0 12px, transparent 12px 24px) 100% 0 / 2px 24px repeat-y,
    color-mix(in srgb, var(--accent) 8%, transparent);
  animation: ants 0.9s linear infinite;
  color: var(--accent);
  font-weight: 700; letter-spacing: 0.06em;
  z-index: 5;
  pointer-events: none;
}
.upload-progress { display: flex; flex-direction: column; gap: 9px; }
/* display:flex above overrides the UA [hidden] rule, so honor the attribute explicitly
   (same trap as .palette) — otherwise the drop overlay and finished progress bar stay
   on screen forever. */
.dropzone[hidden], .upload-progress[hidden] { display: none; }

/* view switcher (icons / list / details / world): a pill rail with a glowing active lens */
.view-seg {
  display: flex; gap: 4px; margin-right: 4px;
  padding: 4px;
  border: 1px solid var(--line); border-radius: 999px;
  background: color-mix(in srgb, var(--bg0) 40%, transparent);
}
.view-seg .btn-icon { width: 34px; height: 30px; border: none; border-radius: 999px; }
.view-seg .btn-icon.on {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  box-shadow: 0 0 10px var(--glow);
}

.files-empty { padding: 36px 14px; text-align: center; }

/* list view */
.files-list { display: flex; flex-direction: column; }
.files-row {
  display: flex; align-items: center; gap: 11px;
  padding: 8px 6px;
  border-bottom: 1px solid var(--line);
  min-width: 0;
}
.files-row:last-child { border-bottom: none; }
.files-row:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.f-name {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 10px;
}
/* the folder variant is a button; strip the default button chrome */
button.f-name.linkish {
  background: none; border: none; padding: 0;
  color: var(--text); font: inherit; cursor: pointer; text-align: left;
}
button.f-name.linkish:hover { color: var(--accent); }
.f-name .f-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.f-size { flex: none; }
.f-actions { flex: none; display: flex; gap: 4px; }
.f-actions .btn-icon { width: 30px; height: 30px; }
.f-actions .icon { width: 15px; height: 15px; }

/* icon (tile) view */
.files-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(132px, 100%), 1fr));
  gap: 12px;
  padding: 4px 0;
}
/* --tc is the file-type colour set inline by files.js; folders carry var(--accent) */
.file-tile {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 18px 10px 13px;
  border: 1px solid var(--line);
  border-top-color: color-mix(in srgb, var(--tc, var(--accent)) 40%, var(--line));
  border-radius: var(--radius-sm);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--tc, var(--accent)) 9%, transparent), transparent 40%),
    color-mix(in srgb, var(--bg0) 30%, transparent);
  cursor: pointer;
  text-align: center;
  transition: border-color 0.13s ease, transform 0.13s ease, background 0.13s ease, box-shadow 0.13s ease;
  animation: file-in 0.35s ease backwards;
  animation-delay: calc(var(--i, 0) * 14ms);
}
.file-tile:hover,
.file-tile:focus-within {
  border-color: color-mix(in srgb, var(--tc, var(--accent)) 45%, var(--line-strong));
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--tc, var(--accent)) 13%, transparent), transparent 45%),
    var(--surface2);
}
.file-tile:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 24px color-mix(in srgb, var(--tc, var(--accent)) 18%, transparent);
}
.file-tile .icon.big { width: 36px; height: 36px; }
/* real cover/preview thumbnail: replaces the generic icon once it loads */
.file-tile .t-thumb {
  width: 72px; height: 96px; object-fit: contain;
  border-radius: 3px;
  background: color-mix(in srgb, var(--bg0) 40%, transparent);
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3);
}
.file-tile.has-thumb .icon.big { display: none; }
.file-tile .t-name {
  max-width: 100%;
  font-size: 0.84rem; font-weight: 600; line-height: 1.25;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  word-break: break-word;
}
.file-tile .t-size { font-size: 0.7rem; }
.file-tile .t-actions {
  position: absolute; top: 5px; right: 5px;
  display: flex; gap: 3px;
  opacity: 0;
  transition: opacity 0.13s ease;
}
.file-tile:hover .t-actions,
.file-tile:focus-within .t-actions,
.file-tile:focus .t-actions { opacity: 1; }
.t-actions .btn-icon { width: 27px; height: 27px; background: color-mix(in srgb, var(--bg0) 72%, transparent); }
.t-actions .icon { width: 14px; height: 14px; }
/* touch devices have no hover; keep tile actions visible */
@media (pointer: coarse) {
  .file-tile .t-actions { opacity: 1; }
}
.upload-progress .u-row { display: flex; align-items: center; gap: 12px; font-size: 0.9rem; }
.upload-progress .u-row .meter { flex: 1; height: 9px; }
/* animated diagonal stripes ride the fill while a transfer is running */
.upload-progress .meter > i { box-shadow: 0 0 12px var(--glow); }
.upload-progress .meter > i::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.22) 0 8px, transparent 8px 16px);
  /* 16px period at 45deg = 22.6px horizontal travel for a seamless loop */
  animation: meter-stripe 0.7s linear infinite;
}
/* details/list rows: type-coloured accent bar tracks the hover */
#files-table tbody tr { transition: background 0.12s ease; }
#files-table tbody tr:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
#files-table tbody tr:hover td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
.files-row:hover { box-shadow: inset 2px 0 0 var(--accent); }

/* Gallery view — the "Gallery" layout button swaps #files-view for a DomeGallery React island (a
   full-stage dome of the current folder's images). Plain divs, no .card: they must stay outside the
   .card::after reticle exclusion list (jupiter-skin-v2.md landmine #1), and since the swap is a
   hidden-attribute flip on a plain div (not .content>*'s tracked children) it never runs through the
   .content>* entrance stagger (landmine #2) either. Mirrors .op-dome-stage / .apod-dome-stage. */
.files-gallery-stage { display: flex; flex-direction: column; gap: 8px; }
.files-dome-stage {
  height: 70vh; min-height: 320px; position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: 14px; background: var(--bg0);
}

/* ---------- library (ebook catalogue) ---------- */
/* Bookshelf treatment: every card is a book standing on a wooden plank (library.js renders
   a .lib-plank after each cover; planks bleed half the 30px column gap each side so a row
   joins into one continuous shelf inside the wooden .lib-case). Wood tones are fixed browns
   blended with theme tokens so all six themes — including light "ice" — read as wood.
   Rules on .lib-card/.lib-grid are scoped to body[data-page="library"]: the Plex deck reuses
   those class names for its library-count tiles and must not inherit book styling. */
body[data-page="library"] {
  --wood-hi: color-mix(in srgb, #96653a 62%, var(--bg1));
  --wood: color-mix(in srgb, #64401f 72%, var(--bg1));
  --wood-lo: color-mix(in srgb, #38220e 78%, var(--bg0));
  --brass: color-mix(in srgb, #d9a441 72%, var(--accent));
}
.deck-actions #lib-search { min-width: 200px; }

/* the bookcase: warm backboard with faint plank grain, side rails, reading-lamp glow up top */
.lib-case {
  padding: 24px 26px 28px;
  background:
    radial-gradient(60% 110px at 50% -14px, color-mix(in srgb, var(--brass) 22%, transparent), transparent 75%),
    linear-gradient(90deg, var(--wood-lo) 0 10px, transparent 10px calc(100% - 10px), var(--wood-lo) calc(100% - 10px)),
    repeating-linear-gradient(90deg, transparent 0 54px, rgba(0, 0, 0, 0.07) 54px 56px),
    linear-gradient(180deg, color-mix(in srgb, var(--wood-lo) 46%, var(--bg1)), color-mix(in srgb, var(--wood-lo) 68%, var(--bg0)));
  box-shadow: var(--shadow, 0 14px 40px rgba(0, 0, 0, 0.35)), inset 0 0 70px rgba(0, 0, 0, 0.4);
}
html[data-theme="ice"] .lib-case {
  box-shadow: var(--shadow), inset 0 0 60px rgba(70, 40, 12, 0.14);
}

body[data-page="library"] .lib-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(148px, 100%), 1fr));
  column-gap: 30px; row-gap: 30px;
}
@keyframes lib-book-in {
  from { opacity: 0; transform: translateY(12px); }
}
body[data-page="library"] .lib-card {
  display: flex; flex-direction: column;
  gap: 0; /* the Plex deck's unscoped .lib-card gap would float books off their planks */
  padding: 0; cursor: pointer;
  perspective: 700px;
  animation: lib-book-in 0.45s ease backwards;
  animation-delay: calc(var(--i, 0) * 22ms);
}
body[data-page="library"] .lib-cover {
  position: relative; aspect-ratio: 3 / 4; width: 100%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--accent) 16%, var(--bg1)),
    color-mix(in srgb, var(--bg0) 78%, var(--accent2)) 75%);
  border: 1px solid color-mix(in srgb, var(--line-strong) 65%, transparent);
  /* a book: square binding edge on the left, softly rounded fore-edge on the right */
  border-radius: 2px 5px 5px 2px;
  overflow: hidden;
  transform-origin: 0% 85%;
  transition: transform 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.25), box-shadow 0.28s ease;
  box-shadow: 3px 4px 9px rgba(0, 0, 0, 0.45);
}
body[data-page="library"] .lib-card:hover .lib-cover,
body[data-page="library"] .lib-card:focus-visible .lib-cover {
  transform: translateY(-9px) rotateY(-14deg) scale(1.02);
  box-shadow: 12px 16px 26px rgba(0, 0, 0, 0.5), 0 0 20px color-mix(in srgb, var(--brass) 28%, transparent);
}
/* spine crease down the binding edge */
body[data-page="library"] .lib-cover::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 12%;
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.42), rgba(255, 255, 255, 0.1) 55%, transparent);
  z-index: 2; pointer-events: none;
}
/* stacked page edges along the fore-edge */
body[data-page="library"] .lib-cover::after {
  content: ""; position: absolute; inset: 2px 0 2px auto; width: 6px;
  background: repeating-linear-gradient(180deg, #efe7d2 0 2px, #cbc2a8 2px 3px);
  border-left: 1px solid rgba(0, 0, 0, 0.25);
  opacity: 0.85;
  z-index: 2; pointer-events: none;
}
/* coverless books read as a cloth binding with a brass frame instead of a bare icon */
body[data-page="library"] .lib-cover:not(.has-cover) {
  box-shadow: 3px 4px 9px rgba(0, 0, 0, 0.45),
    inset 0 0 0 1px color-mix(in srgb, var(--brass) 40%, transparent),
    inset 0 0 30px rgba(0, 0, 0, 0.3);
}
/* Opacity, not display:none — a display:none image has no layout box, so the browser's lazy
   loader never fires its load event and the cover would never appear. Kept absolutely positioned
   so it overlays the fallback icon and fades in once decoded. */
.lib-cover .lib-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 0.2s ease; }
.lib-cover.has-cover .lib-img { opacity: 1; }
.lib-cover .lib-fallback {
  width: 34px; height: 34px; color: var(--brass);
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--brass) 35%, transparent));
}
.lib-cover.has-cover .lib-fallback { display: none; }
/* the shelf board the book stands on; negative margins join neighbouring planks */
.lib-plank {
  flex: none; height: 13px;
  margin: 0 -15px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--wood-hi), var(--wood) 45%, var(--wood-lo));
  box-shadow: 0 7px 12px -5px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
.lib-badge {
  position: absolute; top: 6px; left: 6px; z-index: 3;
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.08em;
  padding: 2px 6px; border-radius: 4px; border: 1px solid;
}
.lib-badge.epub { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.lib-badge.pdf { color: #f59e0b; border-color: #f59e0b; background: rgba(245, 158, 11, 0.12); }
/* spine-label typography under the shelf: serif titles, italic serif authors */
body[data-page="library"] .lib-title {
  margin-top: 9px;
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 0.86rem; font-weight: 600; line-height: 1.3;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; word-break: break-word;
}
body[data-page="library"] .lib-author {
  font-family: Georgia, 'Times New Roman', serif; font-style: italic;
  line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* ice (light theme): --text-dim is a pale blue-grey that vanishes against the light oak
   case (~1.4:1) and the brass active-shelf label drops below AA — use dark ink / accent */
html[data-theme="ice"] body[data-page="library"] .lib-title,
html[data-theme="ice"] body[data-page="library"] .lib-author { color: #3a2a18; }
html[data-theme="ice"] .shelf-row.active .shelf-name { color: var(--accent); }

/* shelves sidebar: a folder-hierarchy tree on the left (desktop); on narrow screens the pane
   hides behind the "Shelves" navigator button in the deck actions. */
.lib-layout { display: grid; grid-template-columns: 250px 1fr; gap: 18px; align-items: start; }
.lib-shelves-pane {
  display: flex; flex-direction: column; gap: 10px;
  max-height: calc(100vh - 170px); overflow-y: auto;
}
.lib-shelves-toggle { display: none; }
.shelf-tree, .shelf-branch { list-style: none; margin: 0; padding: 0; }
.shelf-branch { margin-left: 10px; padding-left: 7px; border-left: 1px solid var(--line); }
.shelf-row { display: flex; align-items: center; border-radius: var(--radius-sm); }
.shelf-row:hover { background: var(--surface2); }
.shelf-row.active {
  background: color-mix(in srgb, var(--brass, var(--accent)) 12%, transparent);
  box-shadow: inset 2px 0 0 var(--brass, var(--accent));
}
.shelf-row.active .shelf-name { color: var(--brass, var(--accent)); }
.shelf-caret {
  flex: none; width: 22px; height: 32px;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--text-dim); font-size: 0.72rem; line-height: 32px;
}
.shelf-caret::before { content: "\25B8"; display: inline-block; transition: transform 0.12s ease; }
.shelf-caret.open::before { transform: rotate(90deg); }
.shelf-caret.leaf { cursor: default; }
.shelf-caret.leaf::before { content: ""; }
.shelf-name {
  flex: 1; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  background: none; border: 0; padding: 6px 8px 6px 0; cursor: pointer;
  font: inherit; font-size: 0.9rem; color: var(--text); text-align: left;
}
.shelf-name .sn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shelf-count { flex: none; font-family: var(--font-mono); font-size: 0.72rem; color: var(--text-dim); }

/* ---------- library members (settings approvals) ---------- */
.mem-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 12px 0; border-top: 1px solid var(--line); }
.mem-row:first-child { border-top: 0; }
.mem-main { min-width: 0; }
.mem-email { font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mem-notes { margin-top: 4px; white-space: pre-wrap; word-break: break-word; }
.mem-meta { margin-top: 4px; }
.mem-actions { display: flex; gap: 6px; flex-shrink: 0; }
.mem-status { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.06em; padding: 2px 7px; border-radius: 999px; border: 1px solid; }
.mem-status.good { color: #4ade80; border-color: #4ade80; }
.mem-status.warn { color: #fbbf24; border-color: #fbbf24; }
.mem-status.bad { color: #f87171; border-color: #f87171; }
.mem-actions { align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.mem-actions .pub-folder { width: 150px; }

/* member upload confirmation / progress line on the Library deck */
.lib-upload-status { margin: 12px 0 0; padding: 10px 14px; border-radius: 8px; border: 1px solid var(--line); font-size: 0.9rem; }
.lib-upload-status.good { color: #4ade80; border-color: #4ade80; }
.lib-upload-status.bad { color: #f87171; border-color: #f87171; }

/* ---------- notes specifics ---------- */
.notes-layout {
  display: grid;
  grid-template-columns: 330px 1fr;
  gap: 18px;
  align-items: start;
}
.notes-list-pane { display: flex; flex-direction: column; gap: 12px; max-height: calc(100vh - 170px); }
.notes-tools { display: flex; gap: 8px; }
.notes-tools .field { flex: 1; min-width: 0; }
.notes-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.notes-tags .chip { cursor: pointer; }
.notes-tags .chip.active { border-color: var(--accent); color: var(--accent); }
.notes-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 7px; }
.note-item {
  position: relative;
  padding: 11px 13px 11px 17px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.note-item::before {
  content: "";
  position: absolute; left: 0; top: 8px; bottom: 8px;
  width: 3px; border-radius: 3px;
  background: var(--note-color, var(--line-strong));
}
.note-item:hover { background: var(--surface2); }
.note-item.active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-item .n-title { font-weight: 700; display: flex; align-items: center; gap: 7px; }
.note-item .n-title .icon { width: 13px; height: 13px; color: var(--warn); }
.note-item .n-prev { font-size: 0.84rem; color: var(--text-dim); margin-top: 3px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.note-item .n-date { font-family: var(--font-mono); font-size: 0.7rem; color: var(--text-dim); margin-top: 5px; }
.notes-editor { min-height: 560px; display: flex; flex-direction: column; }
#editor-pane { display: flex; flex-direction: column; gap: 12px; flex: 1; }
.editor-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.editor-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.editor-foot { display: flex; justify-content: flex-end; }
.color-dots { display: flex; gap: 6px; }
.color-dot {
  width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer; padding: 0;
}
.color-dot.active { border-color: var(--text); box-shadow: 0 0 8px var(--glow); }
.empty {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; color: var(--text-dim); text-align: center; min-height: 220px;
}
.note-preview {
  flex: 1; padding: 16px 18px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg0) 40%, transparent);
  overflow: auto; line-height: 1.6;
}
.note-preview h1, .note-preview h2, .note-preview h3 { font-family: var(--font-body); margin: 0.7em 0 0.35em; }
.note-preview code {
  font-family: var(--font-mono); font-size: 0.86em;
  background: var(--surface2); padding: 2px 6px; border-radius: 5px;
}
.note-preview pre {
  background: color-mix(in srgb, var(--bg0) 70%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 13px 15px; overflow: auto;
}
.note-preview pre code { background: none; padding: 0; }
.note-preview ul, .note-preview ol { margin: 0.4em 0; padding-left: 1.4em; }
.note-preview blockquote {
  margin: 0.5em 0; padding: 2px 0 2px 12px;
  border-left: 3px solid var(--line-strong); color: var(--text-dim);
}

/* ---------- notes markdown toolbar + live preview ---------- */
.md-toolbar { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.md-btn {
  font: inherit; font-size: 0.8rem; line-height: 1;
  min-width: 30px; height: 30px; padding: 0 9px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface2); color: var(--text-dim); cursor: pointer;
  transition: color 0.12s ease, border-color 0.12s ease;
}
.md-btn:hover { color: var(--text); border-color: var(--accent); }
.md-btn.mono { font-family: var(--font-mono); }
.md-btn b { font-weight: 800; }
.md-btn i { font-style: italic; }
.md-sep { width: 1px; height: 18px; background: var(--line); margin: 0 3px; }
.editor-body { flex: 1; display: flex; flex-direction: column; }
.editor-body.split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: stretch; }
.editor-body.split .note-preview { min-height: 0; }

/* ---------- vault specifics ---------- */
.vault-card { display: flex; flex-direction: column; gap: 10px; }
.vault-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.vault-name { font-weight: 700; font-size: 1.06rem; display: flex; align-items: center; gap: 9px; min-width: 0; }
.vault-name .icon { color: var(--accent); flex: none; }
.vault-name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vault-kv { display: flex; align-items: center; gap: 9px; font-size: 0.92rem; min-width: 0; }
.vault-kv .k { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim); width: 74px; flex: none; }
.vault-kv .v { font-family: var(--font-mono); font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.vault-kv .btn-icon { width: 28px; height: 28px; flex: none; }
.vault-kv .btn-icon .icon { width: 14px; height: 14px; }
.vault-actions { display: flex; gap: 8px; margin-top: 4px; }

/* live TOTP code + countdown ring */
.otp-kv .otp-code {
  font-weight: 600; letter-spacing: 0.18em;
  color: var(--accent2);
  text-shadow: 0 0 12px color-mix(in srgb, var(--accent2) 35%, transparent);
}
.otp-ring {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  background: conic-gradient(var(--accent2) var(--p, 100%),
    color-mix(in srgb, var(--text-dim) 25%, transparent) 0);
  -webkit-mask: radial-gradient(closest-side, transparent 52%, #000 56%);
  mask: radial-gradient(closest-side, transparent 52%, #000 56%);
  transition: background 0.3s linear;
}
.otp-kv.low .otp-code { color: var(--bad); text-shadow: none; }
.otp-kv.low .otp-ring {
  background: conic-gradient(var(--bad) var(--p, 100%),
    color-mix(in srgb, var(--text-dim) 25%, transparent) 0);
}

/* passkey protection */
.vault-locked {
  grid-column: 1 / -1;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 56px 24px;
  text-align: center;
}
.vault-locked .icon.big { color: var(--accent); filter: drop-shadow(0 0 18px var(--glow)); }
.vault-locked h3 { letter-spacing: 0.14em; text-transform: uppercase; }
.vault-locked .linkish {
  background: none; border: none; padding: 0;
  font: inherit; cursor: pointer; color: var(--text-dim);
}
.vault-locked .linkish:hover { color: var(--accent); }
.vault-rec-row { display: flex; gap: 8px; width: min(440px, 100%); }

.vault-rec-code {
  margin: 14px 0;
  padding: 16px 18px;
  border: 1px dashed var(--accent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  font-size: 1.05rem; letter-spacing: 0.12em; text-align: center;
  word-break: break-all;
  user-select: all;
}
.vault-sec-list { margin: 10px 0 4px; padding-left: 20px; line-height: 1.55; }
.vault-sec-list li { margin-bottom: 8px; }

.pk-list { display: flex; flex-direction: column; }
.pk-row {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 4px;
  border-bottom: 1px solid var(--line);
}
.pk-row:last-child { border-bottom: none; }
.pk-row .icon { color: var(--accent); flex: none; }
.pk-row .pk-name { font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pk-row .btn-icon { width: 30px; height: 30px; flex: none; }

.pw-meter { height: 5px; border-radius: 999px; background: color-mix(in srgb, var(--text-dim) 18%, transparent); overflow: hidden; }
.pw-meter > i { display: block; height: 100%; border-radius: 999px; transition: width 0.2s ease, background 0.2s ease; }

/* ---------- modal ---------- */
#modal-root { position: fixed; inset: 0; z-index: 90; }
.modal-overlay {
  position: absolute; inset: 0;
  background: color-mix(in srgb, var(--bg0) 70%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 7vh 18px;
  overflow-y: auto;
}
.modal-panel {
  width: 100%; max-width: 540px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  animation: rise 0.18s ease;
}
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }
.modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
}
.modal-head h3 { font-size: 1rem; letter-spacing: 0.1em; text-transform: uppercase; }
.modal-body { padding: 18px 20px; display: flex; flex-direction: column; gap: 13px; }
.modal-body label { display: flex; flex-direction: column; gap: 5px; font-size: 0.86rem; color: var(--text-dim); }
.modal-body .field { width: 100%; }
.modal-foot {
  display: flex; justify-content: flex-end; gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
}

/* ---------- command palette ---------- */
.palette {
  position: fixed; inset: 0; z-index: 95;
  background: color-mix(in srgb, var(--bg0) 65%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 12vh;
}
/* display:flex above overrides the UA [hidden] rule, so honor the attribute explicitly */
.palette[hidden] { display: none; }
.palette-panel {
  width: min(620px, 92vw);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  overflow: hidden;
  animation: rise 0.16s ease;
}
.palette-head { display: flex; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--line); }
.palette-head .icon { color: var(--accent); }
.palette-head input {
  flex: 1; background: none; border: none; outline: none;
  color: var(--text); font-family: var(--font-body); font-size: 1.06rem;
}
.palette-head kbd {
  font-family: var(--font-mono); font-size: 0.7rem;
  border: 1px solid var(--line-strong); border-radius: 5px;
  padding: 2px 7px; color: var(--text-dim);
}
.palette-list { list-style: none; margin: 0; padding: 8px; max-height: 46vh; overflow-y: auto; }
.palette-item {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 13px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text);
}
.palette-item .icon { color: var(--text-dim); }
.palette-item .hint { margin-left: auto; font-family: var(--font-mono); font-size: 0.7rem; color: var(--text-dim); }
.palette-item.sel { background: color-mix(in srgb, var(--accent) 13%, transparent); color: var(--accent); }
.palette-item.sel .icon { color: var(--accent); }

/* ---------- toasts ---------- */
#toasts {
  position: fixed; right: 22px; bottom: 22px; z-index: 130; /* above the file-viewer overlay (120) */
  display: flex; flex-direction: column; gap: 10px;
  max-width: min(380px, calc(100vw - 44px));
}
.toast {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 11px;
  padding: 13px 16px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  font-size: 0.94rem;
  animation: toast-in 0.3s cubic-bezier(0.2, 0.9, 0.25, 1.1);
}
@keyframes toast-in { from { opacity: 0; transform: translateX(30px); } }
/* one materialisation sweep as the transmission arrives */
.toast::after {
  content: "";
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 25%, color-mix(in srgb, var(--text) 9%, transparent) 50%, transparent 75%);
  transform: translateX(-100%);
  animation: shimmer 0.7s ease 0.08s forwards;
}
.toast.good { border-left-color: var(--good); }
.toast.bad { border-left-color: var(--bad); }
.toast.warn { border-left-color: var(--warn); }
.toast .icon { flex: none; }
.toast.good .icon { color: var(--good); }
.toast.bad .icon { color: var(--bad); }
.toast.warn .icon { color: var(--warn); }
.toast.out { opacity: 0; transform: translateY(8px); transition: opacity 0.25s ease, transform 0.25s ease; }

/* ---------- skeletons (establishing-uplink loading state) ---------- */
.skeleton-block, .skeleton-card, .skeleton-chip {
  position: relative; overflow: hidden;
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  border-radius: var(--radius-sm);
}
.skeleton-block { height: 86px; }
.skeleton-card { min-height: 130px; border: 1px solid var(--hud); }
.skeleton-chip { color: transparent !important; border-color: transparent !important; min-width: 70px; }
/* a signal trace reaches out while the link comes up */
.skeleton-block::before, .skeleton-card::before {
  content: "";
  position: absolute; left: 12px; top: 12px; height: 2px; width: 34%;
  background: linear-gradient(90deg, var(--accent2), transparent);
  opacity: 0.55;
  transform-origin: left;
  animation: uplink-trace 1.5s ease-in-out infinite alternate;
}
@keyframes uplink-trace { from { transform: scaleX(0.2); } to { transform: scaleX(1); } }
.skeleton-block::after, .skeleton-card::after, .skeleton-chip::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 30%, color-mix(in srgb, var(--accent) 10%, transparent) 50%, transparent 70%);
  animation: shimmer 1.5s infinite;
  transform: translateX(-100%);
}
@keyframes shimmer { to { transform: translateX(100%); } }

/* ---------- theme settings page ---------- */
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr)); gap: 14px; }
.theme-card {
  display: flex; flex-direction: column; gap: 9px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font-family: var(--font-body);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.13s ease, transform 0.13s ease;
}
.theme-card:hover { transform: translateY(-2px); }
.theme-card.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 8px 24px var(--glow); }
.theme-card .swatches { display: flex; gap: 6px; }
.theme-card .swatches i { width: 26px; height: 26px; border-radius: 7px; border: 1px solid rgba(255, 255, 255, 0.12); }
.theme-card .t-name { font-weight: 700; letter-spacing: 0.06em; }
.test-result {
  margin-top: 14px; padding: 12px 15px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  font-family: var(--font-mono); font-size: 0.86rem;
}
.test-result.good { border-color: var(--good); color: var(--good); }
.test-result.bad { border-color: var(--bad); color: var(--bad); }

/* ---------- about deck ---------- */
.about-hero {
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  overflow: hidden;
}
.about-title {
  font-size: clamp(1.8rem, 4vw, 2.8rem); font-weight: 900; letter-spacing: 0.18em;
  background: linear-gradient(95deg, var(--text), var(--accent) 55%, var(--accent2));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: 0 0 34px var(--glow);
}
.about-pills { margin-top: 12px; gap: 8px; }
.about-hero .orb.big { width: 116px; height: 116px; flex: none; }
.about-grid { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.about-card .kv-grid { grid-template-columns: 1fr; gap: 8px; }
.about-card .kv { background: color-mix(in srgb, var(--bg0) 30%, transparent); }
.about-card .kv .v { font-family: var(--font-mono); font-size: 0.9rem; }
.about-card .kv .v.good { color: var(--good); }
.about-card .kv .v.bad { color: var(--bad); }

@media (max-width: 640px) {
  .about-hero .orb.big { width: 84px; height: 84px; }
}

/* ---------- error page ---------- */
.error-page {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: 24px;
}
.error-card { max-width: 480px; text-align: center; padding: 44px 38px; }
.error-card h1 {
  font-size: 2rem; letter-spacing: 0.22em; margin: 12px 0;
  color: var(--accent); text-shadow: 0 0 26px var(--glow);
}
.error-card p { color: var(--text-dim); margin: 0 0 22px; }

/* ---------- responsive ---------- */
@media (max-width: 1100px) {
  .pihole-grid .span2 { grid-column: span 1; }
  .notes-layout { grid-template-columns: 1fr; }
  .notes-list-pane { max-height: 330px; }
}

/* ============================== downloads deck ============================== */
.dl-compose { display: flex; gap: 12px; align-items: center; padding: 14px; margin-bottom: 16px; flex-wrap: wrap; }
.dl-compose input[type="url"] { flex: 1; min-width: 240px; }
.dl-audio { display: flex; gap: 6px; align-items: center; color: var(--text-dim); font-size: 0.86rem; white-space: nowrap; }
.dl-job { display: flex; flex-direction: column; gap: 6px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.dl-job:last-child { border-bottom: none; }
.dl-job-head { display: flex; align-items: center; gap: 10px; }
.dl-job-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dl-job-state { font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-dim); white-space: nowrap; }
.dl-job-state.done { color: var(--good); }
.dl-job-state.error { color: var(--bad); }
.dl-job-state.cancelled { color: var(--warn); }

/* Archive cookie-jar row: its own full-width line inside the compose card */
.ar-cookies-row { flex-basis: 100%; display: flex; align-items: center; gap: 10px; }
.ar-cookies-row .btn.small { padding: 5px 10px; font-size: 0.78rem; }

/* Archive pre-capture preview screenshot */
.ar-preview-img {
  width: 100%;
  max-height: 60vh;
  object-fit: contain;
  object-position: top;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
}

/* ============================== NASA decks ============================== */
/* Shared 4-up stat tiles (NEO + space weather heads) */
.stat-grid { grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); margin-bottom: 16px; }
.stat-big.warn { color: var(--warn); }
.stat-big.bad { color: var(--bad); }

/* APOD deck: main picture + last-7-days strip */
.apod-layout { display: grid; grid-template-columns: 1fr 190px; gap: 16px; align-items: start; }
.apod-main { padding: 16px; }
.apod-hero { width: 100%; border-radius: var(--radius-sm); border: 1px solid var(--line); display: block; }
.apod-video { aspect-ratio: 16 / 9; }
.apod-video iframe { width: 100%; height: 100%; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.apod-title { margin: 12px 0 6px; font-size: 1.15rem; }
.apod-expl { color: var(--text-dim); line-height: 1.55; margin: 0 0 12px; }
.apod-strip { display: flex; flex-direction: column; gap: 8px; }
.apod-thumb {
  position: relative;
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 0; overflow: hidden; cursor: pointer;
  aspect-ratio: 16 / 10;
}
.apod-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.apod-thumb.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.apod-thumb-date {
  position: absolute; right: 4px; bottom: 4px;
  background: rgba(0, 0, 0, 0.65); color: #fff;
  font-family: var(--font-mono); font-size: 0.62rem;
  padding: 1px 5px; border-radius: 3px;
}
@media (max-width: 900px) {
  .apod-layout { grid-template-columns: 1fr; }
  .apod-strip { flex-direction: row; overflow-x: auto; }
  .apod-thumb { flex: 0 0 130px; }
}

/* Space weather deck */
.sw-grid { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.sw-class { font-family: var(--font-mono); font-weight: 700; }
.sw-class.bad { color: var(--bad); }
.sw-class.warn { color: var(--warn); }
a.sw-row { text-decoration: none; color: inherit; }
a.sw-row:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }

/* APOD dashboard widget image */
.apod-img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  margin-bottom: 8px;
  display: block;
}

/* ============================== kanban deck ============================== */
/* Horizontal lane of columns; cards carry a colour edge via --kb-color and drag
   with a dashed placeholder marking the landing slot. */
.kb-board { display: flex; gap: 14px; align-items: flex-start; overflow-x: auto; padding-bottom: 12px; }
.kb-col {
  flex: 0 0 300px;
  display: flex; flex-direction: column; gap: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px;
  max-height: calc(100vh - 230px);
}
.kb-col-head { display: flex; align-items: center; gap: 8px; }
.kb-col-name {
  background: none; border: none; cursor: pointer;
  padding: 2px 4px;
  color: var(--text);
  font-family: var(--font-body); font-weight: 700; font-size: 0.82rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kb-col-name:hover { color: var(--accent); }
.kb-count { margin-left: auto; font-size: 0.72rem; color: var(--text-dim); }
.kb-col-del { opacity: 0; transition: opacity 0.15s ease; }
.kb-col:hover .kb-col-del { opacity: 1; }
.kb-cards { display: flex; flex-direction: column; gap: 8px; overflow-y: auto; min-height: 26px; }
.kb-card {
  background: var(--surface2);
  border: 1px solid var(--line);
  border-left: 3px solid var(--kb-color, var(--line-strong));
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  cursor: grab;
}
.kb-card:hover { border-color: var(--line-strong); border-left-color: var(--kb-color, var(--line-strong)); }
.kb-card:active { cursor: grabbing; }
.kb-card.dragging { opacity: 0.45; }
.kb-card-title { font-weight: 600; font-size: 0.92rem; }
.kb-card-notes { font-size: 0.8rem; margin-top: 3px; }
.kb-card-meta { font-size: 0.68rem; margin-top: 6px; }

/* ---- agile card face ---- */
.kb-col-role { font-size: 0.7rem; color: var(--text-dim); flex: none; }
.kb-pts { font-size: 0.68rem; color: var(--text-dim); padding: 1px 5px; border: 1px solid var(--line); border-radius: 999px; white-space: nowrap; flex: none; }
.kb-count { white-space: nowrap; flex: none; }
.kb-count-over { color: var(--bad); font-weight: 700; }
.kb-col.kb-over { border-color: color-mix(in srgb, var(--bad) 55%, var(--line)); }
.kb-col.kb-over .kb-col-head::after {
  content: 'WIP'; font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.1em;
  color: var(--bad); border: 1px solid var(--bad); border-radius: 3px; padding: 0 3px; margin-left: 4px;
}
.kb-card-strip { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.kb-strip-gap { flex: 1 1 auto; }
.kb-type { font-size: 0.82rem; line-height: 1; }
.kb-type-story { color: #34d399; }
.kb-type-bug { color: #f87171; }
.kb-type-task { color: #67e8f9; }
.kb-type-spike { color: #ffb347; }
.kb-type-chore { color: #a78bfa; }
.kb-epic {
  font-size: 0.62rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-dim);
  background: var(--surface); border: 1px solid var(--line); border-radius: 3px; padding: 0 4px;
  max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kb-prio { font-size: 0.82rem; line-height: 1; }
.kb-prio-low { color: var(--text-dim); }
.kb-prio-medium { color: #67e8f9; }
.kb-prio-high { color: #ffb347; }
.kb-prio-critical { color: #f87171; }
.kb-points {
  font-size: 0.68rem; font-weight: 700; min-width: 18px; text-align: center;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: 999px; padding: 0 5px;
}
.kb-card-blocked { border-left-color: var(--bad) !important; }
.kb-blocked {
  display: flex; align-items: center; gap: 4px; font-size: 0.66rem; letter-spacing: 0.04em;
  color: var(--bad); margin-top: 6px;
}
.kb-labels { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.kb-label {
  font-size: 0.64rem; color: var(--text-dim);
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px;
}
.kb-crit {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 6px;
  font-size: 0.68rem; color: var(--text-dim);
  background: none; border: 1px solid var(--line); border-radius: 4px; padding: 1px 6px; cursor: pointer;
}
.kb-crit:hover { border-color: var(--line-strong); color: var(--text); }
.kb-crit-done { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, var(--line)); }
.kb-crit-open { border-color: var(--accent); color: var(--text); }
.kb-crit-list { display: flex; flex-direction: column; gap: 3px; margin-top: 6px; }
.kb-crit-row { display: flex; align-items: flex-start; gap: 6px; font-size: 0.74rem; cursor: pointer; }
.kb-crit-row input { margin-top: 2px; }
.kb-crit-struck { text-decoration: line-through; color: var(--text-dim); }
.kb-card-foot { display: flex; align-items: center; gap: 6px; margin-top: 7px; font-size: 0.68rem; color: var(--text-dim); }
.kb-due { font-family: var(--font-mono); }
.kb-overdue { color: var(--bad); font-weight: 700; }
.kb-cycle { color: var(--text-dim); }
.kb-who {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%; font-size: 0.6rem; font-weight: 700;
  background: color-mix(in srgb, var(--accent) 22%, var(--surface)); color: var(--text);
  border: 1px solid var(--line-strong);
}

/* ---- ticket editor (wide modal) ---- */
.kb-modal-wide { max-width: 640px; width: min(640px, 94vw); }
.kb-form-wide { gap: 12px; }
.kb-span { grid-column: 1 / -1; }
.kb-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.kb-field { display: flex; flex-direction: column; gap: 3px; }
.kb-field-label { font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); }
.kb-block { display: flex; flex-direction: column; gap: 6px; }
/* Beat `.modal-body label { flex-direction: column }` (specificity 0,1,1): the blocked toggle is a
   ROW — checkbox beside its label — while the reason field sits full-width beneath it. */
.kb-block .kb-block-cb { flex-direction: row; align-items: center; gap: 6px; font-size: 0.85rem; color: var(--text); cursor: pointer; }
.kb-block .kb-block-cb input { flex: none; }
.kb-ac { border-top: 1px solid var(--line); padding-top: 10px; }
.kb-ac-head { font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 6px; }
.kb-ac-row { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
.kb-ac-text { flex: 1; }
.kb-ac-add { margin-top: 4px; }
@media (max-width: 560px) { .kb-grid { grid-template-columns: 1fr; } }
.kb-drop {
  flex: none; height: 38px;
  border: 1px dashed var(--accent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.kb-add {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  background: none; border: 1px dashed var(--line); border-radius: var(--radius-sm);
  padding: 8px;
  color: var(--text-dim);
  font-family: var(--font-body); font-weight: 600; font-size: 0.8rem;
  cursor: pointer;
}
.kb-add:hover { color: var(--text); border-color: var(--line-strong); }
.kb-form { display: flex; flex-direction: column; gap: 10px; }
@media (max-width: 640px) {
  .kb-col { flex-basis: 84vw; max-height: none; }
}

/* Small tablets: the dock collapses to an icon rail. */
@media (max-width: 900px) {
  html { --dock-w: 76px; }
  .dock-label, .dock-logo-text { display: none; }
  .dock-collapse { display: none; }
  .dock-group-title { height: 0; overflow: hidden; padding: 0; margin: 6px 10px; border-top: 1px solid var(--line); }
  .dock-group.closed .dock-link { display: flex; }
  .topbar { padding: 12px 16px; gap: 12px; }
  .topbar-clock { display: none; }
  .topbar-title h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .topbar-title .eyebrow { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .content { padding: 18px 16px 48px; }
  .hero { flex-direction: column-reverse; text-align: center; padding: 24px 20px; }
  .hero-pills { justify-content: center; }
  .orb.big { width: 130px; height: 130px; }
  .deck-head { flex-direction: column; align-items: stretch; }
  input[type="search"].field { min-width: 0; flex: 1; }
  /* library: the shelves sidebar becomes a navigator toggled from the deck actions */
  .lib-layout { grid-template-columns: 1fr; }
  .lib-shelves-toggle { display: inline-flex; }
  .lib-shelves-pane { display: none; max-height: 320px; }
  .lib-shelves-pane.open { display: flex; }
}

/* Phones: the dock becomes a fixed bottom tab bar and content goes full width.
   The html[data-dock="min"] selectors must be repeated here because a dock state
   stored on desktop otherwise outranks these rules on the phone. */
@media (max-width: 640px) {
  .dock,
  html[data-dock="min"] .dock {
    top: auto; bottom: 0; left: 0; right: 0;
    width: 100%; height: auto;
    flex-direction: row;
    align-items: stretch;
    gap: 0;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    border-right: none;
    border-top: 1px solid var(--line);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .dock::-webkit-scrollbar { display: none; }
  .dock-logo, .dock-foot { display: none; }
  /* editing is a desktop affordance — HTML5 DnD does not exist on touch (core.js also
     force-exits edit mode when this layout takes over) */
  #dock-edit, #dock-reset, #dock-newfolder, .dock-ctl, .dock-tools { display: none; }
  .dock-nav {
    flex-direction: row;
    align-items: stretch;
    justify-content: space-between;
    gap: 2px;
    flex: none;
    min-width: 100%;
    overflow: visible;
  }
  .dock-link,
  html[data-dock="min"] .dock-link {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 7px 11px;
    flex: none;
    font-size: 0.58rem;
    letter-spacing: 0.06em;
  }
  .dock-link.active { box-shadow: inset 0 3px 0 var(--accent); }
  .dock-link.active::before { display: none; }  /* the side live-edge belongs to the rail layout */
  #comms { display: none; }                     /* no room for the comms array on phones */
  .dock-label,
  html[data-dock="min"] .dock-label { display: block; }
  /* the bottom tab bar is one flat row — no room for section headings, and folded
     groups must stay reachable */
  .dock-group-title,
  html[data-dock="min"] .dock-group-title { display: none; }
  .dock-group.closed .dock-link,
  html[data-dock="min"] .dock-group.closed .dock-link { display: flex; }

  .main,
  html[data-dock="min"] .main { margin-left: 0; }
  .content { padding: 14px 14px calc(88px + env(safe-area-inset-bottom)); gap: 16px; }

  .topbar { padding: 10px 14px; gap: 10px; }
  .topbar-title h1 { font-size: 1.06rem; }

  /* iOS Safari zooms the page when a focused control is under 16px. */
  .field, select.field, textarea.field, .palette-head input { font-size: 16px; }

  /* keep floating chrome clear of the bottom bar */
  #toasts {
    left: 12px; right: 12px;
    bottom: calc(80px + env(safe-area-inset-bottom));
    max-width: none;
  }
  .modal-overlay { padding: 14px 10px calc(88px + env(safe-area-inset-bottom)); }
  .modal-foot { flex-wrap: wrap; }
  .palette { padding-top: 7vh; }

  .card { padding: 15px 14px; }
  .hero { padding: 20px 14px; }
  .table .actions .btn-icon { width: 36px; height: 36px; }
  .session-art { width: 58px; height: 86px; }
  .net-map { min-height: 280px; }
  .net-map .empty { min-height: 260px; }
  .notes-editor { min-height: 420px; }
  .field.area { min-height: 260px; }
  .editor-body.split { grid-template-columns: 1fr; }
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-delay: 0s !important; /* staggered `backwards` fills must not hold content invisible */
    animation-iteration-count: 1 !important;
    transition-duration: 0.001s !important;
  }
  #starfield { display: none !important; }
  .ticker-track { animation: none; }
}

/* ============================================================
   Network deck · LAN scanner + map
   ============================================================ */
.net-types { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.net-chip { cursor: pointer; }
.net-chip.active { border-color: var(--accent); color: var(--accent); }
.net-swatch { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: middle; }

.net-map-card { margin-bottom: 18px; }
.net-map { position: relative; min-height: 440px; }
.net-map .empty { min-height: 420px; }
.net-svg { width: 100%; height: auto; display: block; overflow: visible; }

.net-edge { stroke: var(--line-strong); stroke-width: 1; opacity: 0.5; }
.net-edge.off { stroke-dasharray: 3 5; opacity: 0.22; }

.net-node { cursor: pointer; }
.net-node:focus { outline: none; }
.net-node .net-ring { fill: none; stroke: var(--c); stroke-width: 1.5; opacity: 0; transition: opacity 0.15s ease; }
.net-node:hover .net-ring { opacity: 0.9; }
/* Keyboard focus gets a distinct, high-contrast cue (accent colour, thicker, dashed) — not the hover halo. */
.net-node:focus-visible .net-ring { opacity: 1; stroke: var(--accent); stroke-width: 3; stroke-dasharray: 5 3; }
.net-node .net-dot {
  fill: color-mix(in srgb, var(--c) 16%, var(--surface));
  stroke: var(--c); stroke-width: 1.6;
  transition: stroke-width 0.15s ease;
}
.net-node:hover .net-dot,
.net-node:focus-visible .net-dot { stroke-width: 2.6; }
.net-node use { color: var(--c); }
.net-node .net-label {
  fill: var(--text); text-anchor: middle;
  font-family: var(--font-mono); font-size: 12px;
}
.net-node.offline { opacity: 0.42; }
.net-node.gw .net-dot { stroke-width: 2.4; filter: drop-shadow(0 0 7px var(--c)); }
.net-node .net-badge { fill: var(--accent); stroke: var(--bg0); stroke-width: 1.5; }

.net-status { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: none; }
.net-status.ok { background: var(--good); box-shadow: 0 0 6px var(--good); }
.net-status.off { background: var(--text-dim); }

.net-table .icon.tiny { width: 13px; height: 13px; color: var(--accent); }
.pill.net-type, .pill.gw {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase;
  border: 1px solid var(--line-strong);
}
.pill.net-type { color: var(--c); border-color: color-mix(in srgb, var(--c) 45%, transparent); background: color-mix(in srgb, var(--c) 13%, transparent); }
.pill.gw { color: var(--accent2); border-color: color-mix(in srgb, var(--accent2) 45%, transparent); }

.net-edit-meta {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px;
  margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid var(--line);
}
.net-edit-meta .dim.small {
  display: block; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.66rem; margin-bottom: 2px;
}
.net-field { display: block; margin-bottom: 12px; }
.net-field > span { display: block; font-size: 0.8rem; color: var(--text-dim); margin-bottom: 5px; }
.net-field .field { width: 100%; }
.net-field textarea.field { resize: vertical; min-height: 64px; font-family: var(--font-body); }
select.field option { background: var(--bg1); color: var(--text); }

@media (max-width: 720px) {
  .net-edit-meta { grid-template-columns: 1fr; }
  .net-map { min-height: 320px; }
}

/* ============================================================
   Lights deck · Philips Hue + Nanoleaf control
   ============================================================ */
.icon.tiny { width: 13px; height: 13px; }
.btn-icon.tiny { width: 26px; height: 26px; }
.btn-icon.tiny .icon { width: 13px; height: 13px; }

.lights-bridges { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 4px; }
.chip.lights-bridge { gap: 8px; padding: 5px 8px 5px 12px; max-width: 100%; }
/* Bridge names are user-entered (up to 80 chars); shrink and ellipsize them rather
   than letting one wide chip overflow a phone screen. */
.lights-bridge-name {
  font-family: var(--font-body); font-weight: 600; letter-spacing: 0;
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.chip.lights-bridge .dim.small { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

.lights-grid { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); margin-top: 4px; }
.lights-empty { display: block; margin-top: 4px; }

.light-card { display: flex; flex-direction: column; gap: 16px; transition: opacity 0.15s ease; }
.light-card.is-off { opacity: 0.72; }
.light-card.is-unreachable { opacity: 0.55; }

.light-top { display: flex; align-items: center; gap: 13px; }
.light-swatch {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  background: var(--sw, #ffd9a0);
  border: 2px solid color-mix(in srgb, var(--text) 18%, transparent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--sw, #ffd9a0) 65%, transparent);
}
.light-card.is-off .light-swatch { box-shadow: none; filter: grayscale(0.5); }
.light-id { flex: 1; min-width: 0; }
.light-name { font-weight: 700; font-size: 1.05rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.light-meta { display: flex; align-items: center; gap: 5px; margin-top: 2px; }

.light-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: none;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--surface2);
  color: var(--text-dim);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.light-toggle:hover { color: var(--text); border-color: var(--line-strong); }
.light-toggle.on {
  color: var(--bg0);
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 60%, var(--accent2)));
  border-color: transparent;
  box-shadow: 0 0 16px var(--glow);
}

.light-controls { display: flex; flex-direction: column; gap: 11px; }
.light-row {
  display: grid; grid-template-columns: 78px 1fr auto; align-items: center; gap: 12px;
  cursor: default;
}
.light-row-label { font-size: 0.78rem; color: var(--text-dim); letter-spacing: 0.04em; }
.light-row-val { font-family: var(--font-mono); font-size: 0.82rem; min-width: 38px; text-align: right; }
.light-row input[type="range"] { width: auto; min-width: 0; }
.light-row input[type="color"] { justify-self: start; }
.light-row .field { width: 100%; }

/* warm/cool temperature slider gets a gradient track */
.warm-cool {
  -webkit-appearance: none; appearance: none;
  height: 8px; border-radius: 999px; outline: none; padding: 0;
  background: linear-gradient(90deg, #ffb05a 0%, #fff4e6 50%, #bcd4ff 100%);
}
.warm-cool::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--text); border: 2px solid var(--bg0);
  box-shadow: 0 0 6px var(--glow); cursor: pointer;
}
.warm-cool::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--text); border: 2px solid var(--bg0); cursor: pointer;
}
.warm-cool::-moz-range-track { background: transparent; }

/* pairing modal */
.lights-kind { margin-bottom: 12px; }
.lights-help { margin: 0 0 14px; line-height: 1.5; }
.lights-help a { font-weight: 600; }

@media (max-width: 720px) {
  .light-row { grid-template-columns: 70px 1fr auto; }
}

/* ============================================================
   Segmented control · shared toggle (e.g. Gitea's open/closed/all filter)
   ============================================================ */
.seg {
  padding: 8px 6px; text-align: center; cursor: pointer;
  font-family: var(--font-body); font-weight: 600; font-size: 0.82rem; letter-spacing: 0.03em;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg0) 35%, transparent); color: var(--text-dim);
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.seg:hover { color: var(--text); }
.seg.active { color: var(--bg0); border-color: transparent; }
.seg:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---------- files: 3D world + file viewer (see files3d.js / fileviewer.js) ---------- */
.icon.huge { width: 72px; height: 72px; color: var(--accent); }
/* ---------- files: 3D world ---------- */
.world-stage {
  position: relative; width: 100%;
  height: clamp(420px, 64vh, 820px);
  border-radius: var(--radius); overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 60%),
    linear-gradient(180deg, var(--bg1), var(--bg0));
}
.world-stage[hidden] { display: none; }
#world-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; cursor: crosshair; }
.world-hud { position: absolute; inset: 0; pointer-events: none; font-family: var(--font-body); }

.world-crosshair {
  position: absolute; top: 50%; left: 50%; width: 18px; height: 18px;
  margin: -9px 0 0 -9px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--accent) 75%, #fff);
  opacity: 0; transition: opacity 0.15s;
}
.world-crosshair::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 3px; height: 3px; margin: -1.5px 0 0 -1.5px;
  border-radius: 50%; background: #fff;
}
.world-hud.locked .world-crosshair { opacity: 0.95; }

.world-target {
  position: absolute; top: calc(50% + 20px); left: 50%; transform: translateX(-50%);
  max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: 8px;
  padding: 5px 12px; color: var(--text); font-size: 0.85rem; font-weight: 600;
  box-shadow: var(--shadow); opacity: 0; transition: opacity 0.12s;
}
.world-target.show { opacity: 1; }
.world-target .t-hint { margin-left: 10px; color: var(--accent); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; }

.world-prompt {
  position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%);
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px;
  max-width: calc(100% - 28px); text-align: center; white-space: normal;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: 18px;
  padding: 8px 16px; color: var(--text); font-size: 0.88rem; box-shadow: var(--shadow);
}
.world-prompt .icon { width: 16px; height: 16px; color: var(--accent); }
.world-hud.locked .world-prompt { display: none; }

.world-legend {
  position: absolute; right: 14px; bottom: 14px;
  display: grid; gap: 4px; padding: 8px 11px; border-radius: 10px;
  background: color-mix(in srgb, var(--bg0) 60%, transparent); border: 1px solid var(--line);
  font-family: var(--font-mono); font-size: 0.72rem; color: var(--text-dim);
  opacity: 0; transition: opacity 0.2s;
}
.world-hud.locked .world-legend { opacity: 1; }
.world-legend kbd {
  display: inline-block; min-width: 1.1em; text-align: center;
  padding: 1px 5px; margin: 0 1px; border-radius: 4px;
  background: var(--surface2); border: 1px solid var(--line-strong); color: var(--text);
}

.world-empty, .world-fallback {
  position: absolute; inset: 0; display: none;
  flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  text-align: center; color: var(--text-dim); padding: 24px;
}
.world-empty.show { display: flex; }
.world-fallback { display: flex; }
.world-fallback[hidden] { display: none; }
.world-empty .icon.big, .world-fallback .icon.big { color: var(--accent); }

/* ---------- file viewer overlay ---------- */
#viewer-root[hidden] { display: none; }
.viewer-overlay {
  position: fixed; inset: 0; z-index: 120;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(8px, 3vw, 40px);
  background: color-mix(in srgb, var(--bg0) 80%, rgba(0, 0, 0, 0.65));
  backdrop-filter: blur(8px);
  animation: viewer-fade 0.18s ease-out;
}
@keyframes viewer-fade { from { opacity: 0; } to { opacity: 1; } }
.viewer-panel {
  display: flex; flex-direction: column;
  width: min(1120px, 100%); height: min(920px, 100%);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}
.viewer-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 16px; border-bottom: 1px solid var(--line); background: var(--surface2);
}
.viewer-title { display: flex; align-items: center; gap: 12px; min-width: 0; }
.viewer-title > .icon { width: 22px; height: 22px; color: var(--accent); flex: none; }
.viewer-titletext { min-width: 0; }
.viewer-name { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.viewer-meta { color: var(--text-dim); }
.viewer-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.viewer-body { flex: 1; min-height: 0; overflow: auto; position: relative; background: var(--bg0); }

.viewer-loading {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px; color: var(--text-dim);
}
.viewer-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
}
.viewer-error {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; min-height: 100%; padding: 32px; text-align: center; color: var(--text);
}
.viewer-error .icon.big { color: var(--accent); }

.viewer-frame { width: 100%; height: 100%; border: 0; background: #fff; }
.viewer-epub { display: flex; flex-direction: column; height: 100%; background: #fbfaf7; }
.viewer-epub .epub-render { flex: 1; min-height: 0; }
.viewer-epub .epub-bar {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  padding: 8px 14px; border-top: 1px solid var(--line); background: var(--surface); flex-shrink: 0;
}
.viewer-epub .epub-loc { min-width: 46px; text-align: center; color: var(--text-dim); }
.viewer-image { min-height: 100%; display: flex; align-items: center; justify-content: center; padding: 16px; }
.viewer-image img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 6px; cursor: zoom-in; }
.viewer-image img.zoom { max-width: none; max-height: none; cursor: zoom-out; }

.viewer-media { min-height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; padding: 24px; }
.viewer-media .viewer-medianame { color: var(--text-dim); font-weight: 600; }
.viewer-audio audio { width: min(560px, 90%); }
.viewer-video video { max-width: 100%; max-height: 100%; border-radius: 8px; background: #000; }

.viewer-text {
  margin: 0; padding: 18px 20px; white-space: pre-wrap; word-break: break-word;
  font-family: var(--font-mono); font-size: 0.85rem; line-height: 1.55; color: var(--text);
}

.viewer-doc { padding: 20px; display: flex; justify-content: center; }
.viewer-doc .docx-wrapper { background: transparent; padding: 0; }
.viewer-doc .docx-wrapper > section.docx { box-shadow: var(--shadow); margin-bottom: 18px; }

.viewer-sheet { display: flex; flex-direction: column; min-height: 100%; }
.sheet-tabs {
  position: sticky; top: 0; z-index: 1; display: flex; gap: 4px;
  padding: 8px 10px; overflow-x: auto; border-bottom: 1px solid var(--line); background: var(--surface2);
}
.sheet-tab {
  background: none; border: 1px solid transparent; border-radius: 7px;
  padding: 5px 11px; color: var(--text-dim); font: inherit; font-size: 0.85rem; font-weight: 600;
  white-space: nowrap; cursor: pointer;
}
.sheet-tab:hover { color: var(--text); }
.sheet-tab.active { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); border-color: var(--line-strong); }
.sheet-host { overflow: auto; padding: 12px; }
.sheet-host table, .csv-table { border-collapse: collapse; font-size: 0.85rem; width: max-content; max-width: none; }
.sheet-host td, .sheet-host th, .csv-table td, .csv-table th {
  border: 1px solid var(--line); padding: 4px 9px; color: var(--text);
  white-space: nowrap; text-align: left; vertical-align: top;
}
.csv-table thead th, .sheet-host tr:first-child td {
  position: sticky; top: 0; background: var(--bg1); font-weight: 700;
}

.viewer-outline { padding: 22px; display: flex; flex-direction: column; gap: 14px; max-width: 920px; margin: 0 auto; }
.outline-note { color: var(--text-dim); font-size: 0.9rem; line-height: 1.5; }
.slide-card { border: 1px solid var(--line); border-radius: 12px; padding: 16px 18px; background: var(--surface2); }
.slide-no { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.1em; color: var(--accent); }
.slide-title { margin: 5px 0 8px; font-size: 1.08rem; }
.slide-body { margin: 0; padding-left: 20px; color: var(--text-dim); display: flex; flex-direction: column; gap: 4px; }

@media (max-width: 720px) {
  .viewer-panel { width: 100%; height: 100%; border-radius: 0; }
  .world-legend { display: none; }
}

/* ============================================================
   Shodan deck (see shodan.js)
   ============================================================ */
.shodan-bar { display: flex; gap: 10px; align-items: center; padding: 14px 16px; flex-wrap: wrap; }
.shodan-bar #shodan-mode { flex: 0 0 auto; }
.shodan-bar #shodan-q { flex: 1 1 260px; min-width: 0; }
.shodan-bar .btn { flex: 0 0 auto; }
.shodan-hint { margin: 10px 2px 18px; }

#shodan-credits { gap: 8px; flex-wrap: wrap; }

.shodan-setup .shodan-steps { margin: 12px 0 0; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; color: var(--text-dim); }
.shodan-setup a { color: var(--accent); }

.shodan-host-head { display: flex; flex-direction: column; gap: 14px; }
.shodan-host-ip { display: flex; align-items: center; gap: 10px; font-size: 1.4rem; font-weight: 700; }
.shodan-host-ip .icon { color: var(--accent); width: 22px; height: 22px; }
.shodan-tags, .shodan-vulns { display: flex; gap: 7px; flex-wrap: wrap; }
.shodan-row { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; }
.shodan-lbl {
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--text-dim); margin-right: 4px;
}

.shodan-svc, .shodan-match { display: flex; flex-direction: column; gap: 10px; }
.shodan-svc-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.shodan-prod { color: var(--text); font-size: 0.9rem; }
.shodan-title { font-weight: 600; color: var(--text); }
.shodan-ip {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--accent); font: inherit; font-weight: 700; font-family: var(--font-mono);
}
.shodan-ip:hover { text-decoration: underline; }

.shodan-banner {
  margin: 0; padding: 12px 14px; max-height: 320px; overflow: auto;
  white-space: pre-wrap; word-break: break-word;
  background: color-mix(in srgb, var(--bg0) 55%, transparent);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: 0.78rem; line-height: 1.5; color: var(--text-dim);
}

.shodan-summary { margin: 4px 2px 14px; color: var(--text-dim); }
.shodan-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin: 18px 0; }

/* ---------- torrents deck ---------- */
.chip.info { border-color: color-mix(in srgb, var(--accent) 55%, transparent); color: var(--accent); }

.torrent-add { display: flex; flex-direction: column; gap: 10px; }
.torrent-add-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.torrent-add-row #torrent-source { flex: 1 1 320px; }
.torrent-add-row #torrent-folder { flex: 0 1 200px; }
.torrent-add-row.sub { gap: 8px; }
.torrent-engine { margin-left: auto; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

.torrent-list { display: flex; flex-direction: column; gap: 12px; }
.torrent-card { display: flex; flex-direction: column; gap: 8px; }
.torrent-head { display: flex; align-items: center; gap: 10px; }
.torrent-name {
  flex: 1; min-width: 0;
  font-weight: 600; font-size: 0.98rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.torrent-error { align-self: flex-start; }
.torrent-card .meter.done > i { background: linear-gradient(90deg, var(--good), var(--good)); }
.torrent-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.torrent-stats .sep { opacity: 0.4; }
.torrent-path {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0.6;
}
.torrent-actions { display: flex; gap: 6px; justify-content: flex-end; }

.torrent-files { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; max-height: 50vh; overflow: auto; }
.torrent-files li { display: flex; gap: 12px; align-items: baseline; padding: 4px 0; border-bottom: 1px solid var(--line); }
.torrent-files .f-path { flex: 1; min-width: 0; word-break: break-all; }
.torrent-files .f-size { white-space: nowrap; }
.torrent-del-opt { display: flex; align-items: center; gap: 8px; font-size: 0.88rem; color: var(--text-dim); }

.torrent-set-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
.torrent-set-chk { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; font-size: 0.9rem; color: var(--text); }
.torrent-set-root { margin: 6px 0 0; line-height: 1.5; }

@media (max-width: 720px) {
  .torrent-add-row #torrent-folder { flex: 1 1 100%; }
  .torrent-engine { margin-left: 0; }
  .torrent-set-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   Remote Sessions deck (see remote.js)
   ============================================================ */
.remote-group { margin: 18px 2px 10px; }
.remote-group:first-child { margin-top: 6px; }
.remote-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
}
.remote-card { display: flex; flex-direction: column; gap: 10px; }
.remote-card-head { display: flex; align-items: center; gap: 12px; }
.remote-card-icon { width: 24px; height: 24px; color: var(--accent); flex: 0 0 auto; }
.remote-name { flex: 1; min-width: 0; }
.remote-label {
  font-weight: 700; font-size: 1.02rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.remote-target { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.remote-notes { margin: 0; }
.remote-badge { display: inline-flex; align-items: center; flex: 0 0 auto; }
.remote-badge .led { width: 9px; height: 9px; }
.remote-card-foot { display: flex; align-items: center; gap: 10px; margin-top: auto; }
.remote-card-actions { margin-left: auto; display: flex; align-items: center; gap: 6px; }

.remote-empty { text-align: center; padding: 28px 16px; }

/* add/edit form (inside the shared modal) */
.remote-form { display: flex; flex-direction: column; gap: 12px; }
.rs-row { display: grid; grid-template-columns: 110px 1fr; gap: 12px; align-items: center; }
.rs-lbl { color: var(--text-dim); font-size: 0.86rem; }
.rs-ctl { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rs-ctl .field { flex: 1 1 auto; min-width: 0; }
.rs-ctl textarea.field { width: 100%; }
.rs-check { display: inline-flex; align-items: center; gap: 6px; color: var(--text); font-size: 0.9rem; white-space: nowrap; }
.rs-check input { width: auto; }
.rs-size { display: inline-flex; align-items: center; gap: 6px; }
.rs-size .field { width: 84px; flex: 0 0 auto; }

/* per-kind field visibility, driven by the modal panel's data-kind */
.modal-panel[data-kind="rdp"] .rs-rdweb { display: none; }
.modal-panel[data-kind="rdweb"] .rs-rdp { display: none; }

@media (max-width: 560px) {
  .rs-row { grid-template-columns: 1fr; gap: 6px; }
}

/* ---------- Gitea deck ---------- */
.btn-sm { padding: 5px 10px; font-size: 0.82rem; }
.icon.i-sm { width: 14px; height: 14px; vertical-align: -2px; }
#gitea-notifs.has-unread { border-color: color-mix(in srgb, var(--accent) 55%, transparent); color: var(--accent); }

.g-avatar { width: 20px; height: 20px; border-radius: 50%; vertical-align: -5px; object-fit: cover; background: var(--surface2); }
.g-ext { margin-left: 6px; color: var(--text-dim); }
.g-ext:hover { color: var(--accent); }

.g-bar { display: flex; gap: 10px; align-items: center; padding: 14px 16px; }
.g-bar #g-search { flex: 1; min-width: 0; }

.g-setup .g-steps { margin: 12px 0 0; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; color: var(--text-dim); }
.g-pre, .g-code { margin: 8px 0 0; padding: 12px 14px; background: color-mix(in srgb, var(--bg0) 55%, transparent);
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: 0.78rem;
  line-height: 1.5; color: var(--text-dim); white-space: pre; overflow: auto; }
.g-code { max-height: 65vh; }

/* repo grid */
.g-repo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
.g-repo-card { cursor: pointer; display: flex; flex-direction: column; gap: 7px; transition: border-color .15s, transform .15s; }
.g-repo-card:hover { border-color: color-mix(in srgb, var(--accent) 50%, transparent); transform: translateY(-2px); }
.g-repo-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.g-repo-name { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.g-repo-desc { margin: 0; }
.g-repo-meta { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.g-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); display: inline-block; }

.g-pager, .g-pager.g-pager { display: flex; gap: 14px; align-items: center; justify-content: center; margin: 16px 0 4px; }

/* repo detail */
.g-repo-head { display: flex; flex-direction: column; gap: 10px; }
.g-repo-headtop { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.g-title { margin: 0; font-size: 1.15rem; display: flex; align-items: center; gap: 8px; }
.g-head-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.g-stats { display: flex; flex-wrap: wrap; gap: 16px; }
.g-stat { display: flex; align-items: center; gap: 5px; }

.g-tabs { display: flex; gap: 4px; margin: 14px 0; border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.g-tab { background: none; border: none; border-bottom: 2px solid transparent; color: var(--text-dim);
  padding: 8px 12px; cursor: pointer; font: inherit; font-size: 0.9rem; }
.g-tab:hover { color: var(--text); }
.g-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

/* language bar */
.g-langbar { display: flex; height: 8px; border-radius: 999px; overflow: hidden; margin: 0 0 8px; }
.g-langseg { height: 100%; }
.g-langkeys { display: flex; gap: 14px; flex-wrap: wrap; }
.g-lk { display: inline-flex; align-items: center; gap: 5px; }
.g-langdot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.g-lc0 { background: #4ade80; } .g-lc1 { background: #60a5fa; } .g-lc2 { background: #f59e0b; }
.g-lc3 { background: #f472b6; } .g-lc4 { background: #a78bfa; } .g-lc5 { background: #2dd4bf; }

.g-ov-grid { display: grid; grid-template-columns: 1fr 1.4fr; gap: 22px; margin-top: 12px; }
.g-branch { padding: 4px 0; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.g-commit { display: flex; align-items: center; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--line); }
.g-commit-main { flex: 1; min-width: 0; }
.g-commit-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* code browser */
.g-code-bar { display: flex; gap: 12px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.g-code-bar #g-ref { flex: 0 0 auto; max-width: 220px; }
.g-crumbs { font-family: var(--font-mono); font-size: 0.85rem; }
.g-crumb { cursor: pointer; color: var(--accent); }
.g-files { list-style: none; margin: 0; padding: 0; }
.g-file { display: flex; align-items: center; gap: 8px; padding: 7px 6px; border-bottom: 1px solid var(--line); cursor: pointer; border-radius: 6px; }
.g-file:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.g-fname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.g-fsize { white-space: nowrap; }
.g-file-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }

/* issues / pulls list */
.g-listbar { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.g-listbar .btn { margin-left: auto; }
.g-ish { display: flex; align-items: center; gap: 10px; padding: 10px 6px; border-bottom: 1px solid var(--line); cursor: pointer; border-radius: 6px; }
.g-ish:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.g-ish-main { flex: 1; min-width: 0; }
.g-ish-title { font-weight: 500; }
.g-labels { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 5px; }
.g-label { font-size: 0.68rem; }

/* thread (issue / PR detail) */
.g-thread { display: flex; flex-direction: column; gap: 12px; }
.g-thread-title { margin: 6px 0 0; font-size: 1.1rem; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.g-pr-refs { display: flex; gap: 6px; flex-wrap: wrap; }
.g-comment-box { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 10px 12px; }
.g-comment-box.g-body { border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.g-comment-hd { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.g-comment-hd .dim { margin-left: auto; }
.g-md { white-space: pre-wrap; word-break: break-word; line-height: 1.5; }
.g-reply { display: flex; flex-direction: column; gap: 8px; }
.g-reply-actions, .g-merge-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.g-reply-actions { justify-content: flex-end; }
.g-merge-row #g-merge-style { max-width: 220px; }

/* releases */
.g-rel { border-bottom: 1px solid var(--line); padding: 12px 0; }
.g-rel-hd { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.g-assets { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }

/* modals + misc */
.g-fld { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; font-size: 0.85rem; color: var(--text-dim); }
.g-check { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 0.88rem; color: var(--text-dim); }
.g-notifs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; max-height: 60vh; overflow: auto; }
.g-notifs li { padding-bottom: 8px; border-bottom: 1px solid var(--line); }

@media (max-width: 760px) {
  .g-ov-grid { grid-template-columns: 1fr; gap: 14px; }
  .g-head-actions { margin-left: 0; }
}

/* ---------- Cosmos deck (MAST astronomy data) ---------- */
.cosmos-bar { display: flex; gap: 10px; align-items: center; padding: 14px 16px; flex-wrap: wrap; }
.cosmos-bar #cosmos-target { flex: 1 1 280px; min-width: 0; }
.cosmos-bar #cosmos-mission { flex: 0 1 170px; }
.cosmos-bar #cosmos-radius { flex: 0 0 90px; }

.cosmos-featured { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 2px 16px; }
.cosmos-chip { cursor: pointer; }
.cosmos-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, transparent); color: var(--accent); }

.cosmos-resolved { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.cosmos-resolved > .icon { color: var(--accent); width: 22px; height: 22px; flex: 0 0 auto; }
.cosmos-res-main { flex: 1; min-width: 0; }
.cosmos-res-name { font-size: 1.1rem; font-weight: 700; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.cosmos-summary { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 2px 12px; color: var(--text-dim); }
.cosmos-toggle { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; cursor: pointer; }

.cosmos-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 14px; }
.cosmos-grid.previews-only .cosmos-card.no-preview { display: none; }
.cosmos-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.cosmos-thumb {
  position: relative; display: block; width: 100%; aspect-ratio: 1 / 1; border: none; padding: 0; margin: 0;
  background: radial-gradient(circle at 50% 40%, #1b2536, #070b14); cursor: pointer; overflow: hidden;
}
.cosmos-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .25s; }
.cosmos-thumb:hover img { transform: scale(1.05); }
.cosmos-thumb.failed img { display: none; }
.cosmos-thumb.failed::after, .cosmos-thumb.noimg::after { content: ''; }
.cosmos-thumb.noimg { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; cursor: default; color: var(--text-dim); }
.cosmos-thumb.noimg .icon.big { opacity: 0.4; }

.cosmos-body { padding: 11px 13px 13px; display: flex; flex-direction: column; gap: 6px; }
.cosmos-card-head { display: flex; gap: 6px; flex-wrap: wrap; }
.cosmos-mission { font-weight: 600; }
.cosmos-target { font-weight: 600; font-size: 0.95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cosmos-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.cosmos-meta .sep { opacity: 0.4; }

.cosmos-pager { display: flex; gap: 14px; align-items: center; justify-content: center; margin: 18px 0 4px; }
.cosmos-full { background: #05080f; border-radius: var(--radius-sm); display: flex; justify-content: center; max-height: 72vh; overflow: auto; }
.cosmos-full img { max-width: 100%; height: auto; display: block; }

.cosmos-files-row { display: flex; align-items: center; gap: 8px; margin-top: 2px; }

/* data products modal */
.cosmos-prod-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.cosmos-prod-head .cosmos-toggle { margin-left: auto; }
.cosmos-prods { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; max-height: 56vh; overflow: auto; }
.cosmos-prod { display: flex; align-items: center; gap: 12px; padding: 8px 6px; border-bottom: 1px solid var(--line); }
.cosmos-prod-main { flex: 1; min-width: 0; }
.cosmos-prod-name { word-break: break-all; }
.cosmos-prod.is-fits .cosmos-prod-name { color: var(--accent); }
.cosmos-prod-size { white-space: nowrap; flex: 0 0 auto; }
.cosmos-prods.fits-only .cosmos-prod:not(.is-fits) { display: none; }

@media (max-width: 560px) {
  .cosmos-bar #cosmos-mission, .cosmos-bar #cosmos-radius { flex: 1 1 100%; }
}

/* ---------- NED deck (extragalactic object search) ---------- */
.ned-list { display: flex; flex-direction: column; gap: 10px; }
.ned-row { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.ned-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.ned-row-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ned-type { flex: none; }
.ned-name { font-weight: 600; font-size: 0.98rem; overflow: hidden; text-overflow: ellipsis; }
.ned-sep { margin-left: auto; flex: none; }
.ned-facts { display: flex; flex-wrap: wrap; gap: 6px 14px; color: var(--text-dim); }
.ned-fact { white-space: nowrap; }
.ned-avail { display: flex; flex-wrap: wrap; gap: 6px; }
.ned-badge {
  font-size: 0.66rem; letter-spacing: 0.03em; color: var(--text-dim);
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px;
}
.ned-badge b { color: var(--text); font-weight: 700; }
.ned-row-actions { display: flex; align-items: center; gap: 6px; flex: none; }

/* object overview modal */
.ned-detail { width: 100%; border-collapse: collapse; }
.ned-detail th, .ned-detail td { text-align: left; vertical-align: top; padding: 6px 8px 6px 0; border-bottom: 1px solid var(--line); }
.ned-detail th {
  width: 34%; font-weight: normal; font-family: var(--font-mono);
  font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim);
}
.ned-cross { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.ned-cross .dim { flex-basis: 100%; }

@media (max-width: 560px) {
  .ned-row { flex-wrap: wrap; }
  .ned-row-actions { width: 100%; justify-content: flex-end; }
}

/* ---- exoplanets deck (shares the cosmos search bar and .ned-detail table) ---- */
.exo-stats { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.exo-stat { display: flex; align-items: center; gap: 14px; padding: 14px 18px; flex: 1 1 240px; }
.exo-stat .icon.big { color: var(--accent); flex: none; }
.exo-stat-n { font-size: 1.5rem; font-weight: 700; line-height: 1.1; }
.exo-list { display: flex; flex-direction: column; gap: 10px; }
.exo-row { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.exo-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.exo-row-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.exo-status { flex: none; }
.exo-name { font-weight: 600; font-size: 0.98rem; overflow: hidden; text-overflow: ellipsis; }
.exo-host { margin-left: auto; flex: none; }
.exo-size { letter-spacing: 0.04em; }
.exo-facts { display: flex; flex-wrap: wrap; gap: 6px 14px; color: var(--text-dim); }
.exo-fact { white-space: nowrap; }
.exo-row-actions { display: flex; align-items: center; gap: 6px; flex: none; }
.exo-hostblock { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
.exo-export { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 16px; margin-bottom: 14px; }
.exo-export-label { text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.7rem; }
.exo-fullrecord { margin-top: 14px; }
.exo-fullrecord-group + .exo-fullrecord-group { margin-top: 14px; }
.exo-fullrecord-title { text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px; }
.exo-fullrecord-count { margin-top: 10px; }

@media (max-width: 560px) {
  .exo-row { flex-wrap: wrap; }
  .exo-row-actions { width: 100%; justify-content: flex-end; }
  .exo-host { margin-left: 0; }
}

/* ---------- Directory (Active Directory) deck ---------- */
.dir-login {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 12px; padding: 34px 22px; max-width: 480px; margin: 8px auto;
}
.dir-login .icon.big { color: var(--accent); filter: drop-shadow(0 0 18px var(--glow)); }
.dir-login h3 { letter-spacing: 0.1em; text-transform: uppercase; margin: 0; }
.dir-login p.dim { margin: 0; max-width: 40ch; }
.dir-login form { display: flex; flex-direction: column; gap: 10px; width: min(360px, 100%); text-align: left; }
.dir-login label { display: flex; flex-direction: column; gap: 4px; font-size: 0.86rem; color: var(--text-dim); }
.dir-login .field { width: 100%; }
.dir-login .btn.primary { margin-top: 4px; justify-content: center; }
.dir-login-error { color: var(--bad); margin: 0; }

.dir-toolbar { margin: 14px 0; }
#dir-console .tabs { margin-bottom: 14px; }

.dir-error {
  display: flex; align-items: center; gap: 10px; margin-bottom: 14px; padding: 10px 12px;
  border-radius: 10px; color: var(--bad);
  border: 1px solid color-mix(in srgb, var(--bad) 55%, transparent);
  background: color-mix(in srgb, var(--bad) 12%, transparent);
}
.dir-error .icon { flex: none; }
.dir-error span { flex: 1; min-width: 0; word-break: break-word; }
.dir-error .btn-icon { flex: none; color: var(--bad); }

.dir-pager { display: flex; justify-content: center; align-items: center; gap: 10px; padding: 12px 0 4px; flex-wrap: wrap; }
/* In-flight refresh: keep the rows on screen, dimmed and inert, instead of flashing a skeleton. */
#dir-table, #dir-pager { transition: opacity 0.15s ease; }
#dir-table.refreshing, #dir-pager.refreshing { opacity: 0.55; pointer-events: none; }

.dir-fld { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; font-size: 0.86rem; color: var(--text-dim); }
.dir-fld .field, .dir-fld select.field, .dir-fld textarea.field { width: 100%; }

/* lazily-expanding OU tree, reused by the OUs tab, the create pickers and the move dialog */
.ou-tree { max-height: 340px; overflow: auto; padding: 4px; }
.ou-node-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 8px; cursor: pointer;
}
.ou-node-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.ou-node-row.selected { background: color-mix(in srgb, var(--accent) 16%, transparent); border: 1px solid var(--accent); }
.ou-node-row .icon { width: 16px; height: 16px; color: var(--accent); flex: none; }
.ou-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ou-toggle { background: none; border: 0; padding: 0; color: var(--text-dim); cursor: pointer; display: inline-flex; }
.ou-toggle .icon { width: 14px; height: 14px; transition: transform 0.15s ease; }
.ou-node.open > .ou-node-row .ou-toggle .icon { transform: rotate(-90deg); }
.ou-toggle-spacer { display: inline-block; width: 14px; }
.ou-kids { margin-left: 16px; border-left: 1px dashed var(--line); padding-left: 8px; }

/* ---------- People deck (dossiers) ---------- */
.people-toolbar { margin-bottom: 12px; }
.people-tags { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.people-tags .chip, .person-tags .chip { cursor: pointer; }
/* drop-shadow, not box-shadow: the chip chamfer clip-path clips box-shadow away */
.people-tags .chip.active { border-color: var(--accent); color: var(--accent); filter: drop-shadow(0 0 9px var(--glow)); }

/* holo keyframes shared by the grid cards, orbs and links map */
@keyframes holo-in {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
}
@keyframes holo-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}
@keyframes holo-pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 0.85; }
}
@keyframes holo-rotate { to { transform: rotate(360deg); } }
@keyframes net-flow { to { stroke-dashoffset: -12; } }
@keyframes holo-scan {
  from { background-position: 200% 0; }
  to { background-position: -100% 0; }
}
@keyframes pm-pulse {
  from { transform: scale(1); opacity: 0.8; }
  to { transform: scale(1.55); opacity: 0; }
}

/* the stage layers animated connection lines (#people-net) behind the card grid */
.people-stage { position: relative; }
.people-net {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 0; overflow: visible;
}
.net-edge {
  fill: none;
  stroke: color-mix(in srgb, var(--accent2) 45%, transparent);
  stroke-width: 1.2;
  stroke-dasharray: 6 6;
  animation: net-flow 1.4s linear infinite;
}
.net-dot { fill: var(--accent2); opacity: 0.9; filter: drop-shadow(0 0 5px var(--accent2)); }

.people-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.person-card {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  font: inherit; color: inherit; text-align: center; cursor: pointer;
  position: relative; z-index: 1; /* above the .people-net lines */
  overflow: hidden;
  border-color: color-mix(in srgb, var(--accent2) 22%, var(--line));
  /* faint hologram scanlines over the card surface */
  background:
    repeating-linear-gradient(180deg, transparent 0 5px, color-mix(in srgb, var(--accent2) 3%, transparent) 5px 6px),
    var(--surface);
  transition: border-color .15s, transform .15s, box-shadow .15s;
  animation: holo-in 0.4s ease backwards;
  animation-delay: calc(var(--i, 0) * 30ms);
}
.person-card:hover {
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  transform: translateY(-2px);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--accent) 18%, transparent);
}
/* light sweep across the card on hover */
.person-card::after {
  content: ""; position: absolute; top: -60%; bottom: -60%; left: -30%; width: 40%;
  background: linear-gradient(105deg, transparent, color-mix(in srgb, var(--accent2) 14%, transparent), transparent);
  transform: translateX(-140%) rotate(8deg);
  transition: transform 0.55s ease;
  pointer-events: none;
}
.person-card:hover::after { transform: translateX(340%) rotate(8deg); }
/* the avatar orb: levitating, with a rotating dashed halo and a breathing glow;
   phases keyed off --i so the grid doesn't bob in unison */
.person-orb {
  position: relative; display: inline-flex; flex: none; padding: 6px;
  animation: holo-float 6s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -0.7s);
}
.person-orb::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 1px dashed color-mix(in srgb, var(--accent2) 55%, transparent);
  animation: holo-rotate 14s linear infinite;
}
.person-orb::after {
  content: ""; position: absolute; inset: 4px; border-radius: 50%;
  box-shadow: 0 0 18px var(--glow);
  animation: holo-pulse 3.2s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -0.5s);
  pointer-events: none;
}
.person-avatar {
  width: 72px; height: 72px; border-radius: 50%; overflow: hidden; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  font-family: var(--font-display); font-weight: 700; font-size: 1.3rem; color: var(--accent);
}
.person-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person-avatar.place .icon { width: 30px; height: 30px; color: var(--accent); }
.person-name { font-weight: 700; font-size: 1rem; }
.person-aka { font-size: 0.85rem; }
.person-tags { display: flex; gap: 5px; flex-wrap: wrap; justify-content: center; }
.person-tags .chip { font-size: 0.68rem; padding: 2px 8px; cursor: inherit; }
.person-meta { margin-top: auto; }

/* dossier overlay (below the shared modal at 90, so confirms stack on top) */
.dossier-overlay {
  position: fixed; inset: 0; z-index: 80;
  background: color-mix(in srgb, var(--bg0) 70%, transparent);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 5vh 18px; overflow-y: auto;
}
.dossier-panel { width: min(980px, 100%); max-height: 90vh; display: flex; flex-direction: column; animation: rise 0.18s ease; }
/* HUD corner brackets on the panel frame */
.dossier-panel::after {
  content: ""; position: absolute; inset: 7px; pointer-events: none;
  border-radius: calc(var(--radius) - 5px);
  background:
    linear-gradient(var(--accent2), var(--accent2)) left top / 16px 1px,
    linear-gradient(var(--accent2), var(--accent2)) left top / 1px 16px,
    linear-gradient(var(--accent2), var(--accent2)) right top / 16px 1px,
    linear-gradient(var(--accent2), var(--accent2)) right top / 1px 16px,
    linear-gradient(var(--accent2), var(--accent2)) left bottom / 16px 1px,
    linear-gradient(var(--accent2), var(--accent2)) left bottom / 1px 16px,
    linear-gradient(var(--accent2), var(--accent2)) right bottom / 16px 1px,
    linear-gradient(var(--accent2), var(--accent2)) right bottom / 1px 16px;
  background-repeat: no-repeat;
  opacity: 0.55;
}
.dossier-head { display: flex; gap: 16px; align-items: flex-start; padding-bottom: 14px; border-bottom: 1px solid var(--line); position: relative; }
/* scanner shimmer travelling along the header rule */
.dossier-head::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent2) 35%, var(--accent) 65%, transparent);
  background-size: 220% 100%;
  animation: holo-scan 5s linear infinite;
}
.dossier-photo {
  width: 96px; height: 96px; border-radius: 50%; overflow: hidden; flex: none; position: relative;
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--line-strong)); cursor: pointer; padding: 0;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  font-family: var(--font-display); font-weight: 700; font-size: 1.7rem; color: var(--accent);
  box-shadow: 0 0 22px var(--glow);
}
.dossier-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dossier-photo:hover { border-color: var(--accent); }
.dossier-photo-hint { color: var(--text-dim); font-family: var(--font-body); font-weight: 400; }
.dossier-title { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.dossier-title .field.title { font-size: 1.15rem; font-weight: 700; }
.dossier-actions { display: flex; gap: 8px; align-items: center; flex: none; }

.dossier-body { display: flex; gap: 22px; overflow-y: auto; padding: 14px 2px; flex: 1; }
.dossier-col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.dossier-col.side { flex: 0 0 300px; }
.dossier-col.main { flex: 1; }
.dossier-col h4 {
  margin: 8px 0 0; font-size: 0.8rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-dim); display: flex; align-items: center; gap: 10px;
}
.dossier-col h4 .btn { margin-left: auto; }

.dossier-fields { display: flex; flex-direction: column; gap: 6px; }
.d-field { display: flex; gap: 6px; align-items: center; }
.d-field .d-field-label { flex: 0 0 96px; min-width: 0; font-size: 0.82rem; }
.d-field .d-field-name {
  flex: 0 0 96px; min-width: 0; font-size: 0.82rem; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.d-field .d-field-value { flex: 1; min-width: 0; }
.d-field .btn-icon { flex: none; }
.d-field .d-field-area { resize: vertical; min-height: 40px; font-family: var(--font-body); line-height: 1.4; }

/* location block (places): coordinates + what3words + embedded OSM preview */
#dossier-location { display: flex; flex-direction: column; gap: 10px; }
.dossier-geo .field.grow { min-width: 0; }
.dossier-map { border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; }
.dossier-map-frame { width: 100%; height: 190px; border: 0; display: block; }
.dossier-map-links { gap: 12px; }
.dossier-map-link { display: inline-flex; align-items: center; gap: 5px; color: var(--text-dim); }
.dossier-map-link:hover { color: var(--accent); }
.dossier-map-link .icon { width: 13px; height: 13px; }

.dossier-links { display: flex; flex-direction: column; gap: 6px; }
.d-link { display: flex; align-items: center; gap: 8px; }
.d-link-name { background: none; border: 0; padding: 0; color: var(--text); cursor: pointer; font: inherit; }
.d-link-name:hover { color: var(--accent); }
.d-link .btn-icon { margin-left: auto; }
/* multi-select link picker: tick any number of dossiers, then Link */
.dossier-link-people {
  display: flex; flex-direction: column; gap: 7px;
  max-height: 170px; overflow-y: auto;
  padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.d-link-pick { font-size: 0.92rem; }
.d-link-pick .icon { width: 14px; height: 14px; color: var(--accent); }
.d-link-pick span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dossier-link-new .field { min-width: 0; }

.dossier-notes-body { display: flex; gap: 12px; min-height: 180px; }
.dossier-notes-body .field.area { flex: 1; min-height: 180px; resize: vertical; }
.dossier-notes-body .note-preview { flex: 1; overflow: auto; }
.dossier-notes-body:not(.split) .note-preview { display: none; }

.dossier-files { display: flex; flex-direction: column; gap: 6px; }
.d-file { display: flex; align-items: center; gap: 10px; }
.d-file-thumb {
  width: 38px; height: 48px; object-fit: cover; flex: none; border-radius: 6px;
  border: 1px solid var(--line); background: color-mix(in srgb, var(--bg0) 55%, transparent);
}
.d-file-icon { display: inline-flex; align-items: center; justify-content: center; color: var(--text-dim); }
.d-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.d-file-name:hover { color: var(--accent); }
.dossier-foot { border-top: 1px solid var(--line); padding-top: 10px; min-height: 28px; }

/* floating links-map panel: dragged by its header, resizable from the corner */
.people-map {
  position: fixed; left: 40px; top: 90px; z-index: 75;
  width: 620px; height: 470px; min-width: 320px; min-height: 260px;
  max-width: 96vw; max-height: 88vh;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  box-shadow: var(--shadow, 0 14px 40px rgba(0, 0, 0, 0.35));
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  resize: both; overflow: hidden;
}
.people-map-head {
  display: flex; align-items: center; gap: 8px; padding: 9px 12px; flex: none;
  border-bottom: 1px solid var(--line); cursor: grab; user-select: none; touch-action: none;
  letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.8rem;
}
.people-map-head:active { cursor: grabbing; }
.people-map-head .icon { color: var(--accent); }
.people-map-head .btn-icon { margin-left: auto; }
/* header sits above the radar backdrop pseudo-layers */
.people-map-head { position: relative; z-index: 2; background: var(--surface); }
.people-map-svg { flex: 1; width: 100%; height: 100%; min-height: 0; position: relative; z-index: 1; }

/* radar backdrop: concentric rings + faint plot grid behind the constellation */
.people-map::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    repeating-radial-gradient(circle at 50% 55%, transparent 0 46px, color-mix(in srgb, var(--accent) 7%, transparent) 46px 47px),
    linear-gradient(color-mix(in srgb, var(--accent) 3%, transparent) 1px, transparent 1px) 0 0 / 100% 28px,
    linear-gradient(90deg, color-mix(in srgb, var(--accent) 3%, transparent) 1px, transparent 1px) 0 0 / 28px 100%;
}
/* slow radar sweep under the nodes */
.people-map::after {
  content: ""; position: absolute; left: 50%; top: 55%; width: 160%; aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0 336deg, color-mix(in srgb, var(--accent2) 16%, transparent) 354deg, transparent 360deg);
  animation: holo-rotate 9s linear infinite;
  pointer-events: none;
  opacity: 0.55;
}

.pm-edge {
  fill: none;
  stroke: color-mix(in srgb, var(--accent2) 60%, var(--line-strong));
  stroke-width: 1.3;
  stroke-dasharray: 7 5;
  animation: net-flow 1.2s linear infinite;
  opacity: 0.85;
}
.pm-dot { fill: var(--accent2); filter: drop-shadow(0 0 4px var(--accent2)); }
.pm-edge-label { fill: var(--text-dim); font-size: 10px; text-anchor: middle; font-family: var(--font-mono); }
.pm-node { cursor: grab; }
.pm-node:active { cursor: grabbing; }
.pm-fill { fill: color-mix(in srgb, var(--accent) 14%, transparent); }
.pm-ring { fill: none; stroke: var(--accent); stroke-width: 1.4; }
/* expanding echo ring + slowly orbiting tick ring; CSS transforms stay on these child
   circles (transform-box: fill-box) so they never fight the node <g>'s drag transform */
.pm-pulse {
  fill: none; stroke: color-mix(in srgb, var(--accent) 50%, transparent); stroke-width: 1;
  transform-box: fill-box; transform-origin: center;
  animation: pm-pulse 2.8s ease-out infinite;
}
.pm-orbit {
  fill: none; stroke: color-mix(in srgb, var(--accent2) 45%, transparent); stroke-width: 1;
  stroke-dasharray: 4 9; stroke-linecap: round;
  transform-box: fill-box; transform-origin: center;
  animation: holo-rotate 12s linear infinite;
}
.pm-place .pm-ring { stroke-dasharray: 5 3; }
.pm-pin { color: var(--accent); }
.pm-node:hover .pm-ring { stroke-width: 2.4; filter: drop-shadow(0 0 6px var(--glow)); }
.pm-initials { fill: var(--accent); font-family: var(--font-display); font-weight: 700; font-size: 13px;
  text-anchor: middle; dominant-baseline: middle; }
.pm-name { fill: var(--text); font-size: 11px; text-anchor: middle; }

@media (max-width: 760px) {
  .dossier-body { flex-direction: column; }
  .dossier-col.side { flex: none; }
  .dossier-head { flex-wrap: wrap; }
  .dossier-notes-body { flex-direction: column; }
  .people-map { left: 4vw !important; top: 70px !important; width: 92vw; height: 60vh; }
}

/* ============================================================
   Passkey ceremony · full-screen 3D overlay (see passkey3d.js)
   ============================================================ */
.pk3d {
  position: fixed; inset: 0; z-index: 85; /* above deck overlays (80), below modal (90) and toasts (130) */
  background: color-mix(in srgb, var(--bg0) 82%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  opacity: 0; transition: opacity 0.3s ease;
  pointer-events: none; /* the OS passkey sheet owns input; the page stays reachable */
}
.pk3d.show { opacity: 1; }
.pk3d-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.pk3d-cap {
  position: absolute; left: 50%; bottom: 12vh; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center; white-space: nowrap;
}
.pk3d-title {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 700;
  letter-spacing: 0.42em; text-indent: 0.42em; color: var(--text);
  text-shadow: 0 0 18px var(--glow);
}
.pk3d-sub {
  font-family: var(--font-mono); font-size: 0.78rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-dim);
  animation: pk3d-blink 1.6s ease-in-out infinite;
}
@keyframes pk3d-blink { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
@keyframes pk3d-glitch {
  0% { transform: translateX(0); }
  25% { transform: translateX(-3px); }
  50% { transform: translateX(3px); }
  75% { transform: translateX(-2px); }
}
.pk3d[data-state="success"] .pk3d-title { color: var(--good); text-shadow: 0 0 22px color-mix(in srgb, var(--good) 45%, transparent); }
.pk3d[data-state="success"] .pk3d-sub { color: var(--good); animation: none; }
.pk3d[data-state="fail"] .pk3d-title { color: var(--bad); text-shadow: 0 0 22px color-mix(in srgb, var(--bad) 45%, transparent); animation: pk3d-glitch 0.32s steps(2) 3; }
.pk3d[data-state="fail"] .pk3d-sub { color: var(--bad); animation: none; }

/* verdict cinema: full-screen edge veil + success shockwave + failure shake.
   Pure CSS keyed off the overlay state, so every ceremony caller gets it free. */
.pk3d::before {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0;
}
.pk3d[data-state="success"]::before {
  background: radial-gradient(120% 120% at 50% 50%, transparent 55%, color-mix(in srgb, var(--good) 26%, transparent));
  animation: pk3d-veil 0.9s ease-out forwards;
}
.pk3d[data-state="fail"]::before {
  background: radial-gradient(120% 120% at 50% 50%, transparent 52%, color-mix(in srgb, var(--bad) 34%, transparent));
  animation: pk3d-alarm 0.5s ease-in-out 3;
}
.pk3d[data-state="success"]::after {
  content: "";
  position: absolute; left: 50%; top: 50%; width: 12vmax; height: 12vmax; margin: -6vmax 0 0 -6vmax;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--good) 70%, transparent);
  box-shadow: 0 0 40px color-mix(in srgb, var(--good) 40%, transparent), inset 0 0 30px color-mix(in srgb, var(--good) 25%, transparent);
  pointer-events: none;
  opacity: 0;
  animation: pk3d-shock 0.9s cubic-bezier(0.1, 0.7, 0.2, 1) forwards;
}
.pk3d[data-state="fail"] .pk3d-canvas { animation: pk3d-shake 0.45s steps(24); }
@keyframes pk3d-veil { 0% { opacity: 0; } 40% { opacity: 1; } 100% { opacity: 0.7; } }
@keyframes pk3d-alarm { 0%, 100% { opacity: 0.15; } 50% { opacity: 1; } }
@keyframes pk3d-shock { 0% { transform: scale(0.2); opacity: 0.9; } 100% { transform: scale(3.4); opacity: 0; } }
@keyframes pk3d-shake {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-7px, 3px); }
  20% { transform: translate(6px, -4px); }
  30% { transform: translate(-5px, -3px); }
  40% { transform: translate(5px, 4px); }
  50% { transform: translate(-4px, 2px); }
  60% { transform: translate(4px, -2px); }
  70% { transform: translate(-3px, -2px); }
  80% { transform: translate(2px, 2px); }
  90% { transform: translate(-1px, 1px); }
}

/* ============================================================
   Cloud deck · OneDrive + SharePoint through Microsoft Graph
   ============================================================ */
/* uplink (consent) ceremony: expanding pulse rings + an orbiting satellite around the cloud */
.cl-uplink {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 12px; padding: 44px 20px 40px; overflow: hidden;
}
.cl-stage { position: relative; width: 150px; height: 150px; display: flex; align-items: center; justify-content: center; }
.cl-rings i {
  position: absolute; inset: 0; margin: auto; width: 72px; height: 72px; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  animation: cl-pulse 2.7s ease-out infinite;
}
.cl-rings i:nth-child(2) { animation-delay: 0.9s; }
.cl-rings i:nth-child(3) { animation-delay: 1.8s; }
@keyframes cl-pulse { from { transform: scale(0.55); opacity: 0.9; } to { transform: scale(2.05); opacity: 0; } }
.cl-orbit {
  position: absolute; inset: 6px; border-radius: 50%;
  border: 1px dashed color-mix(in srgb, var(--accent2) 40%, transparent);
  animation: holo-rotate 11s linear infinite;
}
.cl-sat {
  position: absolute; top: -4.5px; left: 50%; width: 9px; height: 9px; margin-left: -4.5px;
  border-radius: 50%; background: var(--accent2); box-shadow: 0 0 12px var(--accent2);
}
.cl-stage-icon {
  width: 46px; height: 46px; color: var(--accent);
  filter: drop-shadow(0 0 14px var(--glow));
  animation: holo-float 5s ease-in-out infinite;
}
.cl-uplink-title {
  margin: 0; font-family: var(--font-display); font-size: 1rem; font-weight: 700;
  letter-spacing: 0.35em; text-indent: 0.35em; text-shadow: 0 0 16px var(--glow);
}
.cl-uplink-text { margin: 0; max-width: 540px; }

/* two-column shell: sources rail + browser */
.cl-layout { display: grid; grid-template-columns: 290px minmax(0, 1fr); gap: 16px; align-items: start; }
.cl-right { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

/* sources rail: OneDrive with an animated quota ring, then each site's libraries */
.cl-sources .card-body { display: flex; flex-direction: column; gap: 4px; }
.cl-site-name {
  margin-top: 10px; padding: 0 4px; font-size: 0.72rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cl-source {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  transition: border-color 0.15s, background 0.15s, transform 0.15s;
  animation: holo-in 0.35s ease backwards;
}
.cl-source:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); transform: translateX(2px); }
.cl-source.active {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  box-shadow: inset 2px 0 0 var(--accent);
}
.cl-ring-wrap { position: relative; width: 40px; height: 40px; flex: none; display: inline-flex; align-items: center; justify-content: center; }
.cl-ring { width: 40px; height: 40px; transform: rotate(-90deg); }
.cl-ring circle { fill: none; stroke-width: 3.4; }
.cl-ring .bg { stroke: color-mix(in srgb, var(--line) 85%, transparent); }
/* r=19 -> circumference 119.4; --p (0..1) sweeps in via the transition once JS sets it */
.cl-ring .fg {
  stroke: var(--accent); stroke-linecap: round;
  stroke-dasharray: 119.4;
  stroke-dashoffset: calc(119.4 * (1 - var(--p, 0)));
  transition: stroke-dashoffset 1.3s cubic-bezier(0.2, 0.7, 0.2, 1);
  filter: drop-shadow(0 0 5px var(--glow));
}
.cl-ring .fg.hot { stroke: var(--bad); }
.cl-ring-pct { position: absolute; font-family: var(--font-mono); font-size: 0.56rem; color: var(--text-dim); }
.cl-src-icon { width: 22px; height: 22px; color: var(--accent2); }
.cl-src-text { min-width: 0; display: flex; flex-direction: column; }
.cl-src-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cl-src-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* toolbar: breadcrumbs + search + upload, with the travelling scanner rule underneath */
.cl-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-bottom: 12px; border-bottom: 1px solid var(--line); position: relative; }
.cl-toolbar::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent2) 35%, var(--accent) 65%, transparent);
  background-size: 220% 100%;
  animation: holo-scan 6s linear infinite;
}
.cl-crumbs { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; flex: 1; min-width: 0; }
.cl-crumb {
  background: none; border: 0; padding: 2px 4px; border-radius: 4px;
  color: var(--text-dim); font: inherit; cursor: pointer;
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cl-crumb:hover { color: var(--accent); }
.cl-crumb:last-of-type { color: var(--text); font-weight: 600; }
.cl-crumb-sep { color: var(--text-dim); opacity: 0.6; }
.cl-crumb-search { color: var(--accent2); font-family: var(--font-mono); font-size: 0.85rem; }
.cl-tools { display: flex; gap: 8px; margin-left: auto; }
.cl-tools .field { width: 220px; }

.cl-progress { display: flex; flex-direction: column; gap: 4px; padding-top: 10px; }

/* tile grid: staggered holo entrance, lift + light-sweep on hover, drop-target glow */
.cl-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px;
  padding-top: 14px; min-height: 180px; border-radius: var(--radius-sm);
  transition: box-shadow 0.2s, background 0.2s;
}
.cl-grid.cl-drop {
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 55%, transparent);
}
.cl-tile {
  position: relative; display: flex; flex-direction: column; gap: 6px;
  padding: 12px 10px 10px; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface);
  animation: holo-in 0.4s ease backwards;
  animation-delay: calc(min(var(--i, 0), 30) * 24ms);
  transition: transform 0.16s, border-color 0.16s, box-shadow 0.16s;
}
.cl-tile:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
  box-shadow: 0 10px 26px color-mix(in srgb, var(--accent) 16%, transparent);
}
/* light sweep across the tile on hover (same treatment as the People cards) */
.cl-tile::after {
  content: ""; position: absolute; top: -60%; bottom: -60%; left: -30%; width: 40%;
  background: linear-gradient(105deg, transparent, color-mix(in srgb, var(--accent2) 12%, transparent), transparent);
  transform: translateX(-140%) rotate(8deg);
  transition: transform 0.55s ease;
  pointer-events: none;
}
.cl-tile:hover::after { transform: translateX(340%) rotate(8deg); }
.cl-tile.folder { cursor: pointer; }
.cl-tile.folder .cl-tile-icon { color: var(--accent); }
.cl-tile-art {
  position: relative; height: 74px; display: flex; align-items: center; justify-content: center;
  border-radius: 6px; overflow: hidden;
  background: color-mix(in srgb, var(--bg0) 45%, transparent);
}
.cl-thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
.cl-tile-icon {
  width: 30px; height: 30px; color: var(--accent2);
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--accent2) 30%, transparent));
  transition: transform 0.16s;
}
.cl-tile.folder:hover .cl-tile-icon { transform: scale(1.12); }
.cl-tile-name { font-size: 0.88rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cl-tile-actions {
  position: absolute; top: 8px; right: 8px; z-index: 2; display: flex; gap: 4px;
  opacity: 0; transform: translateY(-4px);
  transition: opacity 0.15s, transform 0.15s;
}
.cl-tile:hover .cl-tile-actions { opacity: 1; transform: none; }
.cl-tile-actions .btn-icon {
  background: color-mix(in srgb, var(--bg0) 78%, transparent);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
}

/* recent strip: a horizontally scrolling shelf of the user's latest files */
.cl-recent { display: flex; gap: 10px; overflow-x: auto; padding: 4px 2px 8px; }
.cl-recent-item {
  display: flex; flex-direction: column; gap: 4px; flex: 0 0 150px; padding: 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: inherit; text-decoration: none;
  animation: holo-in 0.4s ease backwards;
  animation-delay: calc(var(--i, 0) * 40ms);
  transition: transform 0.15s, border-color 0.15s;
}
.cl-recent-item:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.cl-recent-item .icon { width: 20px; height: 20px; color: var(--accent2); }
.cl-recent-name { font-size: 0.82rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (max-width: 900px) {
  .cl-layout { grid-template-columns: 1fr; }
  .cl-tools { margin-left: 0; width: 100%; }
  .cl-tools .field { flex: 1; width: auto; }
}

/* ============================== Search deck ============================== */
.search-bar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; }
.search-bar #search-q { flex: 1; min-width: 220px; }
.search-bar #search-media { width: 150px; }

.search-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
.search-col-title {
  display: flex; align-items: center; gap: 8px; margin: 0 0 10px;
  font-size: 0.85rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-dim);
}
.search-col-title .icon { width: 16px; height: 16px; color: var(--accent2); }

.search-count { margin: 0 2px 8px; }

.search-hit {
  display: flex; gap: 12px; padding: 12px; margin-bottom: 10px;
  animation: holo-in 0.4s ease backwards;
}
.search-thumb {
  width: 72px; height: 72px; flex: none; object-fit: cover;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg0) 45%, transparent);
}
.search-thumb.noimg { display: flex; align-items: center; justify-content: center; }
.search-thumb.noimg .icon { width: 24px; height: 24px; color: var(--accent2); opacity: 0.6; }
.search-hit-body { flex: 1; min-width: 0; }
.search-hit-title { font-weight: 600; overflow-wrap: anywhere; }
.search-excerpt { margin: 6px 0 8px; font-size: 0.9rem; overflow-wrap: anywhere; }
.search-excerpt mark {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent); border-radius: 3px; padding: 0 2px;
}
.search-hit-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

.search-pager { display: flex; gap: 12px; align-items: center; justify-content: center; padding: 6px 0 2px; }

/* in-Jupiter Wikipedia reader (iframe onto our sandboxed proxy endpoint) */
.search-reader { margin-bottom: 14px; }
.search-reader-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.search-reader-title { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-reader iframe {
  width: 100%; height: 72vh; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: #fff;   /* Wikipedia articles assume a light canvas */
}

@media (max-width: 900px) {
  .search-cols { grid-template-columns: 1fr; }
  .search-reader iframe { height: 60vh; }
}

/* ============================================================
   Comms array · live API telemetry HUD (injected by core.js)
   Every J.fetchJson call registers a transmission: the capsule
   wakes, the waveform runs, and the log shows destination,
   verdict and latency. Sits clear of the dock (left) and the
   toasts (right); hidden on phones.
   ============================================================ */
#comms {
  position: fixed;
  left: calc(var(--dock-w) + 18px);
  bottom: 18px;
  z-index: 84; /* under pk3d (85), modal (90) and toasts (130) */
  font-family: var(--font-mono);
  user-select: none;
  opacity: 0.66;
  transition: opacity 0.25s ease, left 0.25s ease;
}
html[data-dock="min"] #comms { left: calc(var(--dock-w-min) + 18px); }
#comms:hover, #comms.tx, #comms.err { opacity: 1; }
/* deck overlays inside .shell stack below a body-level HUD regardless of z-index,
   so step aside while one is open */
body:has(.dossier-overlay:not([hidden])) #comms,
body:has(.people-map:not([hidden])) #comms { opacity: 0; pointer-events: none; }

.comms-capsule {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px 8px 9px;
  background: color-mix(in srgb, var(--bg0) 78%, transparent);
  border: 1px solid var(--hud);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  cursor: pointer;
  transition: border-color 0.25s ease, filter 0.25s ease;
}
/* drop-shadow, not box-shadow: the capsule's clip-path would clip a box-shadow away */
#comms.tx .comms-capsule {
  border-color: color-mix(in srgb, var(--accent2) 55%, transparent);
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--accent2) 35%, transparent));
}
#comms.err .comms-capsule {
  border-color: color-mix(in srgb, var(--bad) 60%, transparent);
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--bad) 45%, transparent));
  animation: comms-errflash 0.5s ease 2;
}
@keyframes comms-errflash { 50% { background: color-mix(in srgb, var(--bad) 14%, transparent); } }

/* radar scope: concentric rings + rotating sweep */
.comms-scope {
  position: relative;
  width: 26px; height: 26px; flex: none;
  border-radius: 50%;
  border: 1px solid var(--hud);
  overflow: hidden;
}
.comms-scope::before {
  content: "";
  position: absolute; inset: 0;
  background:
    radial-gradient(circle, transparent 39%, var(--hud) 40%, transparent 41%),
    radial-gradient(circle, transparent 69%, var(--hud) 70%, transparent 71%);
}
.comms-scope::after {
  content: "";
  position: absolute; inset: 0;
  background: conic-gradient(from 0deg, color-mix(in srgb, var(--accent2) 50%, transparent), transparent 80deg);
  animation: comms-sweep 2.8s linear infinite;
}
/* the radar only turns while transmissions run (or under the pointer) — no idle GPU spin */
#comms:not(.tx):not(:hover) .comms-scope::after { animation-play-state: paused; }
@keyframes comms-sweep { to { transform: rotate(360deg); } }

.comms-label {
  font-size: 0.62rem;
  letter-spacing: 0.26em;
  color: var(--text-dim);
  transition: color 0.2s ease;
}
#comms.tx .comms-label { color: var(--accent2); }
#comms.err .comms-label { color: var(--bad); }

/* transmission waveform: flat when idle, running while calls are in flight */
.comms-wave { display: flex; align-items: flex-end; gap: 2px; height: 14px; }
.comms-wave i { width: 2px; height: 3px; background: var(--text-dim); opacity: 0.45; transition: background 0.2s ease; }
#comms.tx .comms-wave i {
  background: var(--accent2);
  opacity: 1;
  animation: comms-eq 0.65s ease-in-out infinite alternate;
}
#comms.tx .comms-wave i:nth-child(2) { animation-delay: 0.1s; }
#comms.tx .comms-wave i:nth-child(3) { animation-delay: 0.22s; }
#comms.tx .comms-wave i:nth-child(4) { animation-delay: 0.34s; }
#comms.tx .comms-wave i:nth-child(5) { animation-delay: 0.46s; }
@keyframes comms-eq { from { height: 3px; } to { height: 14px; } }

.comms-count { font-size: 0.66rem; color: var(--accent2); min-width: 2ch; text-align: right; }

/* transmission log: rises out of the capsule on hover */
.comms-log {
  position: absolute; left: 0; bottom: calc(100% + 10px);
  width: 320px;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--bg0) 86%, transparent);
  border: 1px solid var(--hud);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
#comms:hover .comms-log, #comms.open .comms-log {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.comms-title {
  font-size: 0.6rem;
  letter-spacing: 0.3em;
  color: var(--text-dim);
  border-bottom: 1px solid var(--line);
  padding-bottom: 6px;
  margin-bottom: 7px;
}
.comms-rows { display: flex; flex-direction: column; gap: 5px; }
.comms-row {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  animation: comms-row-in 0.22s ease backwards;
}
@keyframes comms-row-in { from { opacity: 0; transform: translateX(-8px); } }
.comms-row .dot {
  width: 6px; height: 6px; flex: none;
  background: var(--text-dim);
  transform: rotate(45deg); /* a diamond, matching the chamfer language */
}
.comms-row.pend .dot { background: var(--accent2); box-shadow: 0 0 8px var(--accent2); animation: pulse 0.9s ease-in-out infinite; }
.comms-row.ok .dot { background: var(--good); }
.comms-row.err .dot { background: var(--bad); box-shadow: 0 0 8px var(--bad); }
.comms-row .dest { flex: 1; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.comms-row .stat { font-size: 0.64rem; letter-spacing: 0.08em; color: var(--text-dim); white-space: nowrap; }
.comms-row.pend .stat { color: var(--accent2); }
.comms-row.ok .stat { color: var(--good); }
.comms-row.err .stat { color: var(--bad); }
.comms-row .lat { min-width: 6ch; text-align: right; color: var(--text-dim); font-size: 0.66rem; }

/* ================== Library admin (Settings: libraries + content picker) ================== */
.libpick-crumbs { margin-top: 6px; }
.libpick-crumbs .g-crumb { cursor: pointer; }
.libpick-crumbs .g-crumb:hover { color: var(--accent); }
.libpick-list {
  max-height: 46vh; overflow: auto; margin: 8px 0;
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 6px 10px;
}
.libpick-row { display: flex; align-items: center; gap: 8px; padding: 4px 2px; }
.libpick-row .icon { color: var(--accent2); flex: none; }
.libpick-row .lp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.libpick-row .lp-name.lp-nav { cursor: pointer; }
.libpick-row .lp-name.lp-nav:hover { color: var(--accent); }
.libpick-row .lp-size { margin-left: auto; flex: none; }
#mg-list { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; max-height: 40vh; overflow: auto; }

/* ================== UFO deck (PURSUE archive) ================== */
.ufo-bar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 18px; }
.ufo-count { margin-bottom: 8px; }
.ufo-table { min-width: 960px; }
.ufo-table tbody tr { cursor: pointer; }
.ufo-table .ufo-title .chip { margin-left: 6px; }
.ufo-table .ufo-file { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ufo-release-row { display: flex; flex-direction: column; gap: 8px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.ufo-release-row:last-child { border-bottom: none; }
.ufo-detail { display: flex; flex-direction: column; gap: 10px; }
.ufo-detail-title { margin: 0; font-family: var(--font-display); letter-spacing: 0.04em; }
.ufo-chips { align-items: center; }
.ufo-chips button.chip { cursor: pointer; }
.ufo-thumb {
  max-width: 100%; max-height: 320px; object-fit: contain; align-self: flex-start;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.ufo-row { display: flex; gap: 10px; font-size: 0.85rem; }
.ufo-label {
  flex: none; width: 84px; padding-top: 2px;
  color: var(--text-dim);
  font-family: var(--font-mono); font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.08em;
}
/* Site blurbs and DVIDS narratives carry meaningful paragraph breaks. */
.ufo-desc { margin: 4px 0 0; line-height: 1.55; white-space: pre-line; }

/* ---------- Observation Planner deck ---------- */
.op-layout { display: grid; grid-template-columns: 244px 1fr; gap: 16px; align-items: start; }
.op-rail { position: sticky; top: 12px; }
.op-rail-head {
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-dim); margin: 0 4px 8px;
}
.op-plan-list { display: flex; flex-direction: column; gap: 8px; }
.op-rail-empty { padding: 6px 4px; }
.op-plan {
  display: flex; flex-direction: column; gap: 2px; text-align: left; cursor: pointer;
  background: var(--card-bg, var(--surface)); border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px; color: inherit; transition: border-color .15s, transform .05s;
}
.op-plan:hover { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.op-plan.active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.op-plan-title { font-weight: 600; font-size: 0.92rem; }
.op-plan-meta { font-size: 0.68rem; color: var(--text-dim); }
.op-plan-site { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.op-main { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.op-plan-head { padding: 16px 18px; }
.op-ph-top { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.op-ph-title { margin: 0; font-size: 1.3rem; }
.op-ph-sub { margin: 3px 0 0; font-size: 0.74rem; }
.op-ph-actions { margin-left: auto; display: flex; gap: 6px; flex-wrap: wrap; }
.op-danger:hover { border-color: var(--danger, #f87171); color: var(--danger, #f87171); }
.op-night { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.op-nchip {
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 3px 10px;
  color: var(--text-dim);
}
.op-nchip b { color: var(--accent); font-weight: 700; margin-right: 3px; }

/* Raise the add-target card (and thus its absolutely-positioned #op-suggest dropdown) above the
   target cards below it: those cards get a transform from the deck-in stagger, each becoming its own
   stacking context, so without an explicit z-index here the dropdown paints behind them. */
.op-add { padding: 12px 14px; position: relative; z-index: 20; }
.op-add-bar #op-search { width: 100%; }
.op-suggest {
  display: none; position: absolute; left: 14px; right: 14px; z-index: 30; margin-top: 4px;
  background: var(--card-bg, var(--surface)); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 14px 34px rgba(0,0,0,.4); max-height: 320px; overflow-y: auto;
}
.op-suggest.open { display: block; }
.op-sg {
  display: flex; flex-direction: column; gap: 1px; width: 100%; text-align: left; cursor: pointer;
  background: none; border: 0; border-bottom: 1px solid var(--line); padding: 8px 12px; color: inherit;
}
.op-sg:last-child { border-bottom: 0; }
.op-sg:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.op-sg-name { font-size: 0.9rem; }
.op-sg-none { padding: 10px 12px; }
.op-add-bar { display: flex; gap: 8px; align-items: center; }
.op-add-bar #op-search { flex: 1; min-width: 0; }
.op-add-bar #op-browse { flex: none; white-space: nowrap; }

/* tick-box target picker (modal) — three bundled groups plus a search section, each a grid of
   checkbox rows. The scroll lives on the groups container so the search field and the footer count
   stay put while a 110-row Messier list scrolls under them. */
.op-pick { display: flex; flex-direction: column; gap: 12px; }
/* The picker needs more than the 540px default: two-plus columns of tick rows, or the Messier list
   becomes an endless single column. Opt-in class so no other modal is affected. */
.modal-panel.op-pick-panel { max-width: 880px; }
.op-pick-bar #op-pick-q { width: 100%; }
.op-pick-groups {
  display: flex; flex-direction: column; gap: 18px;
  max-height: min(56vh, 520px); overflow-y: auto; padding-right: 4px;
}
.op-pick-group[hidden] { display: none; }
.op-pick-ghead { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 8px; }
.op-pick-ghead h4 { margin: 0; font-size: 0.95rem; }
.op-pick-ghead p { margin: 2px 0 0; }
.op-pick-gacts { margin-left: auto; display: flex; gap: 6px; flex: none; }
.op-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 4px; }
.op-pick-row {
  display: grid; grid-template-columns: auto 1fr; grid-template-areas: "box name" "box meta";
  gap: 0 8px; align-items: center; cursor: pointer;
  border: 1px solid transparent; border-radius: 10px; padding: 5px 8px;
}
.op-pick-row:hover { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.op-pick-row input { grid-area: box; }
.op-pick-name { grid-area: name; font-size: 0.86rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.op-pick-meta { grid-area: meta; font-size: 0.66rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.op-pick-row.on-plan { opacity: .55; cursor: default; }
.op-pick-row:has(input:disabled:not(:checked)) { opacity: .4; cursor: not-allowed; }
.op-pick-count { margin-right: auto; align-self: center; }

.op-featured { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 10px; }
.op-fchip { cursor: pointer; }
.op-fchip:hover { border-color: var(--accent); color: var(--accent); }

/* sky dome — the "Sky dome" toggle swaps #op-targets' innerHTML wholesale for a DomeGallery React
   island (a full-stage gallery of the plan's DSS target images). Plain divs, no .card: they must stay
   outside the .card::after reticle exclusion list (jupiter-skin-v2.md landmine #1), and since the swap
   is a full innerHTML replace rather than a [hidden] flip, they never run through the .content>* entrance
   stagger (landmine #2) either. */
.op-dome { display: flex; flex-direction: column; gap: 8px; }
.op-dome-note { margin: 0; }
.op-dome-stage {
  height: 70vh; min-height: 320px; position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: 14px; background: var(--bg0);
}

.op-targets { display: flex; flex-direction: column; gap: 12px; }
.op-empty { margin: 6px 0; }
.op-target { padding: 14px 16px; }
.op-t-top { display: flex; gap: 14px; align-items: flex-start; }
.op-thumb {
  width: 96px; height: 96px; flex: none; object-fit: cover; border-radius: 10px;
  border: 1px solid var(--line); background: #05070d;
}
.op-thumb-none {
  display: flex; align-items: center; justify-content: center; text-align: center;
  color: var(--text-dim); font-size: 0.64rem; font-family: var(--font-mono);
}
.op-thumb-ss {
  display: flex; align-items: center; justify-content: center;
  font-size: 2.2rem; color: var(--accent); background: var(--surface);
}
/* A body portrait is a picture of one object on black, so it is fitted whole rather than cropped
   to fill — a survey cutout is a piece of sky and can be cropped freely, a planet cannot. */
.op-thumb-portrait { object-fit: contain; }
.op-t-id { flex: 1; min-width: 0; }
.op-t-id h3 { margin: 2px 0 0; font-size: 1.05rem; }
.op-t-num { color: var(--text-dim); font-size: 0.72rem; }
.op-t-common { color: var(--text-dim); font-weight: 400; font-style: italic; font-size: 0.9rem; }
.op-t-type { margin: 3px 0 0; letter-spacing: 0.08em; }
.op-t-facts { margin: 4px 0 0; color: var(--text-dim); }
.op-t-actions { display: flex; flex-direction: column; gap: 6px; flex: none; }

.op-eph { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.op-verdict { margin: 0 0 8px; font-style: italic; }
.op-eph-grid { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
.op-alt {
  width: 240px; height: 84px; flex: none;
  background: color-mix(in srgb, var(--surface) 70%, transparent); border: 1px solid var(--line); border-radius: 8px;
}
.op-alt-empty { width: 240px; height: 84px; }
.op-alt-dark { fill: color-mix(in srgb, var(--accent) 14%, transparent); }
.op-alt-horizon { stroke: var(--text-dim); stroke-width: 0.8; stroke-dasharray: 3 3; opacity: .7; }

/* ---------- Members admin deck ---------- */
/* Settings' old inline member card is now just a pointer here — styled as a real card-link row
   rather than the small corner label .card-link normally is. */
.mem-goto {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface2);
}
.mem-goto:hover { border-color: var(--accent); color: var(--accent); }
.mem-areas-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 4px 14px; }
.op-alt-line { fill: none; stroke: var(--accent); stroke-width: 1.8; stroke-linejoin: round; }
.op-eph-tab { border-collapse: collapse; }
.op-eph-tab th {
  text-align: left; font-weight: normal; font-family: var(--font-mono); color: var(--text-dim);
  font-size: 0.68rem; letter-spacing: 0.05em; text-transform: uppercase; padding: 2px 12px 2px 0; white-space: nowrap;
}
.op-eph-tab td { padding: 2px 0; }

/* log + edit modals */
.op-log, .op-edit { display: flex; flex-direction: column; gap: 10px; }
.op-log-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.op-log-obs { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.op-field { display: flex; flex-direction: column; gap: 4px; }
.op-field .field, .op-field textarea { width: 100%; }
.op-flabel {
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-dim);
}
.op-edit-site { border: 1px solid var(--line); border-radius: 12px; padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.op-edit-site-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.op-edit-site-head .op-flabel { margin-right: auto; }
.op-site-pick { flex: 0 1 auto; }

@media (max-width: 820px) {
  .op-layout { grid-template-columns: 1fr; }
  .op-rail { position: static; }
  .op-plan-list { flex-direction: row; overflow-x: auto; padding-bottom: 4px; }
  .op-plan { flex: 0 0 200px; }
}
@media (max-width: 560px) {
  .op-t-top { flex-wrap: wrap; }
  .op-log-grid { grid-template-columns: 1fr; }
  .op-eph-grid { gap: 10px; }
}

/* logbook — Plans/Logbook rail tabs and the observation-history list */
.op-rail-tabs { display: flex; gap: 4px; margin: 0 2px 10px; }
.op-tab {
  flex: 1; cursor: pointer; background: none; border: 1px solid var(--line); border-radius: 8px;
  padding: 5px 10px; color: var(--text-dim); font-family: var(--font-mono); font-size: 0.68rem;
  letter-spacing: 0.08em; text-transform: uppercase; transition: border-color .15s, color .15s;
}
.op-tab:hover { color: var(--text); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.op-tab.active { color: var(--accent); border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.op-log-filterbar { margin-top: 12px; }
.op-log-filterbar .field { width: 100%; }
.op-logbook { display: flex; flex-direction: column; gap: 12px; }
.op-le { padding: 14px 16px; }
.op-le-top { display: flex; gap: 14px; align-items: flex-start; }
.op-le-thumb { width: 64px; height: 64px; }
.op-le-id { flex: 1; min-width: 0; }
.op-le-id h3 { margin: 2px 0 0; font-size: 1.02rem; }
.op-le-when { margin: 3px 0 0; color: var(--text-dim); }
.op-le-stars { color: var(--accent); letter-spacing: 1px; margin-right: 6px; align-self: center; white-space: nowrap; }
.op-le-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px 16px;
  margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px;
}
.op-le-field { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.op-le-k { font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); }
.op-le-v { font-size: 0.88rem; overflow-wrap: anywhere; }
.op-le-notes { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.op-le-notes p { margin: 3px 0 0; white-space: pre-line; }
.op-le { cursor: pointer; }
.op-le:hover { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.op-le:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* observation detail — read-only journal page for one logged observation (replaces reopening the form) */
.op-det-bar { display: flex; align-items: center; gap: 10px; }
.op-det-bar-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.op-det { display: flex; flex-direction: column; gap: 4px; }
.op-det-top { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; }

.op-det-hero { display: flex; flex-direction: column; align-items: center; gap: 8px; flex: none; }
.op-det-eyepiece { position: relative; width: clamp(260px, 32vw, 340px); height: clamp(260px, 32vw, 340px); border-radius: 50%; }
.op-det-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: 50%; background: #05070d; }
.op-det-eyepiece-ss { background: radial-gradient(circle, var(--surface), color-mix(in srgb, var(--accent) 8%, var(--surface))); }
.op-det-glyph { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 5rem; color: var(--accent); }
.op-det-ring { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.op-det-ring-outer { fill: none; stroke: var(--line); stroke-width: .8; }
.op-det-tick { stroke: var(--line); stroke-width: .8; }
.op-det-tick-c { stroke: var(--accent); stroke-width: .8; opacity: .8; }
.op-det-caption { margin: 0; }

.op-det-id { flex: 1 1 280px; min-width: 240px; display: flex; flex-direction: column; gap: 4px; padding-top: 4px; }
.op-det-name { margin: 0; font-size: 1.6rem; font-weight: 700; }
.op-det-common { color: var(--text-dim); font-weight: 400; font-style: italic; font-size: 1rem; }
.op-det-kicker { margin: 2px 0 0; letter-spacing: 0.08em; text-transform: uppercase; font-size: 0.74rem; }
.op-det-meta { margin: 2px 0 0; }
.op-det-ratingrow { display: flex; align-items: center; gap: 12px; margin-top: 8px; flex-wrap: wrap; }
.op-det-stars { color: var(--accent); letter-spacing: 2px; }

.op-det-strip { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; border-top: 1px solid var(--line); padding-top: 12px; margin-top: 8px; }
.op-det-striplabel { flex: 0 0 92px; padding-top: 4px; }
.op-det-stripbody { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; flex: 1; min-width: 0; }

.op-det-gauge { display: flex; align-items: center; gap: 6px; }
.op-det-gauge-label { text-transform: lowercase; }
.op-det-gauge-val { color: var(--text-dim); }
.op-led-bar { display: flex; gap: 3px; }
.op-led { width: 18px; height: 8px; border-radius: 2px; border: 1px solid var(--line); background: transparent; display: inline-block; }
.op-led-on { background: color-mix(in srgb, var(--accent) 85%, transparent); border-color: color-mix(in srgb, var(--accent) 85%, transparent); }
.op-det-badge { border: 1px solid var(--line); border-radius: 999px; padding: 2px 10px; font-size: 0.74rem; }
.op-det-nelm { color: var(--text-dim); }

.op-det-kv { color: var(--text-dim); }
.op-det-kv b { color: var(--accent); font-weight: 700; margin-right: 3px; }

.op-det-notes {
  max-width: 62ch; font-size: 0.98rem; line-height: 1.65; white-space: pre-line; margin: 0;
  padding-left: 14px; border-left: 2px solid color-mix(in srgb, var(--accent) 45%, transparent);
}
.op-det-conditions { margin: 0; }

@media (max-width: 560px) {
  .op-det-top { flex-direction: column; align-items: center; }
  .op-det-id { text-align: center; }
  .op-det-ratingrow { justify-content: center; }
}

@media (prefers-reduced-motion: no-preference) {
  /* Entrance animation: JS inserts the detail root as .op-det, then adds .op-det-in on the next
     frame — the initial (hidden) state lives on the :not(.op-det-in) selector so the transition
     actually has something to animate from, instead of the old animation:…forwards starting the
     element already in its final painted state on first render. */
  .op-det .op-det-eyepiece { transition: opacity .22s ease-out, transform .22s ease-out; }
  .op-det:not(.op-det-in) .op-det-eyepiece { opacity: 0; transform: scale(.96); }
  .op-det .op-led-on { transition: background-color .18s ease-out, border-color .18s ease-out; }
  .op-det:not(.op-det-in) .op-led-on { background: transparent; border-color: var(--line); }
}

/* ---- obsplan observatory: the Logbook tab's dashboard (KPI tiles, all-sky chart, activity /
   type / rating charts, field kit) above the record list. One data hue (--accent) everywhere;
   identity is carried by position and labels, so every theme — including light "ice" — stays
   legible. All cards are plain .card consumers: none define their own ::after, so the reticle
   exclusion list is untouched, and nothing here is a fixed overlay (skin-v2 landmines). */
.op-ob-dash { display: flex; flex-direction: column; gap: 14px; }
.op-ob-head .op-log-filterbar { margin-top: 10px; }

.op-ob-kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  gap: 14px 0; padding: 14px 8px;
}
.op-ob-kpi {
  display: flex; flex-direction: column; gap: 3px; padding: 2px 16px;
  border-left: 1px solid var(--line); min-width: 0;
}
.op-ob-kpi:first-child { border-left: none; }
.op-ob-kv { font-size: 1.9rem; font-weight: 600; line-height: 1.1; }
.op-ob-kvsub { font-size: 1rem; font-weight: 500; color: var(--text-dim); }
.op-ob-kl { font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); }
.op-ob-meter {
  display: block; height: 5px; border-radius: 999px; margin-top: 7px; max-width: 132px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);  /* same-ramp track under the fill */
  overflow: hidden;
}
.op-ob-meter-fill { display: block; height: 100%; border-radius: 999px; background: var(--accent); }

.op-ob-chead {
  display: flex; align-items: baseline; justify-content: space-between; gap: 6px 12px;
  margin-bottom: 10px; flex-wrap: wrap;
}

/* the signature: equatorial all-sky chart */
.op-ob-sky { position: relative; padding: 14px 16px 10px; }
.op-sky { display: block; width: 100%; height: auto; }
.op-sky-frame { fill: color-mix(in srgb, var(--bg0) 55%, transparent); stroke: var(--line-strong); stroke-width: 1; }
.op-sky-grid { stroke: var(--line); stroke-width: 1; }            /* hairline, solid, recessive */
.op-sky-eq { stroke: var(--line-strong); }
.op-sky-lab { fill: var(--text-dim); font-family: var(--font-mono); font-size: 10px; }
.op-sky-pt {
  fill: var(--accent); stroke: var(--bg1); stroke-width: 2;   /* 2px surface ring where dots overlap */
  filter: drop-shadow(0 0 4px var(--glow));                   /* faint star-glow; the chart's one flourish */
}
.op-sky-hit { fill: transparent; cursor: pointer; }
.op-sky-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.op-sky-tip {
  position: absolute; transform: translate(-50%, calc(-100% - 10px)); z-index: 5;
  background: var(--bg1); border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: 7px 11px; pointer-events: none; text-align: left;
  width: max-content; max-width: min(340px, 72vw);   /* wraps instead of escaping the card */
  box-shadow: var(--shadow); line-height: 1.5;
}
.op-sky-tip-below { transform: translate(-50%, 14px); }   /* no room above the dot: flip underneath */
.op-sky-tip b { color: var(--accent); }
/* keyboard escape hatch past the per-cluster tab stops: hidden until focused */
.op-skip {
  position: absolute; left: 16px; top: 44px; z-index: 6; padding: 4px 12px;
  background: var(--bg1); border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  opacity: 0; transform: translateY(-8px); pointer-events: none;
}
.op-skip:focus-visible { opacity: 1; transform: none; pointer-events: auto; outline: 2px solid var(--accent); outline-offset: 2px; }
#op-ob-row:focus { outline: none; }   /* skip-link landing spot, not an interactive element */

/* activity + ratings (SVG columns) and object types (HTML rows) */
.op-ob-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; }
.op-ob-chart { padding: 14px 16px 10px; min-width: 0; }
.op-ob-svg { display: block; width: 100%; height: auto; }
.op-ob-axis { stroke: var(--line-strong); stroke-width: 1; }
.op-ob-bar { fill: color-mix(in srgb, var(--accent) 85%, transparent); }
.op-ob-val { fill: var(--text-dim); font-family: var(--font-mono); font-size: 10px; }
/* the small charts use a tighter viewBox (400u), so labels need a step up to stay readable */
.op-ob-svg .op-sky-lab, .op-ob-svg .op-ob-val { font-size: 12px; }

.op-ob-types { display: flex; flex-direction: column; gap: 8px; padding: 2px 0 6px; }
.op-ob-trow { display: flex; align-items: center; gap: 10px; min-width: 0; }
.op-ob-tlab { flex: 0 0 96px; font-size: 0.8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.op-ob-tbar {
  flex: 1; height: 10px; min-width: 0; border-radius: 0 4px 4px 0; overflow: hidden;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.op-ob-tfill {
  display: block; height: 100%; border-radius: 0 4px 4px 0;   /* rounded data-end, square baseline */
  background: color-mix(in srgb, var(--accent) 85%, transparent);
}
.op-ob-tn { flex: 0 0 auto; color: var(--text-dim); min-width: 2ch; text-align: right; }

.op-ob-kit { padding: 12px 16px; }
.op-ob-chips { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; }
.op-ob-kitlab { text-transform: uppercase; letter-spacing: 0.08em; }
.op-ob-kitlab:not(:first-child) { margin-left: 12px; }
.op-ob-chips .op-nchip b { margin: 0 0 0 2px; font-weight: 600; }

.op-ob-rechead {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin: 4px 2px 0;
}

@media (max-width: 560px) {
  .op-ob-kpis { grid-template-columns: repeat(2, 1fr); }
  .op-ob-tlab { flex-basis: 76px; }
}
@media (max-width: 700px) {
  /* the 760-unit sky viewBox shrinks hard on phones; larger user-units keep the ticks readable */
  .op-sky .op-sky-lab { font-size: 15px; }
}

@media (prefers-reduced-motion: no-preference) {
  /* Entrance plays once: renderLogbook adds .op-ob-enter, the first filter keystroke removes it,
     so repaints never replay the sequence (same trap as the .content>* stagger landmine). */
  .op-ob-enter > * { animation: op-ob-rise 0.45s var(--ease) both; }
  .op-ob-enter > :nth-child(2) { animation-delay: 0.07s; }
  .op-ob-enter > :nth-child(3) { animation-delay: 0.14s; }
  .op-ob-enter > :nth-child(4) { animation-delay: 0.21s; }
  /* per-dot ignite delay is set inline; the inline animation-delay outranks this shorthand */
  .op-ob-enter .op-sky-pt { animation: op-ob-ignite 0.5s ease-out both; }
  .op-ob-enter .op-ob-meter-fill { transform-origin: left; animation: op-ob-sweep 0.9s var(--ease) 0.35s both; }
}
@keyframes op-ob-rise { from { opacity: 0; transform: translateY(10px); } }
@keyframes op-ob-ignite { from { opacity: 0; } }
@keyframes op-ob-sweep { from { transform: scaleX(0); } }

/* APOD archive dome — the "Archive dome" toggle swaps #apod-layout for a DomeGallery React island
   (a full-stage gallery of the last 30 APOD entries). Plain divs, no .card: they must stay outside
   the .card::after reticle exclusion list (jupiter-skin-v2.md landmine #1), and since the swap is a
   hidden-attribute flip on a plain div (not .content>*'s tracked children) it never runs through the
   .content>* entrance stagger (landmine #2) either. Mirrors .op-dome-stage (obsplan sky dome). */
.apod-dome-host { display: flex; flex-direction: column; gap: 8px; }
.apod-dome-stage {
  height: 70vh; min-height: 320px; position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: 14px; background: var(--bg0);
}

/* ---------- product keys deck ---------- */
.pk-bar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 18px; }

/* The key column is the reason people open this deck: give it room and a monospace face so
   look-alike characters (0/O, 1/I) in a 25-character key can be told apart. */
.pk-table code.pk-key {
  font-family: var(--font-mono);
  font-size: 0.86rem;
  letter-spacing: 0.04em;
  color: var(--text);
  word-break: break-all;
}
.pk-keycell { display: flex; align-items: center; gap: 6px; }
.pk-copy { flex: none; }

/* Secondary line inside a cell: licence id under the product, activations under the seat count. */
.pk-sub2 { display: block; font-size: 0.74rem; color: var(--text-dim); letter-spacing: 0.04em; }

/* Row-level trace of a key's custom extras — dim so it reads as metadata, not a normal chip. */
.pk-extra-chip { color: var(--text-dim); border-color: var(--line); background: transparent; }

/* Custom-fields editor inside the add/edit dialog: one name+value+remove row per extra. */
.pk-extra { margin-top: 4px; }
.pk-extra-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.pk-extra-rows { display: flex; flex-direction: column; gap: 6px; }
.pk-extra-row { display: flex; gap: 6px; align-items: center; }
.pk-extra-row .pk-extra-name { flex: 1; min-width: 0; }
.pk-extra-row .pk-extra-value { flex: 1; min-width: 0; }
.pk-extra-row .btn-icon { flex: none; }

.pk-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 14px 2px;
}

/* Imported notes are pre-stripped HTML with meaningful line breaks — keep them, but never let a
   long unbroken key run push the modal wider than the viewport. */
.pk-notes {
  margin: 0;
  max-height: 50vh;
  overflow: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-dim);
}

@media (max-width: 760px) {
  /* Organisation and seats are the droppable columns; product, key and type are not. */
  .pk-table th:nth-child(4), .pk-table td:nth-child(4),
  .pk-table th:nth-child(5), .pk-table td:nth-child(5) { display: none; }
}

/* ---------- SSH deck ---------- */
.ssh-servers-card { margin-bottom: 18px; }
.ssh-server-list { display: flex; flex-direction: column; gap: 10px; }

.ssh-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  flex-wrap: wrap;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg1);
}
.ssh-row-main { min-width: 0; }
.ssh-row-name { font-weight: 600; display: flex; align-items: center; gap: 8px; }
.ssh-pin {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--accent); border: 1px solid var(--accent); border-radius: 999px; padding: 1px 8px 1px 6px;
}
.ssh-pin .icon { width: 12px; height: 12px; }
.ssh-row-actions { display: flex; gap: 8px; flex-wrap: wrap; flex: none; }

.ssh-term-card { display: flex; flex-direction: column; gap: 12px; }
.ssh-term-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.ssh-term-status {
  display: flex; align-items: center; gap: 8px; font-size: 0.85rem; color: var(--text-dim);
  margin-right: auto; margin-left: 8px;
}

/* Same traffic-light dot idiom as .net-status: grey = idle, amber pulse = connecting, green = live. */
.ssh-term-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--text-dim); flex: none; }
.ssh-term-dot.connecting { background: var(--warn); box-shadow: 0 0 6px var(--warn); animation: ssh-pulse 1.1s ease-in-out infinite; }
.ssh-term-dot.on { background: var(--good); box-shadow: 0 0 6px var(--good); }
.ssh-term-dot.off { background: var(--bad); }
@keyframes ssh-pulse { 50% { opacity: 0.35; } }

/* The stage is a plain sized box xterm.js fills; FitAddon reads this element's computed
   height/width once before connect, so no padding goes on the stage itself — padding lives on
   .xterm (which FitAddon explicitly subtracts) so the glyph grid still fits exactly. */
.ssh-term-stage {
  height: 60vh; min-height: 360px; max-height: 640px;
  border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: #0b0f14;
}
.ssh-term-stage .xterm { height: 100%; padding: 8px; }
.ssh-term-stage .xterm-viewport { background-color: transparent !important; }

@media (max-width: 760px) {
  .ssh-row { flex-direction: column; align-items: stretch; }
  .ssh-term-stage { height: 50vh; min-height: 280px; }
}

/* ---------- Weather deck ---------- */
.wx-tiles-grid { grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr)); margin-bottom: 18px; }
.wx-tile { padding: 14px 16px; }
.wx-tile-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.wx-tile-name { font-weight: 600; }
.wx-tile-icon { font-size: 1.4rem; line-height: 1; }
.wx-tile-temp { font-size: 1.8rem; font-weight: 700; font-family: var(--font-mono); margin: 4px 0 2px; }

.wx-search-card { margin-bottom: 18px; }
.wx-search-form { display: flex; gap: 10px; flex-wrap: wrap; }
.wx-search-form .field { flex: 1; min-width: 180px; }

.wx-detail { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line); }
.wx-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.wx-detail-name { font-size: 1.05rem; font-weight: 700; }
.wx-detail-now { display: flex; align-items: center; gap: 12px; margin: 10px 0 14px; flex-wrap: wrap; }
.wx-detail-icon { font-size: 2.2rem; line-height: 1; }
.wx-detail-temp { font-size: 2rem; font-weight: 700; font-family: var(--font-mono); }

.wx-week { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(64px, 100%), 1fr)); gap: 8px; }
.wx-day {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 10px 6px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg1);
}
.wx-day-icon { font-size: 1.2rem; line-height: 1; margin: 2px 0; }
.wx-day-hi { font-weight: 700; }

.wx-favorites-card { margin-bottom: 18px; }
.wx-favorites-list { display: flex; flex-direction: column; gap: 8px; }
.wx-fav-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg1);
}
.wx-fav-main { display: flex; align-items: center; gap: 10px; min-width: 0; }
.wx-fav-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.wx-fav-name { font-weight: 600; }
.wx-fav-temp { font-family: var(--font-mono); }

.wx-map-card { display: flex; flex-direction: column; gap: 12px; }
.wx-map-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.wx-zoom-presets { display: flex; gap: 6px; }
#wx-radar-toggle.active { border-color: var(--accent); color: var(--accent); }

/* Leaflet needs an explicit height on its container or it renders 0px tall. */
.wx-map {
  height: 420px; border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
}

@media (max-width: 760px) {
  .wx-fav-row { flex-wrap: wrap; }
  .wx-detail-head { flex-direction: column; align-items: stretch; }
  .wx-map { height: 320px; }
}

/* ---------- World weather deck (search results list; the UK deck has no equivalent since a
   postcode lookup resolves to exactly one place) ---------- */
.wx-results-list { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.wx-result-row { width: 100%; justify-content: flex-start; text-align: left; gap: 10px; }

/* ---------- Eclipses deck (solar + lunar catalogue over /api/eclipses; shares .exo-stats,
   .table/.table-wrap and .ned-detail with the exoplanets/NED decks rather than duplicating them) ---------- */
.ecl-summary-foot { margin-top: 10px; }

.ecl-timeline-grid { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); margin-bottom: 18px; }
.ecl-timeline-list { display: flex; flex-direction: column; gap: 8px; }
.ecl-timeline-row {
  display: flex; flex-direction: column; gap: 4px; cursor: pointer;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg1);
}
.ecl-timeline-row:hover, .ecl-timeline-row:focus-visible { border-color: color-mix(in srgb, var(--accent) 55%, transparent); outline: none; }
.ecl-timeline-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ecl-timeline-date { font-weight: 600; }

.ecl-kind-badge {
  font-size: 0.66rem; letter-spacing: 0.05em; text-transform: uppercase; font-weight: 700;
  border-radius: 999px; padding: 2px 9px; border: 1px solid var(--line);
}
.ecl-kind-badge.solar { color: #ffb347; border-color: color-mix(in srgb, #ffb347 55%, transparent); }
.ecl-kind-badge.lunar { color: #8ba4ff; border-color: color-mix(in srgb, #8ba4ff 55%, transparent); }

.ecl-type-badge {
  font-size: 0.72rem; font-weight: 600; border-radius: 999px; padding: 2px 10px;
  border: 1px solid var(--line); white-space: nowrap;
}
.ecl-type-badge.type-T { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 55%, transparent); }
.ecl-type-badge.type-A { color: #ffb347; border-color: color-mix(in srgb, #ffb347 55%, transparent); }
.ecl-type-badge.type-H { color: #c084fc; border-color: color-mix(in srgb, #c084fc 55%, transparent); }
.ecl-type-badge.type-P { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.ecl-type-badge.type-N { color: var(--text-dim); border-color: var(--line); }

.ecl-filters { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.ecl-kind-toggle { display: flex; gap: 6px; }
.ecl-kind-toggle .active { border-color: var(--accent); color: var(--accent); }
.ecl-year-field { display: flex; align-items: center; gap: 6px; font-size: 0.86rem; color: var(--text-dim); }
.ecl-year-field .field { width: 90px; }
#ecl-type-filter { min-width: 150px; }
#ecl-catalog-count { margin-bottom: 8px; }
.ecl-row { cursor: pointer; }

.ecl-saros-box { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
.ecl-saros-series { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; max-height: 180px; overflow-y: auto; }
.ecl-saros-member {
  font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 999px; padding: 3px 10px;
}
.ecl-saros-member.current { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, transparent); }

@media (max-width: 640px) {
  .ecl-filters { flex-direction: column; align-items: stretch; }
  .ecl-year-field .field { width: 100%; }
}

/* ===== INVENTORY ===== */
/* Personal-item register for insurance. Item tiles are their own .inv-card class rather than
   .card — they sit inside a .card grid wrapper (jupiter-skin-v2.md landmine #1: a nested .card
   would pick up the reticle ::after and the exclusion list would need to grow for no reason).
   Both dialogs (capture + detail) render into #modal-root via J.modal, so neither is a direct
   .content child — the entrance-stagger landmine (#2) never applies to them. */

.inv-bar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 18px; }
.inv-total { margin-left: auto; white-space: nowrap; }

.inv-empty { padding: 24px 4px; text-align: center; }

.inv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr));
  gap: 14px;
}
.inv-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg1);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.inv-card:hover, .inv-card:focus-visible {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line-strong));
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25);
  outline: none;
}
.inv-thumb { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; background: var(--bg0); }
.inv-thumb-empty {
  display: flex; align-items: center; justify-content: center;
  color: var(--text-dim);
}
.inv-thumb-empty .icon.big { width: 34px; height: 34px; opacity: 0.4; }
.inv-card-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 3px; }
.inv-card-name { font-weight: 600; font-size: 0.92rem; line-height: 1.25; word-break: break-word; }
.inv-card-meta { font-size: 0.76rem; color: var(--text-dim); }
.inv-card-value { font-size: 0.82rem; font-weight: 600; color: var(--accent); margin-top: 2px; }

/* ---- capture dialog (Add item — the one-hand phone flow) ---- */
.inv-capture-actions { display: flex; flex-direction: column; gap: 8px; }
.inv-take { justify-content: center; padding: 16px; font-size: 1rem; }
.inv-preview-strip {
  display: flex; gap: 8px; overflow-x: auto; padding: 2px;
}
.inv-preview-item { position: relative; flex: none; width: 76px; height: 76px; }
.inv-preview-item img {
  width: 100%; height: 100%; object-fit: cover; border-radius: 8px;
  border: 1px solid var(--line); display: block;
}
.inv-preview-remove {
  position: absolute; top: -6px; right: -6px;
  width: 22px; height: 22px; min-height: 0;
  background: var(--bg0); border: 1px solid var(--line-strong); border-radius: 50%;
}
.inv-preview-remove .icon { width: 13px; height: 13px; }

/* ---- shared form layout (detail/edit dialog) ---- */
.inv-form-row { display: flex; gap: 12px; }
.inv-form-row label { flex: 1 1 0; min-width: 120px; }

/* ---- detail dialog photo gallery ---- */
.inv-gallery-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.inv-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 8px;
}
.inv-gallery-item { position: relative; aspect-ratio: 1; }
.inv-gallery-item img {
  width: 100%; height: 100%; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--line); display: block;
}
.inv-gallery-remove {
  position: absolute; top: 4px; right: 4px;
  width: 24px; height: 24px; min-height: 0;
  background: color-mix(in srgb, var(--bg0) 75%, transparent);
}
.inv-gallery-remove .icon { width: 14px; height: 14px; }

/* ---- quick capture dialog (zero-typing burst photography) ---- */
.inv-qc-count { margin: 4px 2px 0; }
.inv-capture-actions.inv-busy { opacity: 0.55; pointer-events: none; transition: opacity 0.15s ease; }

/* ---- inbox panel (sits between the filter bar and the item grid) ---- */
.inv-inbox { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }
.inv-inbox-head h3 { margin: 0 0 2px; font-size: 0.98rem; }
.inv-inbox-head p { margin: 0; }
.inv-inbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 8px;
}
.inv-inbox-thumb {
  position: relative; aspect-ratio: 1; min-height: 44px;
  border-radius: 8px; overflow: hidden; cursor: pointer;
  border: 2px solid transparent;
  transition: border-color 0.15s ease;
}
.inv-inbox-thumb:hover, .inv-inbox-thumb:focus-visible { border-color: var(--line-strong); outline: none; }
.inv-inbox-thumb.selected { border-color: var(--accent); }
.inv-inbox-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.inv-inbox-check {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent) 45%, transparent);
  color: #fff;
}
.inv-inbox-check .icon { width: 26px; height: 26px; }
.inv-inbox-actions { display: flex; gap: 8px; flex-wrap: wrap; }

@media (max-width: 640px) {
  .inv-bar { flex-direction: column; align-items: stretch; }
  .inv-total { margin-left: 0; text-align: center; }
  .inv-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .inv-form-row { flex-direction: column; gap: 13px; }
  /* "Take photo" is the dominant control on a phone — full-width, tall, first thing tapped. */
  .inv-take { min-height: 52px; }
  /* Quick capture is the hero flow on a phone: full-width when the deck-head stacks. */
  #inv-quick { width: 100%; justify-content: center; }
  .inv-inbox-grid { grid-template-columns: repeat(3, 1fr); }
  .inv-inbox-actions { flex-direction: column; align-items: stretch; }
}

/* ===== QUAKES ===== */
/* UK / New Zealand / Global earthquake decks share one set of regions (tabs, filters, stats, map,
   list) re-rendered per tab by quakes.js. Shares .tabs/.tab, .chip, .exo-stats/.exo-stat and
   .ned-detail with other decks rather than duplicating them — only what is genuinely new to this
   deck gets a qk- prefix. */

.qk-tabs { margin-bottom: 14px; }

.qk-filters { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.qk-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.qk-chip.active { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
#qk-days { min-width: 110px; margin-left: auto; }

.qk-stats-card { margin-bottom: 18px; }
#qk-source { margin-top: 10px; }

.qk-map-card { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }
/* Leaflet needs an explicit height on its container or it renders 0px tall. */
.qk-map { height: 420px; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }

.qk-list { display: flex; flex-direction: column; gap: 8px; }
.qk-row {
  display: flex; align-items: center; gap: 12px; cursor: pointer;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg1);
}
.qk-row:hover, .qk-row:focus-visible { border-color: color-mix(in srgb, var(--accent) 55%, transparent); outline: none; }
.qk-row-main { flex: 1 1 auto; min-width: 0; }
.qk-row-place { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qk-row-meta { margin-top: 2px; }
.qk-row-flags { display: flex; gap: 6px; flex-wrap: wrap; flex: none; }

.qk-mag-badge {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  min-width: 46px; height: 30px; padding: 0 8px;
  font-family: var(--font-mono); font-weight: 700; font-size: 0.9rem;
  border-radius: 999px; border: 1px solid var(--line);
}
.qk-mag-badge.lo { color: var(--text-dim); border-color: var(--line); }
.qk-mag-badge.mid { color: #ffb347; border-color: color-mix(in srgb, #ffb347 55%, transparent); }
.qk-mag-badge.hi { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 55%, transparent); }

.qk-flag {
  font-size: 0.66rem; letter-spacing: 0.05em; text-transform: uppercase; font-weight: 700;
  border-radius: 999px; padding: 2px 9px; border: 1px solid var(--line); color: var(--text-dim);
  white-space: nowrap;
}
.qk-flag.tsunami { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 55%, transparent); }
.qk-flag.alert-green { color: var(--good); border-color: color-mix(in srgb, var(--good) 55%, transparent); }
.qk-flag.alert-yellow { color: #ffd24a; border-color: color-mix(in srgb, #ffd24a 55%, transparent); }
.qk-flag.alert-orange { color: #ffb347; border-color: color-mix(in srgb, #ffb347 55%, transparent); }
.qk-flag.alert-red { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 55%, transparent); }

/* ---- nz-only volcano section (shares .grid/.card/.qk-flag/.qk-row/.qk-mag-badge/.stat-label
   above rather than duplicating them — only the genuinely new bits get their own rule) ---- */
.qk-volc-grid { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.qk-volc-card { cursor: pointer; display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; }
.qk-volc-card:hover, .qk-volc-card:focus-visible { border-color: color-mix(in srgb, var(--accent) 55%, transparent); outline: none; }
.qk-volc-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.qk-volc-title { font-weight: 600; }
.qk-volc-activity { margin: 0; color: var(--text-dim); font-size: 0.86rem; }
.qk-volc-source { margin-top: 4px; }

.qk-volc-cam-head { margin: 16px 0 8px; }
.qk-volc-cams { grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); }
.qk-volc-cam { display: flex; flex-direction: column; gap: 6px; }
.qk-volc-cam img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--line); display: block;
}

.qk-volc-detail-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }

/* recentQuakes rows in the volcano detail modal are read-only (no per-quake detail endpoint) */
.qk-row-static { cursor: default; }
.qk-row-static:hover, .qk-row-static:focus-visible { border-color: var(--line); }

@media (max-width: 640px) {
  .qk-tabs { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .qk-tabs .tab { flex: 1 1 0; text-align: center; }
  .qk-filters { align-items: stretch; }
  #qk-days { margin-left: 0; width: 100%; }
  .qk-map { height: 40vh; }
  .qk-row { padding: 12px 14px; }
  .qk-mag-badge { min-height: 44px; }
}

/* ===== MESSIER ===== */
/* The 110 Messier objects as a picture catalogue: a thumbnail grid plus a J.modal() detail holding
   the full image, the prose and its provenance. Shares .grid, .card, .chip, .field, .btn, .empty,
   .stat-label and .ned-detail/.ned-cross with the other decks rather than duplicating them — only
   what is genuinely new to this deck gets an ms- prefix.
   .ms-card is a plain .card and is NOT in the .card::after exclusion list, so it inherits the shared
   corner-bracket hover reticle exactly like .cosmos-card does — .ms-thumb adds no decoration of its
   own. The full-image viewer is a J.modal, which renders into #modal-root outside .content, so the
   .content > * entrance stagger never replays over it. No clip-path anywhere here: .ms-card's
   hover/focus state is a border-color swap with outline explicitly set to none (not a browser
   outline), so there is nothing for a clip-path to cut off. */

.ms-filters { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.ms-filters .field { min-width: 170px; }
#ms-count { margin-top: 10px; }

.ms-grid { grid-template-columns: repeat(auto-fill, minmax(min(210px, 100%), 1fr)); gap: 14px; }
.ms-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; cursor: pointer; }
.ms-card:hover, .ms-card:focus-visible { border-color: color-mix(in srgb, var(--accent) 55%, transparent); outline: none; }

.ms-thumb {
  position: relative; width: 100%; aspect-ratio: 1 / 1; overflow: hidden;
  background: radial-gradient(circle at 50% 40%, #1b2536, #070b14);
}
.ms-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .25s; }
.ms-card:hover .ms-thumb img { transform: scale(1.05); }

.ms-body { padding: 11px 13px 13px; display: flex; flex-direction: column; gap: 6px; }
.ms-card-head { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.ms-name { font-weight: 600; font-size: 0.95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ms-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.ms-meta .sep { opacity: 0.4; }

/* ---- detail modal ---- */
/* The 540px modal default barely beats the 420px thumbnail the user just clicked, so a bundled image
   (up to 1200px) needs real room — opt-in class so no other modal is affected, same mechanism as
   .op-pick-panel. */
.modal-panel.ms-full-panel { max-width: 1200px; }
.ms-full {
  background: #05080f; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center; max-height: 60vh; overflow: hidden;
}
/* max-height on the IMAGE itself, not just its container: a container cap alone still lets a tall
   image overflow into a nested scrollbar on a short laptop viewport, hiding most of the picture
   behind a scroll gesture instead of fitting it on screen. */
.ms-full img { max-width: 100%; max-height: 60vh; width: auto; height: auto; object-fit: contain; display: block; }
.ms-credit { margin: 8px 0 0; }
.ms-summary { margin: 14px 0 0; }
.ms-desc p { margin: 10px 0 0; color: var(--text-dim); }
.ms-section { margin: 18px 0 8px; }
/* Provenance is content, not a footnote: it gets its own ruled-off block at the foot of the
   detail so a Wikipedia summary can never be mistaken for the NASA text above it. */
.ms-prov { margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--line); }
.ms-prov p { margin: 6px 0 0; }

@media (max-width: 640px) {
  .ms-filters { align-items: stretch; }
  .ms-filters .field { min-width: 0; }
  .ms-grid { grid-template-columns: repeat(auto-fill, minmax(min(140px, 100%), 1fr)); gap: 10px; }
  .ms-full { max-height: 46vh; }
  .ms-full img { max-height: 46vh; }
}

/* ===== NGC / IC ===== */
/* The whole OpenNGC catalogue as a search-first browser: a paged thumbnail grid plus a J.modal()
   detail holding the picture, the catalogue record and the object's sky neighbours. Shares .grid,
   .card, .chip, .field, .check, .btn, .empty, .stat-label and .ned-detail/.ned-cross with the other
   decks rather than duplicating them — only what is genuinely new to this deck gets an ngc- prefix,
   and the card/thumb geometry deliberately matches the Messier deck so the two read as one family.
   .ngc-card is a plain .card and is NOT in the .card::after exclusion list, so it inherits the shared
   corner-bracket hover reticle exactly like .ms-card does. The detail viewer is a J.modal, which
   renders into #modal-root outside .content, so the .content > * entrance stagger never replays over
   it. No clip-path anywhere here: the card's hover/focus state is a border-color swap with outline
   explicitly set to none, so there is nothing for a clip-path to cut off. */

.ngc-filters { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.ngc-filters .field { min-width: 170px; }
#ngc-count { margin-top: 10px; }

.ngc-grid { grid-template-columns: repeat(auto-fill, minmax(min(210px, 100%), 1fr)); gap: 14px; }
.ngc-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; cursor: pointer; }
.ngc-card:hover, .ngc-card:focus-visible { border-color: color-mix(in srgb, var(--accent) 55%, transparent); outline: none; }

/* The tile keeps its dark field whether or not a picture ever arrives: most of this catalogue has no
   bundled frame, its survey cutout is fetched lazily, and a cutout that 404s is hidden by core.js's
   data-hide-on-error handler. An empty square of sky is the honest fallback, not a broken icon. */
.ngc-thumb {
  position: relative; width: 100%; aspect-ratio: 1 / 1; overflow: hidden;
  background: radial-gradient(circle at 50% 40%, #1b2536, #070b14);
}
.ngc-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .25s; }
.ngc-card:hover .ngc-thumb img { transform: scale(1.05); }

.ngc-body { padding: 11px 13px 13px; display: flex; flex-direction: column; gap: 6px; }
.ngc-card-head { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.ngc-name { font-weight: 600; font-size: 0.95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ngc-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.ngc-meta .sep { opacity: 0.4; }

.ngc-pager { display: flex; justify-content: center; align-items: center; gap: 14px; padding: 16px 0 4px; flex-wrap: wrap; }
/* In-flight page or filter change: keep the current tiles on screen, dimmed and inert, instead of
   flashing a skeleton on every keystroke. Same treatment as the Directory deck's table. */
#ngc-grid, #ngc-pager { transition: opacity 0.15s ease; }
#ngc-grid.refreshing, #ngc-pager.refreshing { opacity: 0.55; pointer-events: none; }

/* ---- detail modal ---- */
/* Same reasoning as .ms-full-panel: the 540px modal default barely beats the tile the user just
   clicked, so the picture needs real room. Opt-in class, so no other modal is affected. */
.modal-panel.ngc-full-panel { max-width: 1000px; }
.ngc-full {
  background: #05080f; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center; max-height: 52vh; overflow: hidden;
}
/* max-height on the IMAGE as well as its container: a container cap alone still lets a tall image
   overflow into a nested scrollbar on a short viewport, hiding most of the picture behind a scroll. */
.ngc-full img { max-width: 100%; max-height: 52vh; width: auto; height: auto; object-fit: contain; display: block; }
.ngc-section { margin: 18px 0 8px; }

/* Neighbours are a navigation control, so each row is a real <button> — keyboard-reachable, and
   styled as a row rather than as a button so it reads as a list of objects. */
.ngc-near { display: flex; flex-direction: column; gap: 6px; }
.ngc-near-row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: transparent; color: var(--text); font: inherit; cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.ngc-near-row:hover, .ngc-near-row:focus-visible {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  outline: none;
}
.ngc-near-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Provenance is content, not a footnote: this deck's pictures are not all local and the block says
   so per object, ruled off at the foot of the detail. */
.ngc-prov { margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--line); }
.ngc-prov p { margin: 6px 0 0; }

@media (max-width: 640px) {
  .ngc-filters { align-items: stretch; }
  .ngc-filters .field { min-width: 0; }
  .ngc-grid { grid-template-columns: repeat(auto-fill, minmax(min(140px, 100%), 1fr)); gap: 10px; }
  .ngc-full { max-height: 40vh; }
  .ngc-full img { max-height: 40vh; }
  .ngc-near-row { flex-wrap: wrap; }
}
