* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  margin: 0;
  height: 100%;
  font-family: -apple-system, Segoe UI, Roboto, sans-serif;
  overflow: hidden;
}

#app {
  display: grid;
  /* The inspector column is `auto`, not a fixed 300px: the inspector is
     hidden (display: none) until a block is selected, and an auto column
     collapses to zero while it is — the canvas gets the full width back. */
  grid-template-columns: 1fr auto;
  grid-template-rows: 52px 1fr;
  grid-template-areas:
    "topbar topbar"
    "canvas inspector";
  height: 100%;
  height: 100dvh;
}

#topbar {
  grid-area: topbar;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  background: #1e2530;
  color: #e6e9ef;
  border-bottom: 1px solid #2c3644;
}

#topbar .brand {
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.file-toolbar,
.doc-sync {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Hidden on desktop — see the max-width:768px rules below, the only place
   either of these actually shows. */
.menu-toggle {
  display: none;
}

/* `contents` makes this wrapper invisible to the flex layout on desktop:
   its children (.file-toolbar, .doc-sync) lay out as direct children of
   #topbar exactly as if the wrapper weren't there. Only the mobile media
   query turns it into a real, positioned dropdown panel. */
.topbar-menu {
  display: contents;
}

.topbar-menu-backdrop {
  display: none;
}

/* Only the first of the two right-aligned topbar clusters needs to claim
   the leftover space — the second just follows it via #topbar's own gap. */
.file-toolbar {
  margin-left: auto;
}

.file-toolbar-button {
  background: none;
  border: 1px solid #2c3644;
  color: #c3c9d4;
  border-radius: 5px;
  padding: 5px 12px;
  font-size: 12px;
  cursor: pointer;
}

.file-toolbar-button:hover:not(:disabled) {
  color: #e6e9ef;
  border-color: #3a4556;
}

.file-toolbar-button:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Icon-only variant (undo/redo): square rather than padded for text. */
.file-toolbar-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  padding: 4px;
}

/* The dot marks edits not yet written into the page address. Placed after
   the label rather than before it so the button's text doesn't shift left
   and right as the state changes. */
.save-url-button.unsaved::after {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 6px;
  border-radius: 50%;
  background: #ffb454;
  vertical-align: middle;
}

/* Hangs below the banner rather than displacing it — the banner is a
   fixed-height row, and a message long enough to be useful would double
   its height. Fixed rather than absolute because on a phone its parent
   scrolls horizontally, which would clip and drag an absolute child
   along with it. */
.toolbar-toast {
  position: fixed;
  top: 60px;
  right: 16px;
  max-width: min(460px, calc(100vw - 32px));
  padding: 10px 14px;
  background: #1e2530;
  border: 1px solid #2c3644;
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  color: #c3c9d4;
  font-size: 12px;
  line-height: 1.45;
  z-index: 25;
}

.toolbar-toast[hidden] {
  display: none;
}

/* Bottom-right, stacked oldest-on-top, so a second toast while one is
   already showing doesn't cover it up. Kept clear of the FAB stack
   (bottom-right too) by sitting above it with margin, not overlapping. */
.app-toast-stack {
  position: fixed;
  right: 16px;
  bottom: 96px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: min(360px, calc(100vw - 32px));
  z-index: 40;
}

.app-toast {
  padding: 12px 14px;
  background: #1e2530;
  border: 1px solid #2c3644;
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  color: #c3c9d4;
  font-size: 13px;
  line-height: 1.5;
}

.app-toast-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
}

.app-toast-button {
  padding: 5px 12px;
  background: #2c3644;
  border: 1px solid #3a4556;
  border-radius: 6px;
  color: #e6e9ef;
  font-size: 12px;
  cursor: pointer;
}

.app-toast-button:hover {
  background: #354154;
}

/* Toggled on while the wires are marching. Blue rather than the green the
   session button uses: one says "this diagram is being edited with
   someone", the other only changes what you're looking at. */
.animate-button.active {
  color: #e6e9ef;
  border-color: #4f8cff;
  background: rgba(79, 140, 255, 0.15);
}

/* Live-session state, shown in the banner because it is a mode the whole
   app is in rather than a one-off action. The dot is the at-a-glance part:
   the label changes too, but a green pip reads without being read. */
.session-button.active {
  color: #e6e9ef;
  border-color: #3ecf5d;
}

.session-button.active::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 6px;
  border-radius: 50%;
  background: #3ecf5d;
  vertical-align: baseline;
}

.file-toolbar-divider {
  width: 1px;
  height: 18px;
  background: #2c3644;
}

.doc-sync-status {
  font-size: 11px;
  color: #8b93a3;
  white-space: nowrap;
}

.doc-sync-status[data-state="synced"] {
  color: #3ecf5d;
}

.doc-sync-status[data-state="error"] {
  color: #e5484d;
}

.doc-sync-status[data-state="saving"],
.doc-sync-status[data-state="loading"] {
  color: #ffb454;
}

.doc-sync-button {
  background: #2f6fed;
  color: white;
  border: none;
  border-radius: 5px;
  padding: 5px 12px;
  font-size: 12px;
  cursor: pointer;
}

.doc-sync-button:hover {
  background: #3f7bf5;
}

.doc-sync-button:disabled {
  background: #2c3644;
  color: #6b7889;
  cursor: default;
}

.doc-sync-settings {
  background: none;
  border: 1px solid #2c3644;
  border-radius: 5px;
  width: 26px;
  height: 26px;
  color: #8b93a3;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}

.doc-sync-settings:hover {
  color: #e6e9ef;
  border-color: #3a4556;
}

/* The navigate-up FAB, mirroring the add FAB in the opposite corner.
   Muted rather than accent-blue so "add a block" stays the one primary
   action on the canvas. */
/* Two-class selector on purpose: the base .fab rules are defined further
   down the file, so a single .fab-left would lose the background on
   source order alone. */
.fab.fab-left {
  right: auto;
  left: 20px;
  background: #2c3644;
}

.fab.fab-left:hover {
  background: #3a4556;
}

/* Floats over the canvas exactly where the name it's replacing is drawn,
   so renaming reads as editing the label in place. */
.name-editor {
  position: fixed;
  z-index: 30;
  box-sizing: border-box;
  padding: 2px 6px;
  background: #10151c;
  border: 1px solid #4f8cff;
  border-radius: 4px;
  color: #e6e9ef;
  font-family: -apple-system, Segoe UI, Roboto, sans-serif;
  font-size: 13px;
  text-align: center;
  outline: none;
}

.name-editor[hidden] {
  display: none;
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}

.breadcrumb::-webkit-scrollbar {
  display: none;
}

.breadcrumb .crumb {
  flex-shrink: 0;
  background: none;
  border: none;
  color: #8b93a3;
  font-size: 12px;
  padding: 2px 4px;
  cursor: pointer;
  border-radius: 3px;
}

.breadcrumb .crumb:hover {
  color: #c3c9d4;
  background: #2c3644;
}

/* Clickable too (opens rename), so it keeps the pointer cursor and hover
   feedback of an ordinary crumb — only the color marks it as "you are
   here" rather than "go here". */
.breadcrumb .crumb.current {
  color: #e6e9ef;
}

.breadcrumb .crumb.current:hover {
  color: #ffffff;
}

.breadcrumb .crumb-sep {
  flex-shrink: 0;
  color: #4a5568;
  font-size: 11px;
}

#scene-canvas {
  grid-area: canvas;
  display: block;
  width: 100%;
  height: 100%;
  background: #12161d;
  cursor: default;
  touch-action: none;
}

/* Floating action button — the app's single primary action, following the
   mobile FAB convention so the same control works for touch and mouse.
   Sits at the viewport edge while the inspector is hidden; the desktop
   media rule below shifts it left of the inspector column when that's
   open, so it stays over the canvas rather than inside the panel. */
.fab {
  position: fixed;
  right: 20px;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: none;
  background: #2f6fed;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  z-index: 20;
  transition: transform 0.15s ease, background 0.15s ease, opacity 0.15s ease;
}

.fab:hover {
  background: #3f7bf5;
  transform: translateY(-1px);
}

.fab:active {
  transform: translateY(0) scale(0.94);
}

/* Selection controls, stacked directly above the add FAB in the same
   corner. Anchored to the same edge and shifted by the same amount when
   the inspector opens (see the desktop media rule below), so the column
   stays a column. */
.fab-stack {
  position: fixed;
  right: 20px;
  bottom: calc(20px + 56px + 12px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 20;
}

.fab-stack[hidden] {
  display: none;
}

/* Smaller and muted: these act on an existing selection, so they must not
   compete with "add a block" for the eye. */
.fab.fab-mini {
  position: static;
  width: 44px;
  height: 44px;
  background: #2c3644;
}

.fab.fab-mini:hover {
  background: #3a4556;
}

.fab.fab-danger:hover {
  background: #a52a2f;
}

/* Opens to the left of the stack rather than above it: above is where the
   canvas content usually is, and the stack already sits near the bottom
   edge on a phone. */
.fab-palette {
  position: absolute;
  right: calc(100% + 12px);
  bottom: 0;
  display: grid;
  grid-template-columns: repeat(3, 28px);
  gap: 8px;
  padding: 10px;
  background: #1e2530;
  border: 1px solid #2c3644;
  border-radius: 10px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}

.fab-palette[hidden] {
  display: none;
}

.fab-swatch {
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 6px;
  cursor: pointer;
  background: none;
}

.fab-swatch:hover {
  border-color: #e6e9ef;
}

/* "Back to the default" is the absence of a colour, so it is drawn as an
   absence — an outline, not a sample of the default blue, which would
   read as just another choice. */
.fab-swatch-default {
  position: relative;
  background: #12161d;
}

.fab-swatch-default::after {
  content: '';
  position: absolute;
  inset: 6px;
  border: 1px dashed #8b93a3;
  border-radius: 3px;
}

.fab-swatch-custom {
  -webkit-appearance: none;
  appearance: none;
  background: none;
}

.fab-swatch-custom::-webkit-color-swatch-wrapper {
  padding: 2px;
}

.fab-swatch-custom::-webkit-color-swatch {
  border: none;
  border-radius: 4px;
}

/* Hidden until a block is actually selected (body.inspector-open, set by
   InspectorPanel.syncOpenState) — an always-there empty panel just ate a
   300px column to say "select a block". */
#inspector {
  grid-area: inspector;
  display: none;
  width: 300px;
  background: #1a1f28;
  color: #e6e9ef;
  border-left: 1px solid #2c3644;
  padding: 14px;
  overflow-y: auto;
  font-size: 13px;
}

body.inspector-open #inspector {
  display: block;
}

/* Desktop only: with the inspector column open, the viewport-anchored FAB
   would land inside the panel — shift it left of the column. (On mobile
   the inspector is a bottom sheet and the FAB is hidden behind it via the
   opacity rule in the mobile media query instead.) */
@media (min-width: 769px) {
  /* Only the right-hand FAB needs to dodge the inspector column; the
     left one is anchored to the opposite edge and never overlaps it. */
  body.inspector-open .fab:not(.fab-left),
  body.inspector-open .fab-stack {
    right: calc(300px + 20px);
  }
}

/* Mobile-only "open the inspector" affordance: the bottom sheet no longer
   auto-opens on selection (it covered most of the small screen the moment
   you tapped a block) — this button appears once a block is selected and
   opening is an explicit tap. Hidden entirely on desktop and while the
   sheet is already open. */
.inspector-toggle {
  display: none;
  position: fixed;
  /* Centred so it clears both FABs, which sit in the bottom corners. */
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  padding: 12px 18px;
  border-radius: 24px;
  border: 1px solid #3a4556;
  background: #1e2530;
  color: #e6e9ef;
  font-size: 14px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  cursor: pointer;
  z-index: 20;
}

#inspector h3 {
  margin: 0 0 12px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #8b93a3;
}

#inspector h4 {
  margin: 18px 0 10px;
  padding-top: 12px;
  border-top: 1px solid #2c3644;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #8b93a3;
}

#inspector .sheet-header {
  display: none;
}

#inspector .tab-bar {
  display: flex;
  gap: 4px;
  margin-bottom: 16px;
  border-bottom: 1px solid #2c3644;
}

#inspector .tab-button {
  flex: 1;
  background: none;
  border: none;
  color: #8b93a3;
  padding: 8px 4px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

#inspector .tab-button.active {
  color: #e6e9ef;
  border-bottom-color: #2f6fed;
}

#inspector .tab-button:hover {
  color: #c3c9d4;
}

#inspector .hint-text {
  margin: 10px 0 0;
  font-size: 11px;
  color: #6b7889;
  line-height: 1.4;
}

#inspector select {
  background: #10151c;
  border: 1px solid #2c3644;
  color: #e6e9ef;
  border-radius: 4px;
  padding: 5px 7px;
  font-size: 13px;
}

#inspector select:focus {
  outline: 1px solid #2f6fed;
}

#inspector .description-editor {
  width: 100%;
  background: #10151c;
  border: 1px solid #2c3644;
  color: #e6e9ef;
  border-radius: 6px;
  padding: 8px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.5;
  resize: vertical;
}

#inspector .description-editor:focus {
  outline: 1px solid #2f6fed;
}

#inspector .description-editor::placeholder {
  color: #4a5568;
}

#inspector .apply-row {
  margin-top: 8px;
  display: flex;
  justify-content: flex-end;
}

#inspector .apply-row button {
  background: #2c3644;
  color: #e6e9ef;
  border: none;
  border-radius: 4px;
  padding: 6px 12px;
  font-size: 12px;
  cursor: pointer;
}

#inspector .apply-row button:hover {
  background: #3a4556;
}

#inspector .port-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
}

#inspector .port-dir-select {
  flex-shrink: 0;
  width: 58px;
  padding: 5px 4px;
  font-size: 11px;
}

#inspector .port-name-input {
  flex: 1;
  min-width: 0;
}

#inspector .port-delete-button {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  background: none;
  border: 1px solid #2c3644;
  border-radius: 4px;
  color: #8b93a3;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

#inspector .port-delete-button:hover {
  color: #e5484d;
  border-color: #e5484d;
}

#inspector .port-desc-input {
  width: 100%;
  margin-bottom: 12px;
  font-size: 11px;
  color: #a8b1c2;
}

#inspector .delete-row {
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px solid #2c3644;
}

#inspector .delete-button {
  width: 100%;
  background: transparent;
  border: 1px solid #e5484d;
  color: #e5484d;
  border-radius: 4px;
  padding: 8px;
  font-size: 12px;
  cursor: pointer;
}

#inspector .delete-button:hover {
  background: rgba(229, 72, 77, 0.12);
}

#inspector .field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 10px;
}

#inspector label {
  color: #8b93a3;
  font-size: 11px;
}

#inspector input {
  background: #10151c;
  border: 1px solid #2c3644;
  color: #e6e9ef;
  border-radius: 4px;
  padding: 5px 7px;
  font-size: 13px;
}

#inspector input:focus {
  outline: 1px solid #2f6fed;
}

#inspector .empty {
  color: #8b93a3;
  font-style: italic;
}

#inspector .row {
  display: flex;
  gap: 8px;
}

#inspector .row .field {
  flex: 1;
}

/* App-like layout below the mobile breakpoint: the inspector becomes a
   bottom sheet that slides up over the full-bleed canvas instead of eating
   permanent screen width. */
@media (max-width: 768px) {
  #app {
    grid-template-columns: 1fr;
    grid-template-areas:
      "topbar"
      "canvas";
  }

  /* Six controls of labelled text don't fit a phone's width, and a button
     pushed off the edge is a button that doesn't exist. Rather than
     leaving the cluster to scroll sideways (nothing hinted there was more
     to find past the edge), it's tucked behind an explicit menu button
     instead — the brand goes too, since it's the one thing here that
     isn't a control and menu-toggle needs the room. */
  #topbar {
    gap: 8px;
    padding: 0 10px;
    position: relative;
  }

  #topbar .brand {
    display: none;
  }

  .menu-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-left: auto;
    padding: 0;
    background: none;
    border: 1px solid #2c3644;
    border-radius: 6px;
    color: #c3c9d4;
    cursor: pointer;
  }

  .menu-toggle:hover {
    color: #e6e9ef;
    border-color: #3a4556;
  }

  .topbar-menu-backdrop:not([hidden]) {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 19;
  }

  /* Overrides the desktop `display: contents` — here it's a real,
     positioned dropdown panel instead of a transparent layout wrapper. */
  .topbar-menu {
    display: none;
  }

  .topbar-menu.open {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    position: absolute;
    top: calc(100% + 8px);
    right: 10px;
    left: 10px;
    background: #1e2530;
    border: 1px solid #2c3644;
    border-radius: 12px;
    padding: 14px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
    z-index: 20;
    max-height: calc(100vh - 76px);
    overflow-y: auto;
  }

  /* Inside the dropdown there's no width pressure any more, so both
     clusters wrap onto as many lines as they need instead of the
     desktop's single scrollable row. */
  .topbar-menu.open .file-toolbar,
  .topbar-menu.open .doc-sync {
    flex-wrap: wrap;
    margin-left: 0;
  }

  .topbar-menu.open .file-toolbar-button {
    flex: 1 1 auto;
  }

  /* Selected but sheet not open yet — offer the explicit opener. */
  body.block-selected:not(.inspector-open) .inspector-toggle {
    display: block;
  }

  #inspector {
    position: fixed;
    width: auto;
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 65vh;
    border-left: none;
    border-top: 1px solid #2c3644;
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
    padding: 8px 16px 20px;
    padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
    transform: translateY(100%);
    transition: transform 0.25s ease;
    z-index: 15;
  }

  #inspector.open {
    transform: translateY(0);
  }

  #inspector .sheet-header {
    display: flex;
    justify-content: center;
    padding: 6px 0 10px;
  }

  #inspector .sheet-header .grabber {
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: #3a4556;
  }

  #inspector h3 {
    display: none;
  }

  body.inspector-open .fab,
  body.inspector-open .fab-stack {
    opacity: 0;
    pointer-events: none;
  }
}

/* --- Share dialog -------------------------------------------------- */

.share-dialog {
  width: min(560px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid #2c3644;
  border-radius: 12px;
  background: #1a1f28;
  color: #e6e9ef;
  font-family: -apple-system, Segoe UI, Roboto, sans-serif;
  font-size: 13px;
}

.share-dialog::backdrop {
  background: rgba(0, 0, 0, 0.55);
}

.share-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 10px;
}

.share-header h2 {
  margin: 0;
  font-size: 15px;
}

.share-close {
  background: none;
  border: none;
  color: #8b93a3;
  font-size: 15px;
  cursor: pointer;
  padding: 4px 6px;
  line-height: 1;
}

.share-close:hover {
  color: #e6e9ef;
}

.share-body {
  padding: 16px;
  max-height: 70vh;
  overflow-y: auto;
}

.share-field {
  margin-bottom: 14px;
}

.share-field label {
  display: block;
  margin-bottom: 5px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #8b93a3;
}

.share-row {
  display: flex;
  gap: 8px;
  align-items: center;
}

.share-row input {
  flex: 1;
  min-width: 0;
  background: #10151c;
  border: 1px solid #2c3644;
  border-radius: 5px;
  padding: 7px 9px;
  color: #e6e9ef;
  font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.share-row input:focus {
  outline: 1px solid #2f6fed;
}

.share-button {
  flex-shrink: 0;
  background: #2c3644;
  border: none;
  border-radius: 5px;
  padding: 7px 12px;
  color: #e6e9ef;
  font-size: 12px;
  cursor: pointer;
}

.share-button:hover {
  background: #3a4556;
}

.share-button-primary {
  background: #2f6fed;
  color: #fff;
}

.share-button-primary:hover {
  background: #3f7bf5;
}

.share-hint {
  margin: 6px 0 0;
  font-size: 11px;
  line-height: 1.45;
  color: #6b7889;
}

.share-hint a {
  color: #4f8cff;
}

.share-warning {
  margin: 14px 0 0;
  padding: 9px 11px;
  border-radius: 6px;
  border: 1px solid #4a3a1c;
  background: rgba(255, 180, 84, 0.08);
  color: #d8a760;
  font-size: 11px;
  line-height: 1.45;
}

.share-preview {
  margin-bottom: 12px;
  padding: 10px;
  border: 1px solid #2c3644;
  border-radius: 8px;
  background: #12161d;
  text-align: center;
}

.share-preview img {
  max-width: 100%;
  max-height: 220px;
}

