
:root {
  --bg-0: #16171f;
  --bg-1: #1b1c26;
  --bg-2: #21222e;
  --bg-3: #292a3a;
  --border: #35364a;
  --border-soft: #2a2b3a;
  --text: #e8e8ee;
  --text-dim: #9a9ab0;
  --accent: #6c8cff;
  --accent-soft: rgba(108, 140, 255, 0.15);
  --green: #2e9c5b;
  --green-hover: #34b167;
  --red: #d64550;
  --red-hover: #e5555f;
  --radius: 7px;
  --shadow-1: 0 2px 8px rgba(0, 0, 0, 0.25);
  --shadow-2: 0 8px 28px rgba(0, 0, 0, 0.5);
}

/* Light mode — toggled via #btn-theme-toggle (js/app.js), persisted in
   localStorage. Only the app chrome (toolbars, panels, code editor,
   console) switches; simulated hardware (breadboard, LEDs, sensors, etc.)
   keeps its own realistic colors regardless of theme, same as a real
   breadboard doesn't change color under different room lighting. */
html[data-theme="light"] {
  --bg-0: #f4f5f9;
  --bg-1: #ffffff;
  --bg-2: #eceef5;
  --bg-3: #e2e5f0;
  --border: #d2d5e2;
  --border-soft: #e1e3ee;
  --text: #1c1d29;
  --text-dim: #62637a;
  --accent: #3f5fe0;
  --accent-soft: rgba(63, 95, 224, 0.12);
  --green: #2e9c5b;
  --green-hover: #278a4f;
  --red: #d64550;
  --red-hover: #c53a45;
  --shadow-1: 0 2px 8px rgba(30, 34, 60, 0.08);
  --shadow-2: 0 8px 28px rgba(30, 34, 60, 0.16);
}

html[data-theme="light"] .console-output .err {
  color: #c62828;
}
.brand {
    display: flex;
    align-items: center;
}

.logo {
    height: 38px;   /* Adjust as needed */
    width: auto;
    display: block;
}

* { box-sizing: border-box; }
html, body {
  margin: 0;
  height: 100%;
  background: var(--bg-0);
  color: var(--text);
  font-family: "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 18px;
  background: var(--bg-1);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-1);
  position: relative;
  z-index: 10;
}
.topbar h1 {
  font-size: 15px;
  font-weight: 600;
  margin: 0;
  letter-spacing: 0.2px;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 8px;
}
.topbar-actions { display: flex; align-items: center; gap: 8px; }
.topbar-actions .divider { width: 1px; height: 22px; background: var(--border); margin: 0 4px; }

.btn {
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 7px 14px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
}
.btn:hover { background: #34364a; border-color: #4a4c66; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }
.btn-run { background: var(--green); border-color: var(--green-hover); color: #eafff1; }
.btn-run:hover { background: var(--green-hover); }
.btn-stop { background: var(--red); border-color: var(--red-hover); color: #fff0f0; }
.btn-stop:hover { background: var(--red-hover); }
.btn-secondary { background: transparent; }
.btn-secondary:hover { background: var(--bg-3); }
.btn-tiny { padding: 2px 8px; font-size: 11px; margin-left: 8px; }
.btn-danger { background: var(--red); border-color: var(--red-hover); width: 100%; margin-top: 8px; margin-left: 0; }
.btn-danger:hover { background: var(--red-hover); }
.btn-flash-error { background: var(--red) !important; border-color: var(--red-hover) !important; }

select {
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 6px 8px;
  font-size: 13px;
  cursor: pointer;
}

.color-field {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: #9a9ab0;
}
.color-field input[type="color"] {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: none;
  cursor: pointer;
}

.tool-group {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 3px 6px;
  background: var(--bg-2);
  border: 1px solid var(--border-soft);
  border-radius: 7px;
}
.tool-group-label { font-size: 10px; color: var(--text-dim); margin-right: 2px; }
.zoom-label {
  font-size: 11px;
  color: var(--text-dim);
  min-width: 38px;
  text-align: center;
  cursor: pointer;
  user-select: none;
}
.zoom-label:hover { color: var(--text); }

.status {
  font-size: 11px;
  font-weight: 600;
  color: #f0b23a;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(240, 178, 58, 0.12);
}
.status.ready { color: #4fd884; background: rgba(79, 216, 132, 0.12); }
.status.error { color: #ff6b6b; background: rgba(255, 107, 107, 0.12); }
.status.status-warn { color: #ff6b6b; background: rgba(255, 107, 107, 0.12); }

.view-toggle {
  display: flex;
  padding: 2px;
  background: var(--bg-2);
  border: 1px solid var(--border-soft);
  border-radius: 8px;
}
.view-toggle-btn {
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--text-dim);
  padding: 5px 10px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
}
.view-toggle-btn:hover { color: var(--text); }
.view-toggle-btn.active { background: var(--bg-3); color: var(--text); box-shadow: var(--shadow-1); }

.layout {
  display: flex;
  align-items: stretch;
  height: calc(100vh - 49px);
  min-height: 0;
}
/* View modes: Design/Code hide the other panel and its splitter, and let
   the remaining one fill the row — !important is needed here since the
   splitter-drag JS sets an inline flex-basis on these panels directly. */
.layout.mode-design .code-panel,
.layout.mode-code .board-panel,
.layout.mode-design #splitter-main,
.layout.mode-code #splitter-main {
  display: none !important;
}
.layout.mode-design .board-panel,
.layout.mode-code .code-panel {
  flex: 1 1 auto !important;
}

.splitter {
  flex: 0 0 auto;
  background: var(--border-soft);
  position: relative;
  z-index: 5;
}
.splitter::after {
  content: "";
  position: absolute;
  background: var(--border);
  border-radius: 2px;
}
.splitter-v {
  width: 6px;
  cursor: col-resize;
}
.splitter-v::after { top: 50%; left: 1.5px; width: 3px; height: 40px; transform: translateY(-50%); }
.splitter-h {
  height: 6px;
  cursor: row-resize;
}
.splitter-h::after { left: 50%; top: 1.5px; height: 3px; width: 40px; transform: translateX(-50%); }
.splitter:hover, .splitter.dragging { background: var(--accent-soft); }
.splitter:hover::after, .splitter.dragging::after { background: var(--accent); }

.board-panel {
  display: flex;
  flex-direction: column;
  min-width: 320px;
  flex: 1 1 60%;
  min-height: 0;
}
.board-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg-1);
}
.hint { font-size: 12px; 
  color: var(--text-dim); 
  /* margin-left: auto;  */
  /* text-align: right;  */
  /* max-width: 380px;  */
}

.board-scroll { flex: 1; overflow: auto; padding: 24px; min-height: 0; }
.board {
  position: relative;
  width: 900px;
  min-height: 600px;
  transform-origin: 0 0;
}

/* Pi board: vertical/portrait, GPIO header on its right edge facing the
   breadboard beside it — so wire runs stay short and direct. */
.pi-board {
  position: absolute;
  top: 0;
  left: 0;
  width: 320px;
  height: 620px;
  z-index: 2;
  transform-origin: 160px 310px;
  
}
.pi-board-art,
.pi-photo {
  position: absolute;
  inset: 0;
  width: 330px;
  height: 500px;
  display: block;
  border-radius: 16px;
}
.pi-photo {
  object-fit: cover;
  display: block; /* shown automatically via JS only if assets/pi-photo.png actually loads */
}

.pin-strip {
  position: absolute;
    left: 294px;
    top: 40px;
    width: 32px;
    height: 282px;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(20, 1fr);
    gap: 1px;
    z-index: 3;
}
/* Fixed CSI connector anchor — positioned to sit over the decorative
   connector rect drawn in the Pi board SVG (viewBox x=234 y=170 w=8 h=46,
   scaled to the art's actual 330x500 rendered size within the 320x620
   .pi-board box: 234*330/320≈241, 170*500/620≈137). Not interactive —
   only exists so nodeCenter() has a stable node-id to draw the ribbon
   cable to/from, the same way it targets GPIO pins. */
.csi-port {
  position: absolute;
  left: 239px;
  top: 135px;
  width: 12px;
  height: 42px;
  border-radius: 2px;
  background: linear-gradient(180deg, #cfcfcf, #9a9a9a);
  border: 1px solid #555;
  z-index: 3;
  pointer-events: none;
}
.pin {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  font-size: 8px;
  font-weight: 600;
  line-height: 1;
  color: #eaffee;
  cursor: pointer;
  border-radius: 3px;
  user-select: none;
}
.pin:hover { background: rgba(255,255,255,0.18); }
.pin .dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #f4d160;
  border: 1.5px solid #8a7000;
  flex-shrink: 0;
}
.pin[data-kind="power"] .dot { background: #ff6b6b; border-color: #8a0000; }
.pin[data-kind="ground"] .dot { background: #666; border-color: #222; }
.pin[data-kind="gpio"] .dot { background: #61dafb; border-color: #0f6a8a; }
.pin.selected .dot { outline: 2px solid #fff; }
.pin.active .dot { background: #ffd54f; box-shadow: 0 0 8px 2px #ffd54f; }
.pin-label { white-space: nowrap; display: none;}

.pin-tooltip {
  position: fixed;
  z-index: 60;
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  width: 104px;
  padding: 8px 6px 7px;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow-2);
  pointer-events: none;
  text-align: center;
}
.pin-tooltip-dot {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  margin-bottom: 4px;
  border: 2px solid;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.04);
  transition: background 0.1s, box-shadow 0.1s;
}
.pin-tooltip-dot-power { background: #ff6b6b; border-color: #8a0000; }
.pin-tooltip-dot-ground { background: #666; border-color: #222; }
.pin-tooltip-dot-gpio { background: #61dafb; border-color: #0f6a8a; }
.pin-tooltip-dot.active {
  background: #ffd54f;
  border-color: #8a7000;
  box-shadow: 0 0 12px 3px rgba(255, 213, 79, 0.7);
}
.pin-tooltip-name { font-size: 12px; font-weight: 700; color: var(--text); }
.pin-tooltip-meta { font-size: 9px; color: var(--text-dim); }
.pin-tooltip-kind {
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  background: var(--bg-3);
  padding: 1px 6px;
  border-radius: 999px;
  margin-top: 2px;
}
.pin-tooltip-state {
  font-size: 9px;
  font-weight: 600;
  margin-top: 3px;
  padding: 2px 7px;
  border-radius: 999px;
}
.pin-tooltip-state.idle { color: var(--text-dim); background: var(--bg-3); }
.pin-tooltip-state.low { color: var(--text-dim); background: var(--bg-3); }
.pin-tooltip-state.high { color: #1a1200; background: #ffd54f; }

.tool-group.pin-legend { gap: 10px; font-size: 10px; color: var(--text-dim); cursor: help; }
.pin-legend span { display: inline-flex; align-items: center; gap: 4px; }
.pin-legend .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.pin-legend .dot.power { background: #ff6b6b; }
.pin-legend .dot.ground { background: #666; }
.pin-legend .dot.gpio { background: #61dafb; }

.breadboard {
  position: absolute;
  top: 0;
  left: 350px;
  right: 0;
  min-height: 620px;
  background:
    radial-gradient(circle, rgba(0, 0, 0, 0.16) 1.1px, transparent 1.3px) 0 0/16px 16px,
    #d8cdb0;
  border-radius: 8px;
  border: 2px solid #8f8567;
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.15);
}
.breadboard-label {
  position: absolute;
  top: 6px;
  left: 10px;
  font-size: 11px;
  color: #6b6248;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  background: #d8cdb0;
  padding: 0 4px;
}

.component {
  --led-color: #ff3b3b;
  --seg-color: #ff3b3b;
  position: absolute;
  cursor: grab;
  user-select: none;
  touch-action: none;
}
.component:active { cursor: grabbing; }
/* Passive "tag" pieces (RFID Card, Fingerprint Hand) always render above
   the reader/sensor they're tapped against, regardless of which was added
   to the board first — otherwise whichever got added second would win by
   DOM order alone, and a reader dropped on top of an existing card would
   visually bury it. */
.comp-rfidcard,
.comp-fingerhand {
  z-index: 2;
}
.comp-label {
  text-align: center;
  font-size: 10px;
  color: #333;
  margin-top: 4px;
}
.lcd-addr { color: #6b6248; font-size: 9px; }
.terminal-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 6px;
}
.terminal-row.wrap { max-width: 200px; margin-left: auto; margin-right: auto; }
.terminal {
  position: relative;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #b0a074;
  border: 2px solid #6b6248;
  cursor: crosshair;
  flex-shrink: 0;
}
.terminal.selected { outline: 2px solid #fff; }
.terminal.anode { background: #e8b923; }
.terminal.cathode { background: #999; }
.terminal-tag {
  position: absolute;
  top: 17px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 7px;
  color: #4a4330;
  white-space: nowrap;
  pointer-events: none;
}
.comp-remove,
.comp-gear {
  position: absolute;
  top: -8px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  color: #fff;
  border: none;
  font-size: 10px;
  line-height: 1;
  cursor: pointer;
  display: none;
  z-index: 4;
}
.comp-remove { right: -8px; background: #b71c1c; }
.comp-gear { left: -8px; background: #33344a; }
.component:hover .comp-remove,
.component:hover .comp-gear { display: block; }
/* While a script is running, its wiring/layout is exactly what it's
   reading/driving — remove/settings/resize are hidden outright (not just
   blocked on click) rather than inviting a click that goes nowhere. */
body.script-running .comp-remove,
body.script-running .comp-gear,
body.script-running .comp-resize-handle {
  display: none !important;
}
.comp-remove-blocked {
  animation: compRemoveShake 0.4s ease;
}
.blocked-message {
  position: fixed;
  z-index: 70;
  background: #3a1414;
  border: 1px solid #6b2020;
  color: #ff9a9a;
  font-size: 12px;
  font-weight: 600;
  padding: 6px 10px;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  animation: blockedMessageFade 1.6s ease forwards;
}
@keyframes blockedMessageFade {
  0% { opacity: 0; transform: translateY(4px); }
  10%, 80% { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-4px); }
}
@keyframes compRemoveShake {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-3px); }
  40%, 80% { transform: translateX(3px); }
}

/* Corner grip, bottom-right of every component — drag to scale the whole
   thing up/down via a CSS transform (see the pointerdown handler in
   createComponentElement). Same show-on-hover treatment as the
   remove/gear buttons. */
.comp-resize-handle {
  position: absolute;
  bottom: -8px;
  right: -8px;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  background: #33344a;
  color: #cfd0e0;
  font-size: 9px;
  line-height: 16px;
  text-align: center;
  cursor: nwse-resize;
  display: none;
  z-index: 4;
  user-select: none;
}
.component:hover .comp-resize-handle { display: block; }

/* Bottom-right grip on the breadboard itself — drag to grow it past what
   the current components need (dragging back down gives up that extra
   room once components no longer need it, see updateBoardSize()). */
.breadboard-resize-handle {
  position: absolute;
  right: -12px;
  bottom: -12px;
  width: 22px;
  height: 22px;
  border-radius: 5px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  cursor: nwse-resize;
  z-index: 6;
}
.breadboard-resize-handle::before {
  content: "⤡";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: var(--text-dim);
}
.breadboard-resize-handle:hover { background: var(--bg-2); }

/* --- LED: 5mm dome with glossy highlight, flat cathode-side rim, and two legs --- */
.led-bulb {
  position: relative;
  width: 34px;
  height: 42px;
  margin: 4px auto 0;
  border-radius: 50% 50% 46% 46% / 62% 62% 38% 38%;
  background: radial-gradient(
    circle at 32% 24%,
    color-mix(in srgb, var(--led-color) 32%, #6a6a6a) 0%,
    color-mix(in srgb, var(--led-color) 42%, #333) 45%,
    color-mix(in srgb, var(--led-color) 55%, #0a0a0a) 100%
  );
  border: 1px solid color-mix(in srgb, var(--led-color) 25%, #050505);
  box-shadow:
    inset -4px -5px 7px rgba(0, 0, 0, 0.45),
    inset 2px 2px 3px rgba(255, 255, 255, 0.12);
  transition: background 0.1s, box-shadow 0.1s;
  overflow: hidden;
}
.led-glint {
  position: absolute;
  top: 14%;
  left: 20%;
  width: 30%;
  height: 20%;
  background: rgba(255, 255, 255, 0.6);
  border-radius: 50%;
  filter: blur(0.5px);
}
.led-bulb::after {
  /* flat notch marking the cathode side, like a real 5mm LED's rim */
  content: "";
  position: absolute;
  left: 1px;
  bottom: 22%;
  width: 2px;
  height: 26%;
  background: rgba(0, 0, 0, 0.4);
  border-radius: 1px;
}
.comp-led.on .led-bulb {
  background: radial-gradient(
    circle at 32% 24%,
    #ffffff 0%,
    var(--led-color) 50%,
    var(--led-color) 100%
  );
  box-shadow:
    0 0 22px 9px color-mix(in srgb, var(--led-color) 65%, transparent),
    inset -3px -4px 6px rgba(0, 0, 0, 0.2);
}
.led-legs {
  display: flex;
  justify-content: center;
  gap: 13px;
  margin-top: -1px;
}
.leg {
  width: 3px;
  background: linear-gradient(180deg, #e2e2e2, #8a8a8a);
  border-radius: 0 0 1px 1px;
}
.leg-anode { height: 24px; }
.leg-cathode { height: 15px; margin-top: 9px; }

/* --- 7-segment display: black epoxy DIP body, dark-red unlit segments --- */
.sevenseg-body {
  width: 62px;
  margin: 6px auto 0;
  padding: 9px 6px 7px;
  background: linear-gradient(155deg, #333 0%, #161616 55%, #030303 100%);
  border-radius: 5px;
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.1),
    inset 0 -2px 3px rgba(0, 0, 0, 0.5),
    0 2px 4px rgba(0, 0, 0, 0.35);
}
.sevenseg-digit {
  position: relative;
  width: 50px;
  height: 90px;
}
.seg {
  position: absolute;
  background: color-mix(in srgb, var(--seg-color) 16%, #250808);
  transition: background 0.08s, box-shadow 0.08s;
}
.seg.on {
  background: var(--seg-color);
  box-shadow: 0 0 7px 1.5px color-mix(in srgb, var(--seg-color) 75%, transparent);
}
.seg-a { top: 8px; left: 10px; width: 30px; height: 7px; clip-path: polygon(12% 0, 88% 0, 100% 100%, 0 100%); }
.seg-g { top: 41px; left: 10px; width: 30px; height: 7px; clip-path: polygon(6% 50%, 18% 0, 82% 0, 94% 50%, 82% 100%, 18% 100%); }
.seg-d { top: 74px; left: 10px; width: 30px; height: 7px; clip-path: polygon(0 0, 100% 0, 88% 100%, 12% 100%); }
.seg-f { top: 15px; left: 6px; width: 7px; height: 26px; clip-path: polygon(0 12%, 100% 0, 100% 88%, 0 100%); }
.seg-b { top: 15px; left: 37px; width: 7px; height: 26px; clip-path: polygon(0 0, 100% 12%, 100% 100%, 0 88%); }
.seg-e { top: 48px; left: 6px; width: 7px; height: 26px; clip-path: polygon(0 12%, 100% 0, 100% 88%, 0 100%); }
.seg-c { top: 48px; left: 37px; width: 7px; height: 26px; clip-path: polygon(0 0, 100% 12%, 100% 100%, 0 88%); }
.seg-dp { top: 76px; left: 42px; width: 7px; height: 7px; border-radius: 50%; }

/* --- LCD: green PCB frame, pixel-grid glass, contrast pot, mounting holes --- */
.lcd-module {
  position: relative;
  width: 226px;
  margin: 0 auto;
  padding: 8px;
  background: linear-gradient(160deg, #12854a, #095c31 70%);
  border: 1px solid #063d20;
  border-radius: 6px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.35);
}
.lcd-hole {
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #cfcfcf;
  border: 1px solid #6b6b6b;
}
.lcd-hole.tl { top: 4px; left: 4px; }
.lcd-hole.tr { top: 4px; right: 4px; }
.lcd-hole.bl { bottom: 4px; left: 4px; }
.lcd-hole.br { bottom: 4px; right: 4px; }
.lcd-screen {
  width: 100%;
  height: 58px;
  background: #17307a;
  border: 2px solid #0a1a4a;
  border-radius: 3px;
  padding: 4px;
}
.lcd-grid {
  display: grid;
  grid-template-columns: repeat(16, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 1.5px;
  width: 100%;
  height: 100%;
}
.lcd-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.18);
  border-radius: 1px;
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 10px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.08);
  overflow: hidden;
  transition: color 0.15s;
}
.lcd-screen.powered .lcd-cell {
  color: #eaf1ff;
  text-shadow: 0 0 3px rgba(150, 190, 255, 0.6);
}
.lcd-pot {
  position: absolute;
  bottom: 7px;
  right: 9px;
  width: 11px;
  height: 11px;
  background: #2b6fd1;
  border: 1px solid #123a80;
  border-radius: 2px;
}
.lcd-pot::after {
  content: "";
  position: absolute;
  top: 5px;
  left: 1px;
  width: 9px;
  height: 1.5px;
  background: #0a1a4a;
}

/* --- TM1637 4-digit display: black epoxy body, glowing digit cells --- */
.tm1637-body {
  width: 110px;
  margin: 8px auto 0;
  padding: 10px 8px;
  background: linear-gradient(155deg, #333 0%, #161616 55%, #030303 100%);
  border-radius: 6px;
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.1),
    inset 0 -2px 3px rgba(0, 0, 0, 0.5),
    0 2px 4px rgba(0, 0, 0, 0.35);
}
.tm1637-screen {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: #0a0a0a;
  border-radius: 3px;
  padding: 8px 4px;
}
.tm1637-digit {
  width: 16px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 17px;
  font-weight: 700;
  color: color-mix(in srgb, var(--seg-color) 18%, #250808);
  background: rgba(255, 255, 255, 0.02);
  border-radius: 2px;
  transition: color 0.1s;
}
.tm1637-screen.powered .tm1637-digit {
  color: var(--seg-color);
  text-shadow: 0 0 6px color-mix(in srgb, var(--seg-color) 75%, transparent);
}
.tm1637-colon {
  font-size: 16px;
  font-weight: 700;
  color: #2a2a2a;
  transition: color 0.1s;
}
.tm1637-colon.on {
  color: var(--seg-color);
  text-shadow: 0 0 6px color-mix(in srgb, var(--seg-color) 75%, transparent);
}

/* --- RFID reader: coil antenna glows green when powered, gold when a card is present --- */
.rfid-body {
  width: 90px;
  height: 90px;
  margin: 6px auto 0;
  background: linear-gradient(160deg, #12854a, #094a26 70%);
  border: 2px solid #063d20;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: box-shadow 0.15s;
}
.rfid-coil {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 3px solid #0a5c34;
  position: relative;
  transition: border-color 0.15s;
}
.rfid-coil::before,
.rfid-coil::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  border: 3px solid #0a5c34;
  transition: border-color 0.15s;
}
.rfid-coil::before { inset: 9px; }
.rfid-coil::after { inset: 19px; }
.rfid-body.powered .rfid-coil,
.rfid-body.powered .rfid-coil::before,
.rfid-body.powered .rfid-coil::after {
  border-color: #4fd884;
}
.rfid-body.active { box-shadow: 0 0 16px 4px rgba(255, 213, 79, 0.6); }
.rfid-body.active .rfid-coil,
.rfid-body.active .rfid-coil::before,
.rfid-body.active .rfid-coil::after {
  border-color: #ffd54f;
}
.rfid-status {
  text-align: center;
  font-size: 9px;
  color: var(--text-dim);
  margin-top: 4px;
}

/* --- RFID Card/Tag: a little plastic card, glows when in reading range --- */
.rfid-card {
  --card-color: #6c8cff;
  width: 100px;
  height: 62px;
  margin: 6px auto 0;
  padding: 8px;
  background: linear-gradient(135deg, var(--card-color), color-mix(in srgb, var(--card-color) 55%, black));
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.35);
  position: relative;
  transition: box-shadow 0.15s;
}
.rfid-card-chip {
  width: 22px;
  height: 16px;
  background: linear-gradient(160deg, #f4d160, #c9a227);
  border-radius: 3px;
  border: 1px solid #8a7000;
}
.rfid-card-uid {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 7px;
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 8.5px;
  letter-spacing: 0.4px;
  color: rgba(255, 255, 255, 0.85);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rfid-card.active {
  box-shadow: 0 0 16px 4px rgba(255, 213, 79, 0.75);
}

/* --- Fingerprint Sensor: same powered(green)/active(amber) language as the RFID reader --- */
.fp-body {
  width: 90px;
  height: 90px;
  margin: 6px auto 0;
  background: linear-gradient(160deg, #12854a, #094a26 70%);
  border: 2px solid #063d20;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: box-shadow 0.15s;
}
.fp-pad {
  width: 54px;
  height: 66px;
  border-radius: 50% / 40%;
  background: linear-gradient(160deg, #0a3d24, #062315);
  border: 3px solid #0a5c34;
  transition: border-color 0.15s;
}
.fp-body.powered .fp-pad { border-color: #4fd884; }
.fp-body.active { box-shadow: 0 0 16px 4px rgba(255, 213, 79, 0.6); }
.fp-body.active .fp-pad { border-color: #ffd54f; }
.fp-status {
  text-align: center;
  font-size: 9px;
  color: var(--text-dim);
  margin-top: 4px;
}

/* --- Fingerprint Hand: a preset person's right hand, glows when pressed against a powered sensor --- */
.fp-hand-wrap {
  width: 110px;
  height: 110px;
  margin: 4px auto 0;
  border-radius: 12px;
  transition: box-shadow 0.15s;
}
.fp-hand-wrap.active { box-shadow: 0 0 16px 4px rgba(255, 213, 79, 0.75); }
.fp-hand { width: 100%; height: 100%; display: block; }
.fp-hand-ridge {
  stroke: #8a5a30;
  stroke-width: 0.9;
  opacity: 0.8;
}
.fp-hand-crease {
  stroke: #a9713f;
  stroke-width: 1;
  opacity: 0.55;
}
.fp-hand-meta {
  text-align: center;
  font-size: 9px;
  color: var(--text-dim);
  margin-top: 2px;
}

/* --- Pi Camera: real webcam preview + capture/recording (no GPIO wiring) --- */
.camera-body { position: relative; }
.camera-pcb {
  width: 110px;
  height: 90px;
  margin: 6px auto 0;
  background: linear-gradient(160deg, #12854a, #094a26 70%);
  border: 2px solid #063d20;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.camera-lens {
  width: 66px;
  height: 66px;
  border-radius: 50%;
  background: #060608;
  border: 3px solid #2a2a30;
  overflow: hidden;
  position: relative;
}
.camera-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.camera-enable-btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 56px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 6px;
  font-size: 8.5px;
  line-height: 1.25;
  padding: 4px 5px;
  cursor: pointer;
  text-align: center;
}
.camera-enable-btn:hover { background: rgba(0, 0, 0, 0.8); }
.camera-enable-btn:disabled { cursor: wait; opacity: 0.7; }
.camera-rec-dot {
  position: absolute;
  top: 5px;
  right: 7px;
  font-size: 9px;
  font-weight: 600;
  color: #ff5c5c;
  text-shadow: 0 0 4px rgba(255, 92, 92, 0.6);
}
/* Ribbon-cable exit point, top edge — where the ribbon graphic anchors */
.camera-csi-marker {
  position: absolute;
  top: -2px;
  left: 50%;
  transform: translateX(-50%);
  width: 14px;
  height: 4px;
  background: #9a9a9a;
  border-radius: 1px;
}
.camera-status {
  text-align: center;
  font-size: 9px;
  color: var(--text-dim);
  margin-top: 4px;
}

/* --- Potentiometer: manual analog source, wired via VCC/GND/OUT --- */
.pot-body {
  width: 86px;
  height: 84px;
  margin: 6px auto 0;
  background: linear-gradient(160deg, #2a2b3a, #1b1c26);
  border: 2px solid #14151d;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px;
}
.pot-slider { width: 100%; accent-color: var(--accent); }
.pot-readout {
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 11px;
  color: #4fd884;
}

/* --- Signal Generator: waveform analog source, wired via VCC/GND/OUT --- */
.siggen-body {
  width: 150px;
  height: 100px;
  margin: 6px auto 0;
  background: linear-gradient(160deg, #2a2b3a, #1b1c26);
  border: 2px solid #14151d;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px;
}
.siggen-icon { font-size: 22px; line-height: 1; }
.siggen-readout {
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 13px;
  font-weight: 600;
  color: #4fd884;
}
.siggen-meta { font-size: 9px; color: var(--text-dim); text-align: center; }

/* --- ADS1115: I2C ADC, same green-IC family as the RFID/Fingerprint bodies --- */
.ads-body {
  width: 140px;
  height: 90px;
  margin: 6px auto 0;
  background: linear-gradient(160deg, #12854a, #094a26 70%);
  border: 2px solid #063d20;
  border-radius: 6px;
  padding: 6px;
  transition: border-color 0.15s;
}
.ads-body.powered { border-color: #0a5c34; }
.ads-label {
  font-size: 9px;
  font-weight: 600;
  color: #eafff1;
  text-align: center;
  margin-bottom: 4px;
}
.ads-addr { color: #c9f7dd; font-weight: 400; }
.ads-channels { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.ads-channel {
  display: flex;
  justify-content: space-between;
  gap: 4px;
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 8px;
  background: rgba(0, 0, 0, 0.25);
  border-radius: 3px;
  padding: 1px 3px;
  color: #8fd4ab;
}
.ads-body.powered .ads-channel { color: #eafff1; }

/* --- Oscilloscope: live voltage-vs-time canvas, no wiring --- */
.scope-body {
  width: 200px;
  margin: 6px auto 0;
  background: linear-gradient(160deg, #2a2b3a, #1b1c26);
  border: 3px solid #14151d;
  border-radius: 8px;
  padding: 6px;
}
.scope-canvas {
  display: block;
  width: 100%;
  height: 100px;
  background: #05140a;
  border-radius: 4px;
  border: 1px solid #0a5c34;
}
.scope-footer {
  text-align: center;
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 9.5px;
  font-weight: 600;
  color: #4fd884;
  margin-top: 4px;
}

/* --- Relay Module: blue driver board with an onboard status LED and an
   armature that visibly flips over when energized --- */
.relay-body {
  position: relative;
  width: 84px;
  height: 56px;
  margin: 4px auto 0;
  background: linear-gradient(180deg, #2f6fd6, #2456a8);
  border: 1px solid #163b70;
  border-radius: 4px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.3);
}
.relay-led {
  position: absolute;
  top: 5px;
  right: 6px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #3a1010;
  box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.6);
}
.relay-body.energized .relay-led {
  background: #ff4d4d;
  box-shadow: 0 0 6px 2px rgba(255, 77, 77, 0.85);
}
.relay-coil {
  position: absolute;
  left: 8px;
  top: 8px;
  width: 26px;
  height: 26px;
  border-radius: 3px;
  background: #1b1b1b;
  overflow: hidden;
}
.relay-armature {
  position: absolute;
  left: 4px;
  top: 6px;
  width: 18px;
  height: 3px;
  background: #c7c7c7;
  border-radius: 1px;
  transform-origin: left center;
  transition: transform 0.12s ease-out;
}
.relay-body.energized .relay-armature { transform: rotate(18deg); background: #e8b923; }
.relay-screws {
  position: absolute;
  right: 6px;
  top: 6px;
  bottom: 6px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.relay-screw {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #d8d8d8, #8a8a8a 60%, #555);
  border: 1px solid #333;
  display: block;
}
.relay-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}
.relay-body.energized ~ .relay-status { color: #ff4d4d; }

/* AC terminals (Relay's COM/NO/NC, AC Mains/Light/Fan's L/N) get a
   red-tinted base so the mains side reads as visually distinct from the
   tan DC terminals everywhere else — a real safety-relevant distinction. */
.terminal.ac-terminal {
  background: #c76b6b;
  border-color: #6b2020;
}

/* --- AC Mains: a simple 240V supply block, no VCC/GND/GPIO side --- */
.acmains-body {
  width: 74px;
  height: 64px;
  margin: 8px auto 0;
  border-radius: 6px;
  background: linear-gradient(160deg, #3a1414, #200808);
  border: 2px solid #200808;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
}
.acmains-icon {
  font-size: 24px;
  line-height: 1;
}
.acmains-label {
  font-size: 9px;
  font-weight: 700;
  color: #ff8a8a;
  letter-spacing: 0.02em;
}

/* --- AC Light: a mains-switched bulb — on/off only, no dimmer --- */
.aclight-wrap {
  position: relative;
  width: 60px;
  height: 78px;
  margin: 4px auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
}
.aclight-halo {
  position: absolute;
  top: 30px;
  left: 50%;
  width: 30px;
  height: 30px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 244, 200, 0.95), rgba(255, 210, 80, 0.35) 45%, rgba(255, 210, 80, 0) 70%);
  opacity: 0;
  filter: blur(4px);
  transition: opacity 0.15s ease, width 0.15s ease, height 0.15s ease;
}
.aclight-bulb {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.5), rgba(180, 180, 190, 0.15) 60%);
  border: 1px solid rgba(255, 255, 255, 0.3);
  box-shadow: none;
  z-index: 1;
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
.aclight-base {
  width: 22px;
  height: 14px;
  margin-top: -2px;
  background: repeating-linear-gradient(0deg, #7a7f8c 0 2px, #5f636e 2px 4px);
  border-radius: 0 0 4px 4px;
  z-index: 1;
}
.aclight-wrap.on .aclight-halo {
  opacity: 0.85;
  width: 80px;
  height: 80px;
}
.aclight-wrap.on .aclight-bulb {
  background: radial-gradient(circle at 35% 30%, #fff6cc, #ffd400 70%);
  box-shadow: 0 0 16px 4px rgba(255, 210, 80, 0.8);
}
.aclight-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- AC Button: a wall-switch plate — click the rocker to toggle it --- */
.acbutton-plate {
  width: 50px;
  height: 76px;
  margin: 8px auto 0;
  border-radius: 6px;
  background: linear-gradient(160deg, #e8e8e8, #c4c4c4);
  border: 2px solid #9a9a9a;
  display: flex;
  align-items: center;
  justify-content: center;
}
.acbutton-rocker {
  width: 26px;
  height: 46px;
  border-radius: 4px;
  background: linear-gradient(180deg, #f5f5f5, #d0d0d0);
  border: 1px solid #8a8a8a;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.25);
  cursor: pointer;
}
.acbutton-plate.on .acbutton-rocker {
  background: linear-gradient(180deg, #ffe28a, #e8b923);
  border-color: #a8791a;
  box-shadow: 0 0 6px 2px rgba(232, 185, 35, 0.6);
}
.acbutton-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- AC Fan: a real blade assembly — a wire-mesh guard (conic-gradient
   spokes), three blades on a rotating hub, a neck, and a stand — not an
   emoji. The parent's own spin animation plus each blade's fixed 120°
   offset compose naturally, so all three sweep together as one unit. --- */
.acfan-body {
  width: 100px;
  height: 130px;
  margin: 4px auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.acfan-guard {
  position: relative;
  width: 74px;
  height: 74px;
  border-radius: 50%;
  background: repeating-conic-gradient(rgba(210, 213, 218, 0.9) 0deg 1.2deg, transparent 1.2deg 15deg);
  border: 3px solid #8a8d94;
  display: flex;
  align-items: center;
  justify-content: center;
}
.acfan-blades {
  position: relative;
  width: 56px;
  height: 56px;
  animation-name: acfanSpin;
  animation-duration: 0.5s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-play-state: paused;
}
.acfan-blades.spinning {
  animation-play-state: running;
}
.acfan-blade {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 26px;
  height: 14px;
  margin-top: -7px;
  transform-origin: left center;
  border-radius: 0 50% 50% 0;
  background: linear-gradient(120deg, #eef0f3, #b8bcc4);
}
.acfan-blade.b1 {
  transform: rotate(0deg);
}
.acfan-blade.b2 {
  transform: rotate(120deg);
}
.acfan-blade.b3 {
  transform: rotate(240deg);
}
.acfan-hub {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 16px;
  height: 16px;
  margin-left: -8px;
  margin-top: -8px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #d8d8d8, #6a6a6a 70%);
  border: 1px solid #444;
  z-index: 2;
}
@keyframes acfanSpin {
  to {
    transform: rotate(360deg);
  }
}
.acfan-neck {
  width: 8px;
  height: 18px;
  background: #6a6d74;
}
.acfan-stand {
  width: 46px;
  height: 8px;
  border-radius: 4px;
  background: linear-gradient(180deg, #7a7d84, #4a4d54);
}
.acfan-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- AC Ceiling Fan: hangs from a ceiling mount on a downrod, four wide
   paddle blades on a central motor housing instead of the pedestal fan's
   wire-guarded head — same "parent spins, each blade has its own fixed
   angle offset" composition trick as the AC Fan. --- */
.aceilingfan-body {
  width: 130px;
  height: 100px;
  margin: 4px auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.aceilingfan-mount {
  width: 30px;
  height: 8px;
  border-radius: 0 0 4px 4px;
  background: linear-gradient(180deg, #7a7d84, #4a4d54);
}
.aceilingfan-rod {
  width: 3px;
  height: 20px;
  background: #6a6d74;
}
.aceilingfan-assembly {
  position: relative;
  width: 110px;
  height: 50px;
}
.aceilingfan-blades {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  animation-name: aceilingfanSpin;
  animation-duration: 0.45s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-play-state: paused;
}
.aceilingfan-blades.spinning {
  animation-play-state: running;
}
.aceilingfan-blade {
  position: absolute;
  left: 0;
  top: -6px;
  width: 48px;
  height: 12px;
  transform-origin: left center;
  border-radius: 3px 10px 10px 3px;
  background: linear-gradient(180deg, #d8d3c4, #b0a888);
  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
}
.aceilingfan-blade.b1 {
  transform: rotate(0deg);
}
.aceilingfan-blade.b2 {
  transform: rotate(90deg);
}
.aceilingfan-blade.b3 {
  transform: rotate(180deg);
}
.aceilingfan-blade.b4 {
  transform: rotate(270deg);
}
@keyframes aceilingfanSpin {
  to {
    transform: rotate(360deg);
  }
}
.aceilingfan-motor {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 26px;
  height: 16px;
  margin-left: -13px;
  margin-top: -8px;
  border-radius: 8px;
  background: linear-gradient(180deg, #5a5d64, #34363c);
  border: 1px solid #222;
  z-index: 2;
}
.aceilingfan-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- AC Tube Light: a long fluorescent fixture — cool white-blue glow
   plus a faint continuous flicker while on, unlike the AC Light bulb's
   steady warm glow. --- */
.actube-fixture {
  display: flex;
  align-items: center;
  width: 120px;
  height: 30px;
  margin: 30px auto 0;
}
.actube-cap {
  width: 8px;
  height: 20px;
  background: #6a6d74;
  border-radius: 2px;
  flex-shrink: 0;
}
.actube-glass {
  flex: 1;
  height: 18px;
  border-radius: 9px;
  background: #4a4d54;
  box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.4);
  /* No `transition` here: a `background` transition on an element that
     also picks up a running `animation` (tubeFlicker, below) leaves the
     background stuck at its pre-transition value in this app's browser
     — confirmed empirically, same class of bug as the Sun/Moon sky's
     color-mix()+transition issue. An instant flip reads fine anyway. */
}
.actube-fixture.on .actube-glass {
  background: #eaf6ff;
  box-shadow: 0 0 18px 6px rgba(200, 235, 255, 0.85), inset 0 0 8px rgba(255, 255, 255, 0.9);
  animation: tubeFlicker 3s ease-in-out infinite;
}
@keyframes tubeFlicker {
  0%,
  92%,
  100% {
    opacity: 1;
  }
  93% {
    opacity: 0.85;
  }
  94% {
    opacity: 1;
  }
  95% {
    opacity: 0.7;
  }
  96% {
    opacity: 1;
  }
}
.actube-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Buzzer: piezo disc with sound-wave rings that pulse while driven --- */
.buzzer-body {
  position: relative;
  width: 46px;
  height: 46px;
  margin: 6px auto 0;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #d8b23a, #8a6a1a 70%, #5a4610);
  border: 1px solid #4a3810;
}
.buzzer-grille {
  position: absolute;
  inset: 8px;
  border-radius: 50%;
  background:
    radial-gradient(circle at center, transparent 0 3px, #3a2c0c 3px 4px, transparent 4px) center / 10px 10px,
    #2a2008;
}
.buzzer-waves {
  position: absolute;
  top: -4px;
  right: -10px;
  width: 26px;
  height: 46px;
  pointer-events: none;
}
.buzzer-waves span {
  position: absolute;
  top: 50%;
  left: 0;
  width: 10px;
  height: 10px;
  border: 2px solid #61dafb;
  border-left: none;
  border-bottom: none;
  border-radius: 0 100% 0 0;
  transform: translateY(-50%) rotate(45deg) scale(0.4);
  opacity: 0;
}
.buzzer-body.buzzing ~ .buzzer-waves span { animation: buzzer-wave 0.9s ease-out infinite; }
.buzzer-waves span:nth-child(2) { animation-delay: 0.15s !important; left: 6px; }
.buzzer-waves span:nth-child(3) { animation-delay: 0.3s !important; left: 12px; }
@keyframes buzzer-wave {
  0% { transform: translateY(-50%) rotate(45deg) scale(0.4); opacity: 0.9; }
  100% { transform: translateY(-50%) rotate(45deg) scale(1.3); opacity: 0; }
}

.popover-checkbox-row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text);
  cursor: pointer;
  margin-top: 4px;
}
.popover-checkbox-row input { cursor: pointer; }

/* --- DC Motor: cylindrical can with a shaft-mounted fan that spins at a
   rate/direction reflecting the live IN1/IN2/EN state --- */
.motor-body {
  position: relative;
  width: 56px;
  height: 60px;
  margin: 4px auto 0;
  border-radius: 8px;
  background: linear-gradient(90deg, #7a7a7a, #b8b8b8 45%, #7a7a7a);
  border: 1px solid #4a4a4a;
}
.motor-cap {
  position: absolute;
  top: -3px;
  left: 6px;
  right: 6px;
  height: 8px;
  border-radius: 4px 4px 0 0;
  background: #5a5a5a;
}
.motor-shaft {
  position: absolute;
  right: -14px;
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 5px;
  background: #333;
}
.motor-fan {
  position: absolute;
  right: -22px;
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
}
.motor-fan span {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 22px;
  height: 5px;
  margin-top: -2.5px;
  margin-left: -11px;
  background: #d64545;
  border-radius: 3px;
}
.motor-fan span:nth-child(2) { transform: rotate(60deg); }
.motor-fan span:nth-child(3) { transform: rotate(120deg); }
.motor-fan.spinning { animation: motor-spin var(--motor-duration, 0.6s) linear infinite; }
.motor-fan.spinning.reverse { animation-direction: reverse; }
@keyframes motor-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.motor-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Servo Motor: body + a horn that visibly sweeps to the commanded
   angle (0-180 mapped across a half-circle) --- */
.servo-body {
  position: relative;
  width: 54px;
  height: 44px;
  margin: 10px auto 0;
  border-radius: 4px;
  background: linear-gradient(180deg, #3d5a80, #2c3e57);
  border: 1px solid #1c2740;
}
.servo-horn {
  position: absolute;
  left: 50%;
  top: -3px;
  width: 3px;
  height: 22px;
  background: #d8d8d8;
  border-radius: 2px;
  transform-origin: bottom center;
  transform: translateX(-50%) rotate(calc(90deg - 90deg));
  transition: transform 0.15s ease-out;
  opacity: 0.35;
}
.servo-horn.active {
  opacity: 1;
  transform: translateX(-50%) rotate(calc(var(--servo-angle, 90deg) - 90deg));
}
.servo-horn-tip {
  position: absolute;
  top: -3px;
  left: 50%;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #e8b923;
  transform: translateX(-50%);
}
.servo-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Push Button: square tactile switch with a cap that visibly depresses --- */
.btn-body {
  width: 46px;
  height: 46px;
  margin: 6px auto 0;
  border-radius: 6px;
  background: #3a3a44;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.3);
}
.btn-cap {
  width: 30px;
  height: 30px;
  border-radius: 4px;
  background: linear-gradient(180deg, #e8e8e8, #b8b8b8);
  border: 1px solid #888;
  cursor: pointer;
  transform: translateY(0);
  box-shadow: 0 3px 0 rgba(0, 0, 0, 0.35);
  transition: transform 0.05s, box-shadow 0.05s;
}
.btn-cap.pressed {
  transform: translateY(3px);
  box-shadow: 0 0 0 rgba(0, 0, 0, 0.35);
  background: linear-gradient(180deg, #d64545, #a83030);
}
.btn-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- PIR Motion Sensor: white dome that glows + pulses a ring on trigger --- */
.pir-body {
  width: 56px;
  height: 50px;
  margin: 6px auto 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pir-dome {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f5f5f0, #cfcfc4 70%, #a8a89c);
  border: 1px solid #8a8a7c;
  cursor: pointer;
}
.pir-dome.active {
  background: radial-gradient(circle at 35% 30%, #fff6d0, #ffdd66 70%, #e8b923);
  box-shadow: 0 0 12px 3px rgba(232, 185, 35, 0.7);
}
.pir-ring {
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 2px solid #e8b923;
  opacity: 0;
}
.pir-dome.active .pir-ring { animation: pir-pulse 1.1s ease-out infinite; }
@keyframes pir-pulse {
  0% { transform: scale(0.8); opacity: 0.8; }
  100% { transform: scale(1.5); opacity: 0; }
}
.pir-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Reed / Tilt Switch: glass capsule with two contacts that visibly join
   when closed --- */
.reed-body {
  width: 70px;
  height: 40px;
  margin: 10px auto 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.reed-glass {
  position: relative;
  width: 56px;
  height: 16px;
  border-radius: 8px;
  background: rgba(140, 200, 220, 0.18);
  border: 1px solid rgba(140, 200, 220, 0.55);
  cursor: pointer;
  overflow: hidden;
}
.reed-contact {
  position: absolute;
  top: 50%;
  width: 22px;
  height: 2px;
  background: #b8b8b8;
  transform: translateY(-50%);
  transition: transform 0.1s;
}
.reed-contact-l { left: 2px; }
.reed-contact-r { right: 2px; }
.reed-glass.closed .reed-contact-l { transform: translateY(-50%) translateX(6px); }
.reed-glass.closed .reed-contact-r { transform: translateY(-50%) translateX(-6px); }
.reed-glass.closed { background: rgba(79, 216, 132, 0.22); border-color: #4fd884; }
.reed-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- IR Obstacle Avoidance Sensor: two lenses (emitter/receiver) with a
   clickable "obstacle" puck that slides into the beam --- */
.irobs-body {
  position: relative;
  width: 70px;
  height: 40px;
  margin: 8px auto 0;
  border-radius: 5px;
  background: #2c2c34;
  border: 1px solid #1a1a20;
  display: flex;
  align-items: center;
  justify-content: space-evenly;
}
.irobs-lens {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #6a3030, #2a0f0f 70%);
  border: 1px solid #1a0808;
}
.irobs-body.powered .irobs-lens-tx {
  background: radial-gradient(circle at 35% 30%, #ff8a8a, #b71c1c 70%);
  box-shadow: 0 0 5px 1px rgba(255, 77, 77, 0.6);
}
.irobs-beam {
  position: absolute;
  right: -18px;
  top: 50%;
  width: 16px;
  height: 2px;
  transform: translateY(-50%);
  background: repeating-linear-gradient(90deg, #d64545 0 3px, transparent 3px 6px);
  opacity: 0.35;
}
.irobs-beam.blocked {
  opacity: 1;
  background: #7a5a3a;
}
.irobs-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Flame Sensor: same small PCB shape as the IR Obstacle Sensor, one
   red-tinted IR photodiode lens that lights up when a flame is seen --- */
.flame-sensor-body {
  position: relative;
  width: 70px;
  height: 40px;
  margin: 8px auto 0;
  border-radius: 5px;
  background: #2c2c34;
  border: 1px solid #1a1a20;
  display: flex;
  align-items: center;
  justify-content: center;
}
.flame-sensor-lens {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #6a3030, #2a0f0f 70%);
  border: 1px solid #1a0808;
}
.flame-sensor-lens.detected {
  background: radial-gradient(circle at 35% 30%, #ffcf8a, #ff3d00 70%);
  box-shadow: 0 0 8px 2px rgba(255, 120, 0, 0.7);
}
.flame-sensor-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Rain Sensor: a small PCB with parallel copper traces (like the
   YL-83/FC-37 boards) that glow blue when wet --- */
.rain-sensor-body {
  position: relative;
  width: 70px;
  height: 40px;
  margin: 8px auto 0;
  border-radius: 5px;
  background: #163a2e;
  border: 1px solid #0d241c;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px 8px;
}
.rain-sensor-traces {
  width: 100%;
  height: 100%;
  background: repeating-linear-gradient(0deg, #c8a45a 0 2px, transparent 2px 6px);
  border-radius: 2px;
  opacity: 0.55;
}
.rain-sensor-traces.wet {
  opacity: 1;
  box-shadow: 0 0 6px 1px rgba(90, 180, 255, 0.6);
  background: repeating-linear-gradient(0deg, #c8a45a 0 2px, rgba(90, 180, 255, 0.5) 2px 6px);
}
.rain-sensor-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Light Sensor (LDR): a small PCB with a round photoresistor cell —
   its zig-zag track tint brightens from dim gray to warm yellow-white
   based on --b (0..1 measured brightness), giving direct visual feedback
   of what the sensor is currently reading. --- */
.ldr-body {
  position: relative;
  width: 70px;
  height: 40px;
  margin: 8px auto 0;
  border-radius: 5px;
  background: #2c2c34;
  border: 1px solid #1a1a20;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ldr-cell {
  --b: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: color-mix(in srgb, #4a4a4a calc((1 - var(--b)) * 100%), #ffe28a calc(var(--b) * 100%));
  border: 2px solid #8a8a78;
  box-shadow: 0 0 calc(var(--b) * 10px) rgba(255, 220, 130, calc(var(--b) * 0.8));
  background-image: repeating-radial-gradient(circle, transparent 0 2px, rgba(0, 0, 0, 0.25) 2px 3px);
}
.ldr-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Gas Sensor: same small PCB shape as the other digital sensors, one
   round metal detection mesh (like an MQ-2's sensing can) that glows
   green when gas is detected --- */
.gas-sensor-body {
  position: relative;
  width: 70px;
  height: 40px;
  margin: 8px auto 0;
  border-radius: 5px;
  background: #2c2c34;
  border: 1px solid #1a1a20;
  display: flex;
  align-items: center;
  justify-content: center;
}
.gas-sensor-mesh {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: repeating-conic-gradient(#8a8a8a 0deg 12deg, #6a6a6a 12deg 24deg);
  border: 2px solid #4a4a4a;
}
.gas-sensor-mesh.detected {
  background: repeating-conic-gradient(#9adf6a 0deg 12deg, #5fae3a 12deg 24deg);
  box-shadow: 0 0 8px 2px rgba(140, 220, 90, 0.7);
  border-color: #5fae3a;
}
.gas-sensor-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Ultrasonic Sensor (HC-SR04): blue PCB with two round transducer
   "eyes" that glow when powered --- */
.us-body {
  display: flex;
  justify-content: center;
  gap: 8px;
  width: 84px;
  height: 40px;
  margin: 8px auto 0;
  padding: 6px 0;
  border-radius: 4px;
  background: linear-gradient(180deg, #1f4a7a, #163556);
  border: 1px solid #0e2338;
}
.us-eye {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #d8d8d8, #8a8a8a 65%, #555);
  border: 2px solid #444;
}
.us-body.powered .us-eye {
  background: radial-gradient(circle at 35% 30%, #cfeeff, #6bb8e0 65%, #2f7aa8);
  box-shadow: 0 0 6px 1px rgba(107, 184, 224, 0.6);
}
.us-status {
  text-align: center;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin-top: 3px;
}

/* --- Environmental props: Person/Car/Fire/Rain — passive board objects
   sensors detect by proximity --- */
.prop-person {
  font-size: 52px;
  line-height: 1;
  text-align: center;
  margin-top: 8px;
}
.prop-car {
  font-size: 46px;
  line-height: 1;
  text-align: center;
  margin-top: 12px;
}
.prop-fire-body,
.prop-rain-body,
.prop-sunmoon-body,
.prop-light-body,
.prop-gas-body {
  width: 100px;
  height: 108px;
  margin: 4px auto 0;
  background: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 3px;
}

/* Fire: three layered/blurred radial-gradient blobs flickering at
   independent, slightly-desynced speeds, scaled and glowing harder as
   --t (normalized intensity, 0..1) rises. No image asset involved. */
.prop-fire-wrap {
  position: relative;
  width: 72px;
  height: 78px;
  --t: 0.5;
  opacity: var(--t);
  transform: scale(calc(0.4 + var(--t) * 1.1));
  transform-origin: bottom center;
  filter: drop-shadow(0 0 calc(var(--t) * 18px) rgba(255, 120, 0, calc(var(--t) * 0.8)));
}
.prop-fire-flame {
  position: absolute;
  left: 50%;
  bottom: 0;
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  filter: blur(1.5px);
  animation-name: fireFlicker;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}
.prop-fire-flame.f1 {
  width: 58px;
  height: 76px;
  margin-left: 0px;
  background: radial-gradient(circle at 50% 72%, #ff8a00, #ff3d00 62%, rgba(255, 61, 0, 0) 100%);
  animation-duration: calc(1.3s - var(--t) * 0.5s);
}
.prop-fire-flame.f2 {
  width: 40px;
  height: 56px;
  margin-left: 0px;
  background: radial-gradient(circle at 50% 66%, #ffd400, #ff9100 65%, rgba(255, 145, 0, 0) 100%);
  animation-duration: calc(1s - var(--t) * 0.4s);
  animation-delay: -0.3s;
}
.prop-fire-flame.f3 {
  width: 20px;
  height: 34px;
  margin-left: 0px;
  background: radial-gradient(circle at 50% 60%, #fff6cc, #ffd400 70%, rgba(255, 212, 0, 0) 100%);
  animation-duration: calc(0.8s - var(--t) * 0.3s);
  animation-delay: -0.6s;
}
@keyframes fireFlicker {
  0%,
  100% {
    transform: translateX(-50%) scaleY(1) scaleX(1) translateY(0);
  }
  25% {
    transform: translateX(-50%) scaleY(1.08) scaleX(0.94) translateY(-3px);
  }
  50% {
    transform: translateX(-50%) scaleY(0.92) scaleX(1.06) translateY(1px);
  }
  75% {
    transform: translateX(-50%) scaleY(1.05) scaleX(0.96) translateY(-2px);
  }
}

/* Rain: a flat CSS cloud plus a fixed set of drop divs. Each drop's `--i`
   (baked in at render time) fixes its horizontal slot and fall stagger;
   --t (set live from intensity) reveals more of them, speeds up the fall,
   and lengthens the streak as intensity rises. */
.prop-rain-cloud {
  width: 62px;
  height: 26px;
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  background: linear-gradient(180deg, #8b93a8, #6b7286);
  position: relative;
}
.prop-rain-cloud::before,
.prop-rain-cloud::after {
  content: "";
  position: absolute;
  bottom: 6px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: inherit;
}
.prop-rain-cloud::before {
  left: -10px;
}
.prop-rain-cloud::after {
  right: -10px;
}
.prop-rain-drops {
  position: relative;
  width: 70px;
  height: 58px;
  overflow: hidden;
  --t: 0.5;
}
.prop-rain-drop {
  position: absolute;
  top: 0;
  width: 2px;
  height: calc(10px + var(--t) * 14px);
  border-radius: 2px;
  background: linear-gradient(to bottom, rgba(130, 195, 255, 0), rgba(130, 195, 255, 0.9));
  animation-name: rainFall;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: calc(1.3s - var(--t) * 0.85s);
  animation-delay: calc(var(--i) * -0.11s);
  opacity: clamp(0, calc((var(--t) - var(--i) * 0.08) * 30), 1);
}
@keyframes rainFall {
  from {
    transform: translateY(-20px);
  }
  to {
    transform: translateY(84px);
  }
}

.prop-fire-slider,
.prop-rain-slider {
  width: 90%;
  accent-color: var(--accent);
}
.prop-fire-readout,
.prop-rain-readout,
.prop-sunmoon-readout,
.prop-light-readout,
.prop-gas-readout {
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 11px;
  color: #4fd884;
}

/* Sun/Moon: a small sky whose background, disc color, and glow all blend
   with color-mix() against the same --t (0..1, from the intensity
   slider) — night/moon at 0, day/sun at 1 — plus a rotating sun-ray burst
   and twinkling stars that fade in opposite directions, and a crescent
   overlay (same color-mix as the sky, so it blends seamlessly) that bites
   a moon phase out of the disc and disappears as the sun takes over. */
.prop-sunmoon-sky {
  position: relative;
  width: 84px;
  height: 64px;
  margin: 4px auto 0;
  border-radius: 10px;
  overflow: hidden;
  --t: 0.5;
  background: color-mix(in srgb, #0b1730 calc((1 - var(--t)) * 100%), #7fd0ff calc(var(--t) * 100%));
  box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.4);
}
.prop-sunmoon-stars {
  position: absolute;
  inset: 0;
  opacity: calc(1 - var(--t));
}
.prop-sunmoon-star {
  position: absolute;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: #fff;
  animation: sunmoonTwinkle 2.4s ease-in-out infinite;
}
.prop-sunmoon-star.s0 {
  top: 8px;
  left: 10px;
}
.prop-sunmoon-star.s1 {
  top: 14px;
  left: 62px;
  animation-delay: 0.4s;
}
.prop-sunmoon-star.s2 {
  top: 30px;
  left: 12px;
  animation-delay: 0.8s;
}
.prop-sunmoon-star.s3 {
  top: 46px;
  left: 68px;
  animation-delay: 1.2s;
}
.prop-sunmoon-star.s4 {
  top: 10px;
  left: 38px;
  animation-delay: 0.6s;
}
.prop-sunmoon-star.s5 {
  top: 48px;
  left: 44px;
  animation-delay: 1.6s;
}
.prop-sunmoon-rays {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 64px;
  height: 64px;
  margin: -32px 0 0 -32px;
  border-radius: 50%;
  opacity: var(--t);
  background: repeating-conic-gradient(rgba(255, 205, 80, 0.55) 0deg 8deg, transparent 8deg 30deg);
  animation: sunmoonSpin 14s linear infinite;
}
.prop-sunmoon-disc {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 50%;
  overflow: hidden;
  background: color-mix(in srgb, #dfe6f0 calc((1 - var(--t)) * 100%), #ffd35c calc(var(--t) * 100%));
  box-shadow: 0 0 calc(6px + var(--t) * 16px)
    color-mix(in srgb, rgba(180, 200, 255, 0.5) calc((1 - var(--t)) * 100%), rgba(255, 190, 60, 0.85) calc(var(--t) * 100%));
}
.prop-sunmoon-crescent {
  position: absolute;
  top: -6px;
  left: 10px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: color-mix(in srgb, #0b1730 calc((1 - var(--t)) * 100%), #7fd0ff calc(var(--t) * 100%));
  opacity: calc(1 - var(--t));
}
@keyframes sunmoonSpin {
  to {
    transform: rotate(360deg);
  }
}
@keyframes sunmoonTwinkle {
  0%,
  100% {
    opacity: 0.25;
  }
  50% {
    opacity: 1;
  }
}

/* Light bulb: a glass bulb whose filament color-mixes from dim gray to
   warm white and whose glow (box-shadow) and surrounding halo scale with
   --t — dark and off at 0, a bright glowing bulb with a soft breathing
   halo at 1. */
.prop-light-wrap {
  position: relative;
  width: 70px;
  height: 92px;
  margin: 4px auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  --t: 0.5;
}
.prop-light-halo {
  position: absolute;
  top: 34px;
  left: 50%;
  width: calc(30px + var(--t) * 55px);
  height: calc(30px + var(--t) * 55px);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 244, 200, 0.95), rgba(255, 210, 80, 0.35) 45%, rgba(255, 210, 80, 0) 70%);
  opacity: calc(var(--t) * 0.9);
  filter: blur(4px);
  animation: lightBreathe 2.4s ease-in-out infinite;
}
.prop-light-bulb {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.15) 40%, rgba(230, 230, 240, 0.08) 70%);
  border: 1px solid rgba(255, 255, 255, 0.35);
  box-shadow: 0 0 calc(var(--t) * 20px) rgba(255, 220, 130, calc(var(--t) * 0.9));
  z-index: 1;
}
.prop-light-filament {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 10px;
  transform: translate(-50%, -50%);
  border-radius: 2px;
  background: color-mix(in srgb, #5a5a5a calc((1 - var(--t)) * 100%), #fff2ad calc(var(--t) * 100%));
  box-shadow: 0 0 calc(var(--t) * 8px) rgba(255, 240, 180, calc(var(--t) * 0.9));
}
.prop-light-base {
  width: 22px;
  height: 14px;
  margin-top: -2px;
  background: repeating-linear-gradient(0deg, #7a7f8c 0 2px, #5f636e 2px 4px);
  border-radius: 0 0 4px 4px;
  z-index: 1;
}
@keyframes lightBreathe {
  0%,
  100% {
    transform: translate(-50%, -50%) scale(1);
  }
  50% {
    transform: translate(-50%, -50%) scale(1.06);
  }
}

/* Gas: three layered, blurred, translucent puffs that drift upward and
   sideways on a slow loop — bigger, more opaque, and drifting faster as
   --t (normalized concentration, 0..1) rises. Barely visible at 0, a
   thick haze at 1. No image asset involved. */
.prop-gas-wrap {
  position: relative;
  width: 76px;
  height: 84px;
  --t: 0.5;
  opacity: calc(0.15 + var(--t) * 0.75);
  transform: scale(calc(0.7 + var(--t) * 0.5));
  transform-origin: bottom center;
}
.prop-gas-cloud {
  position: absolute;
  left: 50%;
  bottom: 6px;
  border-radius: 50%;
  filter: blur(3px);
  animation-name: gasDrift;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}
.prop-gas-cloud.g1 {
  width: 60px;
  height: 44px;
  margin-left: 0px;
  background: radial-gradient(circle at 50% 55%, rgba(180, 210, 90, 0.85), rgba(120, 150, 60, 0.15) 70%);
  animation-duration: calc(4.5s - var(--t) * 1.8s);
}
.prop-gas-cloud.g2 {
  width: 46px;
  height: 36px;
  margin-left: 0px;
  bottom: 30px;
  background: radial-gradient(circle at 50% 55%, rgba(200, 225, 120, 0.8), rgba(140, 165, 70, 0.12) 70%);
  animation-duration: calc(3.6s - var(--t) * 1.4s);
  animation-delay: -1.2s;
}
.prop-gas-cloud.g3 {
  width: 34px;
  height: 26px;
  margin-left: 0px;
  bottom: 52px;
  background: radial-gradient(circle at 50% 55%, rgba(215, 235, 150, 0.75), rgba(150, 175, 80, 0.1) 70%);
  animation-duration: calc(3s - var(--t) * 1.1s);
  animation-delay: -2.1s;
}
@keyframes gasDrift {
  0% {
    transform: translateX(-50%) translate(0, 0) scale(1);
  }
  25% {
    transform: translateX(-50%) translate(6px, -6px) scale(1.05);
  }
  50% {
    transform: translateX(-50%) translate(-4px, -12px) scale(0.96);
  }
  75% {
    transform: translateX(-50%) translate(5px, -6px) scale(1.03);
  }
  100% {
    transform: translateX(-50%) translate(0, 0) scale(1);
  }
}

.prop-sunmoon-slider,
.prop-light-slider,
.prop-gas-slider {
  width: 90%;
  accent-color: var(--accent);
}

.minmax-row {
  display: flex;
  gap: 10px;
  margin-top: 4px;
}
.minmax-row label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 11px;
  color: var(--text-dim);
}
.minmax-row input {
  width: 60px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 6px;
  color: var(--text);
  font-size: 12px;
}

.wire-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 3;
  overflow: visible;
}
.wire-layer path {
  fill: none;
  stroke-width: 3;
  pointer-events: stroke;
  cursor: pointer;
}
.wire-layer path:hover { filter: brightness(1.35); }
/* Live rubber-band preview while a wire is mid-connection — dashed and
   non-interactive so it never blocks clicking the pin/terminal beneath
   the cursor to complete (or a bend point below it). */
.wire-layer path.wire-preview {
  stroke-dasharray: 7 5;
  opacity: 0.8;
  pointer-events: none;
}
/* Draggable bend points on a rerouted wire — double-click the wire to add
   one, drag to reposition, double-click a point to remove it. */
.wire-waypoint-handle {
  pointer-events: all;
  cursor: grab;
  stroke: rgba(255, 255, 255, 0.7);
  stroke-width: 1.5;
}
.wire-waypoint-handle:hover { filter: brightness(1.35); }
.wire-waypoint-handle:active { cursor: grabbing; }
/* Camera CSI ribbon — automatic (not a user-drawn circuit.wires entry), so
   no hover/click affordance: a flat grey band (wide outer stroke) with a
   lighter centerline (thin inner stroke) to read as ribbon rather than a
   round jumper wire. */
.ribbon-cable-outer {
  fill: none;
  stroke: #8a8a8a;
  stroke-width: 7;
  stroke-linecap: round;
}
.ribbon-cable-inner {
  fill: none;
  stroke: #c8c8c8;
  stroke-width: 1.5;
  stroke-linecap: round;
}

/* --- Draw mode: a purely cosmetic annotation layer on top of the
   breadboard (freehand/lines/shapes/text) — lives inside #breadboard so
   it inherits its rotate()/zoom transform for free, same as components. */
.annotation-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 5;
  overflow: visible;
}
.annotation-layer.draw-active {
  pointer-events: all;
  cursor: crosshair;
}

#btn-draw.active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--text);
}

.draw-toolbar {
  position: fixed;
  top: 64px;
  right: 24px;
  z-index: 55;
  background: #26273a;
  border: 1px solid #4a4b66;
  border-radius: 8px;
  padding: 10px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  width: 210px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.draw-toolbar-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.draw-tool-btn {
  flex: 1 0 calc(33% - 4px);
  padding: 5px 4px;
  font-size: 11px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  cursor: pointer;
}
.draw-tool-btn:hover {
  background: var(--bg-3);
}
.draw-tool-btn.active {
  background: var(--accent-soft);
  border-color: var(--accent);
}
.swatch.active {
  border-color: #fff;
  box-shadow: 0 0 0 2px var(--accent);
}
.draw-color-picker {
  width: 28px;
  height: 24px;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
}
.draw-width-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-dim);
  width: 100%;
}
.draw-width-label input[type="range"] {
  flex: 1;
}
.draw-toolbar-actions {
  justify-content: space-between;
}
.draw-toolbar-actions .btn {
  flex: 1;
}
.draw-text-input {
  position: fixed;
  z-index: 65;
  font-size: 15px;
  padding: 3px 6px;
  border-radius: 4px;
  border: 1px solid #4a4b66;
  background: #1b1c26;
  min-width: 120px;
}

.popover {
  position: fixed;
  z-index: 50;
  background: #26273a;
  border: 1px solid #4a4b66;
  border-radius: 8px;
  padding: 10px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  width: 190px;
}
.popover-title {
  font-size: 10px;
  color: #9a9ab0;
  margin: 6px 0 4px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.popover-title:first-child { margin-top: 0; }
.swatches { display: flex; flex-wrap: wrap; gap: 6px; }
.swatch {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.15);
  cursor: pointer;
  padding: 0;
}
.swatch:hover { border-color: #fff; }
.rotate-row { display: flex; align-items: center; gap: 6px; }
.rotate-row input[type="range"] { flex: 1; }
.addr-select,
.bus-select,
.finger-select,
.channel-select,
.waveform-select,
.siggen-num-input {
  width: 100%;
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 4px 6px;
  font-size: 12px;
  margin-bottom: 8px;
}

.palette-popover { width: 260px; }
.palette-list { display: flex; flex-direction: column; gap: 4px; }
.palette-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 6px 8px;
  cursor: pointer;
  text-align: left;
  color: var(--text);
}
.palette-item:hover { background: var(--bg-3); border-color: var(--border); }
.palette-item:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.palette-item:disabled:hover { background: none; border-color: transparent; }
.palette-icon { font-size: 20px; flex-shrink: 0; width: 24px; text-align: center; }
.palette-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.palette-name { font-size: 13px; font-weight: 500; }
.palette-desc { font-size: 10.5px; color: var(--text-dim); line-height: 1.3; }

.code-panel {
  display: flex;
  flex-direction: column;
  min-width: 280px;
  flex: 1 1 40%;
  min-height: 0;
}
.code-toolbar {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  padding: 6px 8px 0;
  background: var(--bg-1);
  border-bottom: 1px solid var(--border-soft);
  font-size: 12px;
  color: var(--text-dim);
  overflow-x: clip;
}
.file-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--border);
  display: inline-block;
  flex-shrink: 0;
  transition: background 0.15s ease;
}
.file-dot.file-dot-dirty {
  background: #f0b23a;
}
.file-tabs { display: flex; align-items: flex-end; gap: 3px; }
.file-tab {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 10px;
  background: var(--bg-2);
  border: 1px solid var(--border-soft);
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}
.file-tab:hover { color: var(--text); }
.file-tab.active { background: var(--bg-0); color: var(--text); border-color: var(--border); position: relative; top: 1px; }
.file-tab-close {
  font-size: 13px;
  line-height: 1;
  color: var(--text-dim);
  border-radius: 3px;
  padding: 1px 3px;
}
.file-tab-close:hover { color: #fff; background: var(--red); }
.file-tab-add {
  background: none;
  border: 1px dashed var(--border);
  border-radius: 6px 6px 0 0;
  color: var(--text-dim);
  width: 26px;
  height: 26px;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  flex-shrink: 0;
}
.file-tab-add:hover { color: var(--text); border-color: var(--accent); }
.popover-error { color: #ff6b6b; font-size: 11px; margin-top: 6px; }

.file-manager-popover { width: 240px; }
.file-manager-hint {
  display: block;
  margin-top: 8px;
  font-size: 11px;
  color: var(--text-dim);
}
.file-manage-list { display: flex; flex-direction: column; gap: 2px; max-height: 200px; overflow-y: auto; }
.file-manage-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 4px;
  border-radius: 5px;
}
.file-manage-row:hover { background: var(--bg-3); }
.file-manage-name { font-size: 12px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-manage-tag { font-size: 10px; color: var(--text-dim); background: var(--bg-2); padding: 2px 6px; border-radius: 999px; }
.file-manage-empty { font-size: 11.5px; color: var(--text-dim); padding: 8px 4px; line-height: 1.4; }
.file-manage-thumb {
  width: 28px;
  height: 22px;
  object-fit: cover;
  border-radius: 3px;
  flex-shrink: 0;
  border: 1px solid var(--border-soft);
}
.file-manage-thumb-video {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-2);
  color: var(--text-dim);
  font-size: 10px;
}
.file-manage-download {
  font-size: 13px;
  color: var(--accent);
  text-decoration: none;
  padding: 2px 4px;
  border-radius: 4px;
  flex-shrink: 0;
}
.file-manage-download:hover { background: var(--accent-soft); }
.file-manage-open {
  font-size: 11px;
  color: var(--accent);
  background: none;
  border: 1px solid var(--accent);
  border-radius: 5px;
  padding: 2px 7px;
  cursor: pointer;
}
.file-manage-open:hover { background: var(--accent-soft); }
.file-manage-delete {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 12px;
  padding: 2px 4px;
  border-radius: 4px;
  opacity: 0.7;
}
.file-manage-delete:hover { opacity: 1; background: rgba(214, 69, 80, 0.2); }
.editor-wrap {
  flex: 1 1 auto;
  min-height: 80px;
  overflow: hidden;
  position: relative;
}
.editor-wrap .CodeMirror {
  position: absolute;
  inset: 0;
  height: 100% !important;
  font-size: 13px;
}
.btn-toggle-whitespace.active { background: var(--bg-3); color: var(--text); box-shadow: var(--shadow-1); }
/* "Show whitespace" overlay — one faint dot per space, one per tab stop
   (tabs are 4 columns wide here, so a single centered dot reads better
   than one per column). currentColor keeps it legible in both themes. */
.cm-ws-space {
  background-image: radial-gradient(circle, currentColor 1px, transparent 1.4px);
  background-repeat: repeat-x;
  background-position: center;
  background-size: 1ch 1ch;
  opacity: 0.25;
}
.cm-ws-tab {
  background-image: radial-gradient(circle, currentColor 1px, transparent 1.4px);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 1ch;
  opacity: 0.2;
}
.console-wrap {
  flex: 0 0 240px;
  min-height: 60px;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--border-soft);
}
.console-toolbar {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  font-size: 11px;
  color: var(--text-dim);
  background: var(--bg-1);
}
.console-output {
  flex: 1;
  margin: 0;
  padding: 10px 12px;
  overflow: auto;
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 12px;
  background: var(--bg-0);
  white-space: pre-wrap;
  line-height: 1.5;
}
.console-output .err { color: #ff6b6b; }
.console-output .sys { color: var(--text-dim); font-style: italic; }

/* Smooth transitions only for users who haven't asked to reduce motion —
   also sidesteps a Chromium quirk where transition+transform can otherwise
   freeze the rotated/scaled state instead of animating or snapping to it. */
@media (prefers-reduced-motion: no-preference) {
  .board { transition: transform 0.15s ease; }
  .pi-board { transition: transform 0.2s ease; }
  .breadboard { transition: min-height 0.15s ease, transform 0.2s ease; }
  .comp-visual { transition: transform 0.15s ease; }
}

/* ==================== Auth: modal, form, user chip ==================== */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(8, 9, 14, 0.6);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
}
/* `.modal-overlay`'s own `display: flex` has the same specificity as the
   browser default `[hidden] { display: none; }` and loads after it, so it
   wins the tie unless overridden here — generic on the class (not just
   #auth-modal-overlay) so it covers every modal that uses this shell,
   including #projects-modal-overlay. */
.modal-overlay[hidden] {
  display: none !important;
}
.modal {
  position: relative;
  background: var(--bg-1);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-2);
  padding: 28px 26px 24px;
}
.modal-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: transparent;
  border: none;
  color: var(--text-dim);
  font-size: 16px;
  cursor: pointer;
}
.modal-close:hover { background: var(--bg-3); color: var(--text); }

/* ---- Add-component browser: centered modal, search + category sidebar + tile grid ---- */
.add-modal {
  width: min(680px, 92vw);
  height: min(560px, 82vh);
  padding: 22px 24px 24px;
  display: flex;
  flex-direction: column;
}
.add-title {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 14px;
}
.add-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.add-header { margin-bottom: 14px; }
.add-search {
  width: 100%;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 9px 12px;
  font-size: 13px;
}
.add-search:focus { outline: 1px solid var(--accent); }
.add-main {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 18px;
}
.add-categories {
  flex: 0 0 128px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.add-cat-btn {
  text-align: left;
  background: none;
  border: none;
  border-radius: 7px;
  color: var(--text-dim);
  font-size: 13px;
  padding: 8px 10px;
  cursor: pointer;
}
.add-cat-btn:hover { background: var(--bg-2); color: var(--text); }
.add-cat-btn.active { background: var(--bg-3); color: var(--text); font-weight: 600; }
.add-body.searching .add-categories { opacity: 0.35; pointer-events: none; }

.add-scroll {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  padding-right: 10px;
}

/* Shared "professional" thin scrollbar — every scrollable area in the app
   (board canvas, console, project/file lists, the Add-component modal)
   uses this instead of the browser's default scrollbar, for one
   consistent look. Firefox via scrollbar-width/-color, Chromium/Safari via
   the ::-webkit-scrollbar-* pseudo-elements. */
.board-scroll,
.console-output,
.project-list,
.file-manage-list,
.add-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.board-scroll::-webkit-scrollbar,
.console-output::-webkit-scrollbar,
.project-list::-webkit-scrollbar,
.file-manage-list::-webkit-scrollbar,
.add-scroll::-webkit-scrollbar {
  width: 9px;
  height: 9px;
}
.board-scroll::-webkit-scrollbar-track,
.console-output::-webkit-scrollbar-track,
.project-list::-webkit-scrollbar-track,
.file-manage-list::-webkit-scrollbar-track,
.add-scroll::-webkit-scrollbar-track {
  background: transparent;
}
.board-scroll::-webkit-scrollbar-thumb,
.console-output::-webkit-scrollbar-thumb,
.project-list::-webkit-scrollbar-thumb,
.file-manage-list::-webkit-scrollbar-thumb,
.add-scroll::-webkit-scrollbar-thumb {
  background-color: var(--border);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
.board-scroll::-webkit-scrollbar-thumb:hover,
.console-output::-webkit-scrollbar-thumb:hover,
.project-list::-webkit-scrollbar-thumb:hover,
.file-manage-list::-webkit-scrollbar-thumb:hover,
.add-scroll::-webkit-scrollbar-thumb:hover {
  background-color: #4a4c66;
}
.add-section + .add-section { margin-top: 20px; }
.add-section-heading {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  margin: 0 0 10px;
}
.add-body.searching .add-section-heading { display: none; }

.add-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.add-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: var(--bg-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 14px 10px 12px;
  cursor: pointer;
  text-align: center;
  position: relative;
}
.add-tile:hover { background: var(--bg-3); border-color: var(--border); }
.add-tile-icon-box {
  width: 42px;
  height: 42px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-1);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
}
.add-tile-icon { font-size: 21px; line-height: 1; }
.add-tile-name { font-size: 12.5px; font-weight: 600; color: var(--text); }
.add-tile-desc {
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--text-dim);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.add-tile[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.add-tile[disabled]:hover { background: var(--bg-2); border-color: var(--border-soft); }
.add-tile-used {
  position: absolute;
  top: 8px;
  right: 8px;
  font-size: 9px;
  color: var(--text-dim);
  background: var(--bg-1);
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  padding: 1px 7px;
}

.add-tile-locked { opacity: 0.75; }
.add-tile-locked:hover { background: var(--bg-2); border-color: #f0b23a; }
.add-tile-lock {
  position: absolute;
  top: 8px;
  right: 8px;
  font-size: 9px;
  font-weight: 600;
  color: #f0b23a;
  background: rgba(240, 178, 58, 0.14);
  border: 1px solid rgba(240, 178, 58, 0.4);
  border-radius: 999px;
  padding: 1px 7px;
}

.upsell-modal {
  max-width: 380px;
  text-align: center;
  padding: 28px 24px 24px;
}
.upsell-icon { font-size: 32px; margin-bottom: 8px; }
.upsell-title { font-size: 17px; margin: 0 0 10px; color: var(--text); }
.upsell-body { font-size: 13px; line-height: 1.5; margin: 0 0 20px; }
.upsell-actions {
  display: flex;
  justify-content: center;
  gap: 10px;
}

.add-header-preset { display: flex; align-items: center; gap: 12px; }
.add-back {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 12.5px;
  cursor: pointer;
  padding: 4px 0;
}
.add-back:hover { color: var(--text); }
.add-preset-title { font-size: 13px; color: var(--text-dim); }
.add-scroll-flat { padding-top: 4px; }

.auth-modal { width: 340px; }
.auth-tabs {
  display: flex;
  gap: 4px;
  background: var(--bg-2);
  border-radius: 8px;
  padding: 3px;
  margin-bottom: 18px;
}
.auth-tab {
  flex: 1;
  padding: 8px 0;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}
.auth-tab.active { background: var(--bg-3); color: var(--text); }

.google-btn-slot { display: flex; justify-content: center; min-height: 40px; }
.google-btn-disabled {
  width: 100%;
  text-align: center;
  padding: 10px;
  font-size: 12px;
  color: var(--text-dim);
  background: var(--bg-2);
  border: 1px dashed var(--border);
  border-radius: 6px;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 16px 0;
  color: var(--text-dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border-soft);
}

.auth-form { display: flex; flex-direction: column; gap: 12px; }
.field { display: flex; flex-direction: column; gap: 5px; }
/* Same `[hidden]` vs. own-`display` specificity tie as `.modal-overlay`
   above — each of these sets its own `display`, which wins over the
   browser's default `[hidden] { display: none }` unless overridden here.
   Without this, JS setting `.hidden = true` (signup-only fields, hiding
   Google sign-in) toggles the attribute but has no visual effect. */
.field[hidden],
.google-btn-slot[hidden],
.auth-divider[hidden],
.auth-tabs[hidden] {
  display: none !important;
}
.field label { font-size: 12px; color: var(--text-dim); }
.field input {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 9px 10px;
  color: var(--text);
  font-size: 13px;
}
.field input:focus { outline: none; border-color: var(--accent); }
.field select {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 9px 10px;
  color: var(--text);
  font-size: 13px;
}
.auth-error {
  font-size: 12px;
  color: #ff8a8a;
  background: rgba(214, 69, 80, 0.12);
  border-radius: 6px;
  padding: 8px 10px;
}
.auth-info {
  font-size: 12px;
  color: #4fd884;
  background: rgba(79, 216, 132, 0.12);
  border-radius: 6px;
  padding: 8px 10px;
  line-height: 1.4;
}
.auth-referral-toggle span { font-size: 13px; color: var(--text); }
.auth-referral-fields { border-left: 2px solid var(--border); padding-left: 10px; margin-left: 2px; }
.auth-submit { width: 100%; margin-top: 4px; }

.auth-area { display: flex; align-items: center; }
.user-menu { position: relative; }
.user-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 12px 4px 4px;
  cursor: pointer;
  color: var(--text);
}
.user-chip:hover { background: var(--bg-3); }
.user-menu-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 200px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.user-menu-dropdown[hidden] { display: none; }
.user-menu-role, .user-menu-plan {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 2px 4px 4px;
}
.user-menu-role-label, .user-menu-plan-label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; }
.user-menu-role-name, .user-menu-plan-name { font-size: 13.5px; font-weight: 600; color: var(--text); }
.user-menu-dashboard, .user-menu-upgrade, .user-menu-logout { width: 100%; text-align: center; margin-left: 0; }
.user-menu-divider { width: 100%; border: none; border-top: 1px solid var(--border); margin: 0; }
.user-avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.user-name { font-size: 12.5px; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.modal-title { font-size: 18px; font-weight: 600; margin: 0 0 16px; color: var(--text); }
.projects-modal { width: 440px; max-height: 82vh; display: flex; flex-direction: column; }
.save-example-modal { width: 380px; }
.save-example-hint { font-size: 12.5px; color: var(--text-dim); line-height: 1.5; margin: -8px 0 16px; }
.auth-subscribe-hint { font-size: 12.5px; color: var(--text-dim); line-height: 1.5; margin: 0 0 14px; }
.save-example-update-row,
.save-example-update-toggle { margin-bottom: 14px; }
.save-example-update-toggle span { font-size: 13px; color: var(--text); }
.save-example-classrooms-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 140px;
  overflow-y: auto;
  border: 1px solid var(--border-soft);
  border-radius: 8px;
  padding: 8px 10px;
}
.save-example-classrooms-list label {
  flex-direction: row;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text);
}

.save-example-thumb-field { margin-bottom: 14px; }
.save-example-thumb-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.save-example-thumb-preview {
  width: 90px;
  height: 68px;
  flex-shrink: 0;
  border-radius: 8px;
  border: 1px solid var(--border-soft);
  background: var(--bg-1);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.save-example-thumb-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.save-example-thumb-placeholder { font-size: 26px; opacity: 0.5; }
.save-example-thumb-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
}
.save-example-thumb-actions .btn-tiny { white-space: nowrap; }

/* Examples gallery */
.gallery-modal { width: 100%; max-width: 900px; }
.gallery-locked {
  text-align: center;
  padding: 40px 20px;
  font-size: 15px;
  color: var(--text-dim);
}
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 14px;
  max-height: 70vh;
  overflow-y: auto;
  padding: 2px;
}
.gallery-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  padding: 0;
}
.gallery-card:hover { border-color: var(--border); background: var(--bg-3); }
.gallery-card-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--bg-1);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.gallery-card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery-card-thumb .gallery-card-icon { font-size: 34px; opacity: 0.6; }
.gallery-card-body { padding: 10px 12px 12px; }
.gallery-card-title { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.gallery-card-category {
  display: inline-block;
  font-size: 10.5px;
  color: var(--text-dim);
  background: var(--bg-1);
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  padding: 1px 8px;
}
.gallery-section-heading {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  margin: 16px 0 10px;
  grid-column: 1 / -1;
}
.gallery-section-heading:first-child { margin-top: 2px; }
.gallery-grid > .muted { grid-column: 1 / -1; text-align: center; padding: 30px 10px; }

.gallery-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.gallery-header-row .modal-title { margin: 0; }

.gallery-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: -4px 0 16px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border-soft);
}
.gallery-search {
  flex: 1 1 200px;
  min-width: 160px;
  font-size: 13px;
  padding: 7px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-soft);
  background: var(--bg-1);
  color: var(--text);
}
.gallery-tabs {
  display: flex;
  gap: 4px;
  background: var(--bg-1);
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  padding: 3px;
}
.gallery-tab {
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 12.5px;
  font-weight: 600;
  padding: 5px 14px;
  border-radius: 999px;
  cursor: pointer;
}
.gallery-tab.active { background: var(--accent); color: #fff; }
.gallery-sensor-select {
  font-size: 12.5px;
  padding: 7px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-soft);
  background: var(--bg-1);
  color: var(--text);
}
.gallery-sensor-select.active { border-color: var(--accent); }

.gallery-card-empty {
  border-style: dashed;
  align-items: center;
  justify-content: center;
}
.gallery-card-empty .gallery-card-thumb { background: transparent; }
.gallery-card-empty .gallery-card-icon { font-size: 30px; }
.gallery-card-empty .gallery-card-title { text-align: center; }

.unsaved-changes-modal { width: 380px; }
.unsaved-changes-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

.muted { color: var(--text-dim); font-size: 12.5px; }

/* Logged-out state: no list, just a reason + a way to sign in. */
.projects-signin-prompt { text-align: center; padding: 24px 8px; color: var(--text-dim); font-size: 13px; }
.projects-signin-prompt .btn { margin-top: 14px; }

/* Which project (if any) is currently open, and — only when naming a
   brand-new project — the name field. Kept visually separate from the
   list below via .popover-divider. Saving itself happens via the one
   toolbar Save button, not from here. */
.projects-current { font-size: 12px; color: var(--text-dim); margin-bottom: 10px; }
.projects-current strong { color: var(--text); }
.saveas-row { display: flex; gap: 6px; margin-top: 8px; }
.saveas-row input {
  flex: 1;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  color: var(--text);
  font-size: 12px;
  min-width: 0;
}
.popover-divider { height: 1px; background: var(--border-soft); margin: 8px 0; }

.project-list { display: flex; flex-direction: column; gap: 3px; max-height: 320px; overflow-y: auto; margin-top: 4px; }
.project-list-empty { font-size: 12px; color: var(--text-dim); padding: 8px 2px; }
/* Each row splits cleanly into three independent hit targets — the name
   (opens it), a rename icon, and a delete icon — spaced far enough apart
   that a misclick can't turn "open" into "delete". */
.project-item {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px;
  border-radius: 6px;
}
.project-item.current { background: var(--accent-soft); }
.project-item-open {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 5px;
}
.project-item-open:hover { background: var(--bg-3); }
.project-item-name { font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;     color: greenyellow;     padding-bottom: 10px;}
.project-item-time { font-size: 10px; color: var(--text-dim); }
.project-item-rename,
.project-item-delete {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 13px;
  padding: 6px 7px;
  border-radius: 5px;
  flex-shrink: 0;
}
.project-item-rename:hover { background: var(--accent-soft); color: var(--accent); }
.project-item-delete:hover { background: rgba(214, 69, 80, 0.2); color: #ff8a8a; }
.project-item-rename-row { display: flex; gap: 6px; padding: 4px; flex: 1; }
.project-item-rename-row input {
  flex: 1;
  background: var(--bg-2);
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 5px 8px;
  color: var(--text);
  font-size: 12.5px;
  min-width: 0;
}
