/* =========================================================
   ATELIER — Home Designer
   Modern, calm, architectural. Soft warm neutrals, a single
   sage-teal accent. No grain, no rotations, no italics gone wild.
   ========================================================= */

:root {
  /* surfaces */
  --bg:           #f6f5f1;   /* off-white warm */
  --surface:      #ffffff;
  --surface-2:    #fafaf7;
  --surface-3:    #f0eee8;
  --canvas:       #fcfbf7;

  /* ink */
  --ink:          #1d2025;
  --ink-90:       rgba(29, 32, 37, 0.92);
  --ink-70:       rgba(29, 32, 37, 0.70);
  --ink-50:       rgba(29, 32, 37, 0.50);
  --ink-40:       rgba(29, 32, 37, 0.40);
  --ink-30:       rgba(29, 32, 37, 0.30);
  --ink-20:       rgba(29, 32, 37, 0.20);
  --ink-15:       rgba(29, 32, 37, 0.15);
  --ink-10:       rgba(29, 32, 37, 0.10);
  --ink-05:       rgba(29, 32, 37, 0.05);

  /* accent: muted sage-teal */
  --accent:       #2d8275;
  --accent-deep:  #1f5e54;
  --accent-soft:  #d4e7e3;
  --accent-glow:  rgba(45, 130, 117, 0.22);

  /* secondary highlights */
  --warn:         #b8623a;
  --warn-soft:    #f3dccd;

  /* grid */
  --grid-minor:   rgba(29, 32, 37, 0.05);
  --grid-major:   rgba(29, 32, 37, 0.12);

  /* dimensions */
  --rail-w: 232px;
  --insp-w: 296px;
  --ruler-w: 26px;
  --bar-h: 60px;

  /* typography */
  --f-display: 'Fraunces', 'Times New Roman', serif;
  --f-body: 'Inter', system-ui, sans-serif;
  --f-mono: 'JetBrains Mono', monospace;

  /* radius / shadows */
  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 10px;
  --shadow-sm: 0 1px 2px rgba(29, 32, 37, 0.05);
  --shadow-md: 0 4px 12px rgba(29, 32, 37, 0.06), 0 1px 2px rgba(29, 32, 37, 0.04);
  --shadow-lg: 0 24px 48px -16px rgba(29, 32, 37, 0.14), 0 4px 12px rgba(29, 32, 37, 0.04);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--f-body);
  font-feature-settings: "ss01", "cv11";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

button { font-family: inherit; }
input { font-family: inherit; }

/* Make HTML hidden attribute actually hide flex containers */
[hidden] { display: none !important; }

/* =========================================================
   TOP BAR
   ========================================================= */
.studio-bar {
  position: relative;
  z-index: 10;
  height: var(--bar-h);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 0 20px;
  gap: 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--ink-10);
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand-mark {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-sm);
}

.brand-mark svg { width: 22px; height: 22px; }

.brand-name {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 19px;
  letter-spacing: -0.01em;
  line-height: 1;
  font-variation-settings: "opsz" 144;
}

.brand-sub {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-50);
  margin-top: 4px;
}

/* Editable project name in middle of bar */
.project-name-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.project-name {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 7px 32px 7px 14px;
  font-family: var(--f-display);
  font-size: 18px;
  font-weight: 500;
  color: var(--ink);
  text-align: center;
  min-width: 240px;
  max-width: 340px;
  outline: none;
  transition: all 160ms ease;
  cursor: text;
}

.project-name:hover {
  background: var(--surface-2);
  border-color: var(--ink-10);
}

.project-name:focus {
  background: var(--surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.project-edit-hint {
  position: absolute;
  right: 10px;
  width: 12px;
  height: 12px;
  color: var(--ink-30);
  pointer-events: none;
  opacity: 0;
  transition: opacity 160ms ease;
}

.project-name-wrap:hover .project-edit-hint { opacity: 1; }
.project-name:focus + .project-edit-hint { opacity: 0; }

/* Right meta + actions */
.studio-meta {
  display: flex;
  align-items: center;
  gap: 20px;
  justify-content: flex-end;
}

.meta-block {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
}

.meta-k {
  font-family: var(--f-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-50);
}

.meta-v {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ink);
  font-weight: 500;
}

.action-group {
  display: flex;
  align-items: center;
  gap: 4px;
  padding-left: 16px;
  border-left: 1px solid var(--ink-10);
}

.action-undo-redo {
  display: inline-flex;
  gap: 2px;
  margin-right: 8px;
  padding-right: 10px;
  border-right: 1px solid var(--ink-10);
}

.btn-icon {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--ink-70);
  padding: 7px;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: all 160ms ease;
}

.btn-icon svg {
  width: 16px;
  height: 16px;
}

.btn-icon:hover:not(:disabled) {
  background: var(--surface-2);
  color: var(--ink);
  border-color: var(--ink-10);
}

.btn-icon:disabled {
  color: var(--ink-30);
  cursor: not-allowed;
}

.btn-ghost,
.btn-accent {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  appearance: none;
  border-radius: var(--r-sm);
  padding: 8px 12px;
  font-family: var(--f-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: all 160ms ease;
}

.btn-ghost {
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink-70);
}

.btn-ghost svg { width: 14px; height: 14px; }

.btn-ghost:hover {
  background: var(--surface-2);
  color: var(--ink);
  border-color: var(--ink-10);
}

.btn-accent {
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  padding: 8px 14px;
  box-shadow: var(--shadow-sm);
}

.btn-accent svg { width: 14px; height: 14px; }

.btn-accent:hover {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
  box-shadow: 0 4px 12px var(--accent-glow);
  transform: translateY(-1px);
}

.btn-accent:active { transform: translateY(0); }

/* Generic ghost-styled dropdown (File / View menus). Same visual grammar as
   the export split menu, but without the accent color — these are secondary
   groupings, not primary actions. */
.btn-dropdown {
  position: relative;
  display: inline-flex;
}
.btn-menu {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.btn-menu-caret {
  width: 10px;
  height: 10px;
  opacity: 0.6;
}
.btn-menu-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 180px;
  background: white;
  border: 1px solid var(--ink-15);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 2px;
  animation: menuOpen 160ms ease both;
}
.btn-menu-item {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  padding: 8px 10px;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  transition: background 140ms ease;
  text-decoration: none;
}
.btn-menu-item:hover { background: var(--surface-2); }
.btn-menu-item svg { width: 14px; height: 14px; flex: 0 0 auto; }
.btn-menu-item small { color: var(--ink-50); font-size: 11px; }

/* Submenu (e.g. File → Export): expands in place inside the parent panel.
   Items are indented; the toggle's caret rotates when open. */
.btn-menu-sub {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-left: 22px;
}
.submenu-caret {
  margin-left: auto;
  transition: transform 140ms ease;
}
.btn-menu-item.open .submenu-caret { transform: rotate(180deg); }

.btn-menu-divider {
  height: 1px;
  background: var(--ink-10);
  margin: 4px 8px;
}

/* Icon-only button variant (Help "?" button). No gap for a missing label. */
.btn-icon { padding: 8px 10px; }
.btn-icon svg { margin: 0; }

/* Donate button: warm rust color to gently stand out from the ghost cluster
   without competing with the accent Export button. Heart icon fills on hover. */
.btn-donate {
  color: var(--warn);
}
.btn-donate svg { transition: fill 180ms ease; }
.btn-donate:hover svg { fill: var(--warn-soft); }
.btn-donate:hover { color: var(--warn); background: var(--warn-soft); }

/* Split export button: main + caret + menu */
.export-split {
  position: relative;
  display: inline-flex;
  align-items: stretch;
}

.btn-accent-split {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  padding-right: 10px;
}

.btn-accent-caret {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 1px solid rgba(255, 255, 255, 0.25);
  padding: 8px 8px;
  margin-left: 1px;
}

.btn-accent-caret svg { width: 12px; height: 12px; }
.btn-accent-caret:hover { transform: none; }
.btn-accent-split:hover { transform: none; box-shadow: var(--shadow-sm); }
.export-split:hover .btn-accent { background: var(--accent-deep); border-color: var(--accent-deep); }

.export-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 240px;
  background: white;
  border: 1px solid var(--ink-15);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 2px;
  animation: menuOpen 160ms ease both;
}

@keyframes menuOpen {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

.export-menu-item {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  padding: 9px 12px;
  text-align: left;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 1px;
  transition: background 140ms ease;
}

.export-menu-item:hover { background: var(--surface-2); }

.emi-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

.emi-sub {
  font-size: 11px;
  color: var(--ink-50);
}

/* =========================================================
   MAIN LAYOUT
   ========================================================= */
.studio {
  height: calc(100% - var(--bar-h));
  display: grid;
  grid-template-columns: var(--rail-w) 1fr var(--insp-w);
  position: relative;
}

/* Print-only footer line. Hidden on screen; the @media print block turns it
   into the last flex child of .studio so the plan and the caption share one
   unbreakable page block. */
.print-footer { display: none; }

/* =========================================================
   LEFT TRAY
   ========================================================= */
.tray {
  background: var(--surface);
  border-right: 1px solid var(--ink-10);
  padding: 20px 18px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  overflow-y: auto;
}

.tray-label {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-50);
  margin-bottom: 10px;
}

/* Collapsible tray sections (the furniture groups) */
.tray-label-collapsible {
  appearance: none;
  background: transparent;
  border: 0;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 0;
  text-align: left;
  cursor: pointer;
  color: var(--ink-50);
  font: inherit;
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  transition: color 140ms ease;
}

.tray-label-collapsible:hover { color: var(--accent-deep); }

.tray-chevron {
  display: inline-grid;
  place-items: center;
  width: 12px;
  height: 12px;
  color: var(--ink-40);
  transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), color 140ms ease;
}

.tray-chevron svg { width: 10px; height: 10px; }

.tray-label-collapsible:hover .tray-chevron { color: var(--accent-deep); }

/* Collapsed: chevron points right, body hidden */
.tray-section.collapsed .tray-chevron {
  transform: rotate(-90deg);
}

.tray-section.collapsed .tray-body {
  display: none;
}

.tool-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}

.tool {
  appearance: none;
  background: var(--surface);
  border: 1px solid var(--ink-10);
  border-radius: var(--r-sm);
  padding: 12px 6px 9px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  color: var(--ink-70);
  transition: all 160ms ease;
}

/* Tools that support drag-and-drop placement: grab cursor on hover so it's
   discoverable, and a subtle scale-down + glow while actively dragged. */
.tool[draggable="true"] { cursor: grab; }
.tool[draggable="true"]:active { cursor: grabbing; }
.tool[draggable="true"].tool-dragging {
  opacity: 0.6;
  transform: scale(0.96);
  border-color: var(--accent);
}

.tool svg {
  width: 22px;
  height: 22px;
  stroke-linecap: round;
}

.tool span {
  font-size: 11px;
  font-weight: 500;
}

.tool:hover {
  background: var(--surface-2);
  border-color: var(--ink-30);
  color: var(--ink);
}

.tool.active {
  background: var(--ink);
  color: white;
  border-color: var(--ink);
  box-shadow: var(--shadow-sm);
}

.tool.active svg { color: var(--accent-soft); }

.furniture-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5px;
}

.furn {
  appearance: none;
  background: var(--surface-2);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 10px 6px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  cursor: grab;
  color: var(--ink-70);
  transition: all 160ms ease;
}

.furn svg { width: 26px; height: 22px; }

.furn span {
  font-size: 10px;
  font-weight: 500;
}

.furn:hover {
  background: var(--accent-soft);
  color: var(--accent-deep);
  border-color: var(--accent);
  transform: translateY(-1px);
}

.furn:active { cursor: grabbing; transform: translateY(0); }

/* Snap selector */
.snap-select {
  appearance: none;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--ink-15);
  border-radius: var(--r-sm);
  padding: 9px 28px 9px 12px;
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ink);
  cursor: pointer;
  outline: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%231d2025' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round' d='M4 6 L8 10 L12 6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 14px 14px;
  transition: all 160ms ease;
}

.snap-select:hover { border-color: var(--ink-30); }

.snap-select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.tray-foot {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--ink-10);
}

.save-status {
  display: flex;
  align-items: center;
  gap: 8px;
}

.save-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-30);
  transition: background 200ms ease;
}

.save-dot.saved { background: var(--accent); }
.save-dot.dirty { background: var(--warn); }

.save-text {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink-50);
  text-transform: uppercase;
}

/* =========================================================
   CANVAS
   ========================================================= */
.canvas-wrap {
  position: relative;
  display: grid;
  grid-template-columns: var(--ruler-w) 1fr;
  grid-template-rows: var(--ruler-w) 1fr;
  background: var(--bg);
  overflow: hidden;
}

.ruler {
  background: var(--surface);
  border-bottom: 1px solid var(--ink-10);
  position: relative;
  overflow: hidden;
}

.ruler-corner {
  background: var(--surface);
  border-bottom: 1px solid var(--ink-10);
  border-right: 1px solid var(--ink-10);
  display: grid;
  place-items: center;
  color: var(--ink-30);
}

.ruler-corner svg { width: 14px; height: 14px; }

.ruler-top {
  grid-column: 2;
  grid-row: 1;
  background:
    var(--surface)
    repeating-linear-gradient(90deg,
      transparent 0,
      transparent 23px,
      var(--ink-15) 23px,
      var(--ink-15) 24px,
      transparent 24px,
      transparent 95px,
      var(--ink-40) 95px,
      var(--ink-40) 96px
    );
}

.ruler-left {
  grid-column: 1;
  grid-row: 2;
  border-bottom: 0;
  border-right: 1px solid var(--ink-10);
  background:
    var(--surface)
    repeating-linear-gradient(0deg,
      transparent 0,
      transparent 23px,
      var(--ink-15) 23px,
      var(--ink-15) 24px,
      transparent 24px,
      transparent 95px,
      var(--ink-40) 95px,
      var(--ink-40) 96px
    );
}

.paper {
  grid-column: 2;
  grid-row: 2;
  position: relative;
  overflow: hidden;
  cursor: default;
}

.paper-scroll {
  position: absolute;
  inset: 0;
  overflow: auto;
  scroll-behavior: auto;
  /* Grid + `safe center` centers the canvas while it fits, and falls back to
     start-alignment the moment it overflows — so the far edges stay
     scrollable. Plain flex/`center` would push overflow into negative scroll
     space where it can't be reached.

     Breathing room comes from a margin on #canvas rather than padding here:
     browsers don't reliably include a scroll container's trailing padding in
     scrollWidth/scrollHeight. */
  display: grid;
  place-content: safe center;
  /* `pan-x pan-y` keeps one-finger scrolling native but withholds pinch-zoom
     from the browser so the app can handle it as canvas zoom. Browser page
     zoom is useless here: the app shell is position:fixed and sized to the
     layout viewport, so a magnified page has nowhere to pan and strands the
     user wherever they pinched. */
  touch-action: pan-x pan-y;
}

#canvas {
  /* The visual "paper" treatment (background, border, shadow) lives on
     #canvas-bg now so SVG children can render outside the canvas bounds
     without losing the paper look. The <svg> element itself stays
     transparent and uses overflow:visible to let off-canvas items show.

     Centering uses `margin: auto` instead of flex centering on the parent.
     When the canvas is smaller than the scroll viewport, the auto margins
     center it; when it's larger (typical when zoomed in), the margins
     collapse to 0 and the full overflow becomes scrollable. Flex centering
     would clip the left/top half because the scroll container can't scroll
     into negative coords. */
  overflow: visible;
  display: block;
  /* Fixed gutter on all sides. Deliberately NOT `margin: auto`:
     when a block box is wider than its container, `auto` resolves the
     trailing margin to a large NEGATIVE value to satisfy the box equation,
     which shrinks scrollWidth below the element's own width and makes the
     right/bottom part unreachable. Centering is handled by place-content
     on .paper-scroll instead (grid, which — unlike flex — keeps overflow
     scrollable via `safe center`). */
  margin: 80px;
}

/* Drop shadow + border are on the background rect so they hug the canvas
   even when items are dragged into the surrounding gutter. */
#canvas-bg {
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.08)) drop-shadow(0 8px 24px rgba(0,0,0,0.12));
  stroke: var(--ink-10);
  stroke-width: 1;
}

/* Larger, more visible scrollbars on the canvas */
.paper-scroll::-webkit-scrollbar {
  width: 14px;
  height: 14px;
}

.paper-scroll::-webkit-scrollbar-track {
  background: var(--surface-2);
  border-left: 1px solid var(--ink-10);
  border-top: 1px solid var(--ink-10);
}

.paper-scroll::-webkit-scrollbar-thumb {
  background: var(--ink-20);
  border-radius: 7px;
  border: 3px solid var(--surface-2);
}

.paper-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--ink-40);
}

.paper-scroll::-webkit-scrollbar-corner {
  background: var(--surface-2);
}

/* status bar floating */
.status {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 7px 14px;
  background: var(--ink);
  color: white;
  border-radius: var(--r-md);
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  box-shadow: var(--shadow-md);
  z-index: 5;
}

.status-l, .status-r { display: flex; align-items: center; gap: 10px; }

.status-dot {
  width: 6px;
  height: 6px;
  background: var(--accent);
  border-radius: 50%;
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(45, 130, 117, 0.5); }
  50% { box-shadow: 0 0 0 5px rgba(45, 130, 117, 0); }
}

.status-text { color: rgba(255, 255, 255, 0.85); }
.status-coord { color: rgba(255, 255, 255, 0.7); }
.status-divider { color: rgba(255, 255, 255, 0.3); }
.status-zoom { font-variant-numeric: tabular-nums; }

.status-btn {
  appearance: none;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: white;
  border-radius: 3px;
  padding: 2px 7px;
  cursor: pointer;
  font-family: var(--f-mono);
  font-size: 11px;
  transition: all 150ms ease;
}

.status-btn:hover {
  background: white;
  color: var(--ink);
  border-color: white;
}

.status-fit { font-size: 9px; letter-spacing: 0.14em; }

/* =========================================================
   CANVAS — SVG element styles
   ========================================================= */

/* Building shell */
#canvas .building {
  cursor: move;
}
#canvas .building-fill {
  fill: var(--ink-05);
  stroke: var(--ink-50);
  stroke-width: 5;
  stroke-dasharray: 14 7;
}
#canvas .building.selected .building-fill {
  stroke: var(--accent);
  stroke-width: 6;
}
#canvas .building-label {
  font-family: var(--f-mono);
  font-size: 28px;
  font-weight: 500;
  letter-spacing: -0.04em;
  fill: var(--ink-50);
  text-transform: uppercase;
  text-anchor: middle;
  pointer-events: none;
  user-select: none;
}

#canvas .building-section-tag {
  font-family: var(--f-mono);
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -0.04em;
  fill: var(--ink-40);
  text-transform: uppercase;
  pointer-events: none;
  user-select: none;
}

/* Text annotation */
#canvas .text-item {
  cursor: move;
}
#canvas .text-hit {
  fill: transparent;
}
#canvas .text-item.selected .text-hit {
  fill: var(--accent-glow);
}
#canvas .text-content {
  user-select: none;
}

/* Standalone wall */
#canvas .wall-item {
  cursor: move;
}
#canvas .wall-fill {
  fill: var(--ink);
  stroke: none;
}
#canvas .wall-item:hover .wall-fill {
  fill: var(--accent-deep);
}
#canvas .wall-item.selected .wall-fill {
  fill: var(--accent-deep);
}

/* Rooms */
#canvas .room { cursor: move; }
#canvas {
  /* Fill used by door/window cut-outs — the rects that erase the wall behind
     an opening. It must match the room floor the opening sits in, otherwise
     the gap reads as a differently-colored patch. Rich mode overrides this
     (see the rich style block) to match its warmer floors. */
  --ds-door-cut: white;

  /* Halo color for room/hallway/outdoor labels. Matches the floor so the
     halo reads as "the text is sitting on the floor" rather than as an
     outline. Same reasoning as --ds-door-cut. */
  --ds-label-halo: white;
}

#canvas .room-fill {
  fill: white;
  stroke: none;
  transition: fill 160ms ease;
}
#canvas .room-wall {
  stroke: var(--ink);
  stroke-width: 8;
  stroke-linecap: square;
  pointer-events: none;
}
#canvas .room:hover .room-fill { fill: var(--accent-soft); }
#canvas .room.selected .room-fill {
  fill: var(--accent-soft);
}
#canvas .room.selected .room-wall {
  stroke: var(--accent-deep);
}

#canvas .room-name {
  font-family: var(--f-display);
  font-size: 40px;
  font-weight: 600;
  fill: var(--ink);
  text-anchor: middle;
  font-variation-settings: "opsz" 144;
  pointer-events: none;
  user-select: none;
  /* Halo: a thick light stroke painted BEHIND the fill, so the label
     stays readable when it crosses a wall or furniture. */
  paint-order: stroke;
  stroke: var(--ds-label-halo, white);
  stroke-width: 7;
  stroke-linejoin: round;
}

#canvas .room-dims {
  font-family: var(--f-mono);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.04em;
  fill: var(--ink-70);
  text-anchor: middle;
  text-transform: uppercase;
  pointer-events: none;
  user-select: none;
  /* Halo: a thick light stroke painted BEHIND the fill, so the label
     stays readable when it crosses a wall or furniture. */
  paint-order: stroke;
  stroke: var(--ds-label-halo, white);
  stroke-width: 5;
  stroke-linejoin: round;
}

#canvas .room-area-label {
  font-family: var(--f-mono);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.04em;
  fill: var(--accent-deep);
  text-anchor: middle;
  pointer-events: none;
  user-select: none;
  /* Halo: a thick light stroke painted BEHIND the fill, so the label
     stays readable when it crosses a wall or furniture. */
  paint-order: stroke;
  stroke: var(--ds-label-halo, white);
  stroke-width: 5;
  stroke-linejoin: round;
}

/* Hallway */
#canvas .hallway { cursor: move; }
#canvas .hallway-fill {
  fill: var(--ink-10);
  stroke: var(--ink-40);
  stroke-width: 5;
  stroke-linejoin: miter;
}
#canvas .hallway:hover .hallway-fill { fill: var(--accent-soft); }
#canvas .hallway.selected .hallway-fill {
  fill: var(--accent-soft);
  stroke: var(--accent-deep);
}
#canvas .hallway-label {
  font-family: var(--f-mono);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.04em;
  fill: var(--ink-70);
  text-transform: uppercase;
  text-anchor: middle;
  pointer-events: none;
  user-select: none;
  /* Halo: a thick light stroke painted BEHIND the fill, so the label
     stays readable when it crosses a wall or furniture. */
  paint-order: stroke;
  stroke: var(--ds-label-halo, white);
  stroke-width: 5;
  stroke-linejoin: round;
}

/* Outdoor */
#canvas .outdoor { cursor: move; }
#canvas .outdoor-fill {
  fill: var(--accent-soft);
  stroke: var(--accent-deep);
  stroke-width: 4;
  stroke-dasharray: 10 5;
  fill-opacity: 0.6;
}
#canvas .outdoor:hover .outdoor-fill { fill-opacity: 0.8; }
#canvas .outdoor.selected .outdoor-fill {
  stroke-width: 3;
  fill-opacity: 0.9;
  stroke-dasharray: none;
}

/* Outdoor labels sit on the pale mint patio fill (--accent-soft over the
   cream canvas), so their halo needs to match that, not the white room
   floor. Computed blend of accent-soft @60% over --canvas. */
#canvas .outdoor-name,
#canvas .outdoor-dims {
  --ds-label-halo: #e5efe9;
}

#canvas .outdoor-name {
  font-family: var(--f-display);
  font-size: 42px;
  font-weight: 600;
  fill: var(--accent-deep);
  text-anchor: middle;
  pointer-events: none;
  user-select: none;
  /* Halo: a thick light stroke painted BEHIND the fill, so the label
     stays readable when it crosses a wall or furniture. */
  paint-order: stroke;
  stroke: var(--ds-label-halo, white);
  stroke-width: 7;
  stroke-linejoin: round;
}

#canvas .outdoor-dims {
  font-family: var(--f-mono);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.04em;
  fill: var(--accent-deep);
  text-anchor: middle;
  text-transform: uppercase;
  pointer-events: none;
  user-select: none;
  opacity: 0.85;
  /* Halo: a thick light stroke painted BEHIND the fill, so the label
     stays readable when it crosses a wall or furniture. */
  paint-order: stroke;
  stroke: var(--ds-label-halo, white);
  stroke-width: 5;
  stroke-linejoin: round;
}

/* Furniture */
#canvas .furn-item {
  cursor: move;
  opacity: 0.78;
  transition: filter 160ms ease, opacity 160ms ease;
}

#canvas .furn-item:hover {
  opacity: 1;
  filter: drop-shadow(0 1px 2px var(--accent-glow));
}

#canvas .furn-item.selected,
#canvas .furn-item.multi-selected {
  opacity: 1;
}

#canvas .furn-item.selected .furn-hit {
  fill: var(--accent-soft);
  fill-opacity: 0.4;
}

#canvas .furn-hit {
  fill: transparent;
}

/* Resize handles */
#handles-layer .handle {
  fill: white;
  stroke: var(--accent);
  stroke-width: 3;
  cursor: pointer;
  filter: drop-shadow(0 2px 4px rgba(29, 32, 37, 0.18));
}

#handles-layer .handle.corner { cursor: nwse-resize; }
#handles-layer .handle.corner-ne { cursor: nesw-resize; }
#handles-layer .handle.corner-sw { cursor: nesw-resize; }
#handles-layer .handle.edge-n,
#handles-layer .handle.edge-s { cursor: ns-resize; }
#handles-layer .handle.edge-e,
#handles-layer .handle.edge-w { cursor: ew-resize; }

#handles-layer .selection-outline {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-dasharray: 8 5;
  pointer-events: none;
}

/* Rubber-band marquee — translucent fill + dashed border so it stays
   readable over both grid and items. Sits in #preview-layer. */
#canvas .marquee-rect {
  fill: var(--accent);
  fill-opacity: 0.08;
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 4 3;
  pointer-events: none;
}

/* Furniture rotate handle */
#handles-layer .rotate-line {
  stroke: var(--accent);
  stroke-width: 1;
  stroke-dasharray: 2 2;
  pointer-events: none;
}

#handles-layer .rotate-handle {
  fill: white;
  stroke: var(--accent);
  stroke-width: 2;
  cursor: grab;
}

/* =========================================================
   INSPECTOR
   ========================================================= */
.inspector {
  background: var(--surface);
  border-left: 1px solid var(--ink-10);
  padding: 20px 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 22px;
  overflow-y: auto;
}

.insp-section { display: flex; flex-direction: column; }

.insp-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--ink-10);
}

/* Collapsible header — looks like the static one but acts like a button */
.insp-head-collapsible {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--ink-10);
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding: 4px 0 6px;
  cursor: pointer;
  color: inherit;
  text-align: left;
  font: inherit;
  transition: color 140ms ease;
}

.insp-head-collapsible:hover { color: var(--accent-deep); }
.insp-head-collapsible:hover .insp-label { color: var(--accent-deep); }

.insp-head-collapsible .insp-label {
  flex: 1;
}

.insp-chevron {
  display: inline-grid;
  place-items: center;
  width: 14px;
  height: 14px;
  color: var(--ink-50);
  transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), color 140ms ease;
}

.insp-chevron svg { width: 12px; height: 12px; }

.insp-head-collapsible:hover .insp-chevron { color: var(--accent-deep); }

/* When collapsed, rotate the chevron to point right and hide the body */
.insp-section.collapsed .insp-chevron {
  transform: rotate(-90deg);
}

.insp-section.collapsed .insp-body {
  display: none;
}

.insp-section.collapsed .insp-head-collapsible {
  margin-bottom: 0;
}

.insp-label {
  font-family: var(--f-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

.insp-num {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  color: var(--ink-40);
}

/* Schedule */
.schedule {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sch-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 0;
  border-bottom: 1px solid var(--ink-10);
}

.sch-item:last-child { border-bottom: 0; }

/* Rows that map to a canvas item are clickable. The dataset attr is only
   present on rows with a selKind, so the Interior Total row stays plain. */
.sch-item[data-sel-kind] {
  cursor: pointer;
  margin: 0 -6px;
  padding-left: 6px;
  padding-right: 6px;
  border-radius: 4px;
  transition: background 0.12s ease;
}
.sch-item[data-sel-kind]:hover { background: var(--ink-05, rgba(0,0,0,0.04)); }

.sch-name {
  font-family: var(--f-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.2;
}

.sch-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.sch-dims {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.sch-area {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-70);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

.sch-total {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--ink-20) !important;
  border-bottom: 0 !important;
}

.sch-total .sch-name { font-weight: 600; color: var(--accent-deep); }
.sch-total .sch-area { font-weight: 600; color: var(--accent-deep); }

.sch-empty {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-40);
  font-style: italic;
  letter-spacing: 0.04em;
}

/* Layers */
.layers {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}

.layer-row {
  display: grid;
  grid-template-columns: 24px 12px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--ink-10);
}

.layer-row:last-child { border-bottom: 0; }

.layer-eye {
  width: 22px;
  height: 13px;
  appearance: none;
  background: var(--ink-15);
  border: 0;
  border-radius: 7px;
  padding: 0;
  position: relative;
  cursor: pointer;
  transition: background 200ms ease;
}

.layer-eye::after {
  content: "";
  position: absolute;
  top: 1.5px;
  left: 1.5px;
  width: 10px;
  height: 10px;
  background: white;
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
  transition: left 200ms ease;
}

.layer-eye.on { background: var(--accent); }
.layer-eye.on::after { left: 10px; }

.layer-swatch {
  width: 10px;
  height: 10px;
  background: var(--c);
  border-radius: 2px;
}

.layer-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
}

.layer-count {
  font-family: var(--f-mono);
  font-size: 10px;
  color: var(--ink-40);
  font-variant-numeric: tabular-nums;
  background: var(--surface-3);
  padding: 1px 6px;
  border-radius: 8px;
  min-width: 20px;
  text-align: center;
}

/* Properties */
.props {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.prop-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 7px 0;
  border-bottom: 1px solid var(--ink-10);
}

.prop-row:last-of-type { border-bottom: 0; }

.prop-k {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--ink-50);
  text-transform: uppercase;
}

.prop-v {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ink);
  font-weight: 500;
}

.prop-row-name { flex-direction: column; align-items: stretch; gap: 6px; }
.prop-row-inline { gap: 12px; }

.prop-input {
  appearance: none;
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--ink-15);
  border-radius: var(--r-sm);
  padding: 7px 9px;
  font-size: 13px;
  color: var(--ink);
  outline: none;
  transition: all 160ms ease;
}

.prop-input:hover { border-color: var(--ink-30); }

.prop-input-textarea {
  font-family: var(--f-body);
  resize: vertical;
  min-height: 60px;
  line-height: 1.4;
}

.text-align-group {
  display: inline-flex;
  gap: 2px;
  background: var(--surface-2);
  border: 1px solid var(--ink-15);
  border-radius: var(--r-sm);
  padding: 2px;
}

.text-align-btn {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: 3px;
  padding: 4px 8px;
  cursor: pointer;
  color: var(--ink-50);
  display: grid;
  place-items: center;
  transition: background 140ms ease, color 140ms ease;
}

.text-align-btn svg { width: 14px; height: 14px; }

.text-align-btn:hover {
  color: var(--ink);
  background: rgba(0, 0, 0, 0.04);
}

.text-align-btn.active {
  background: var(--accent);
  color: white;
}

/* Toggle-button group: used for stair "Direction" (UP / DN). Same visual
   pattern as text alignment, but with text labels and a slightly different
   layout. */
.btn-toggle-group {
  display: inline-flex;
  border: 1px solid var(--ink-10);
  border-radius: 4px;
  overflow: hidden;
}
.btn-toggle {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 4px 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ink-50);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.btn-toggle + .btn-toggle {
  border-left: 1px solid var(--ink-10);
}
.btn-toggle:hover { color: var(--ink); background: rgba(0,0,0,0.04); }
.btn-toggle.active {
  background: var(--accent);
  color: white;
}

.prop-color-input {
  appearance: none;
  width: 60px;
  height: 30px;
  padding: 2px;
  border: 1px solid var(--ink-15);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  cursor: pointer;
}

.prop-color-input::-webkit-color-swatch-wrapper { padding: 0; }
.prop-color-input::-webkit-color-swatch { border: 0; border-radius: 2px; }

.prop-input:focus {
  background: white;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.prop-input-num {
  width: 110px;
  text-align: right;
  font-family: var(--f-mono);
  font-size: 12px;
}

.prop-section-label {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-50);
  margin-top: 14px;
  margin-bottom: 6px;
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.prop-hint {
  font-family: var(--f-body);
  font-size: 10px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink-40);
  font-style: italic;
}

.open-sides-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 4px;
  margin-bottom: 8px;
}

.open-side-btn {
  appearance: none;
  background: var(--surface-2);
  border: 1px solid var(--ink-10);
  border-radius: var(--r-sm);
  padding: 6px 4px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--ink-70);
  font-family: var(--f-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  transition: all 140ms ease;
}

.open-side-btn svg { width: 22px; height: 22px; }

.open-side-btn:hover {
  background: var(--surface);
  border-color: var(--ink-30);
  color: var(--ink);
}

/* "Open" state — wall is removed from the room */
.open-side-btn.open {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-deep);
}

.open-side-btn.open .side-line {
  stroke: var(--accent);
  stroke-dasharray: 3 2;
  opacity: 0.6;
}

.open-sides-center {
  background: var(--ink-05);
  border-radius: 2px;
}

.btn-delete {
  margin-top: 10px;
  appearance: none;
  background: transparent;
  border: 1px solid var(--ink-15);
  border-radius: var(--r-sm);
  padding: 8px 12px;
  font-family: var(--f-body);
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-70);
  cursor: pointer;
  transition: all 160ms ease;
}

.btn-delete:hover {
  border-color: var(--warn);
  color: var(--warn);
  background: var(--warn-soft);
}

.btn-ghost-small {
  appearance: none;
  background: var(--surface-2);
  border: 1px solid var(--ink-15);
  border-radius: var(--r-sm);
  padding: 7px 10px;
  font-family: var(--f-body);
  font-size: 11px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  flex: 1;
  transition: all 160ms ease;
}

.btn-ghost-small:hover {
  border-color: var(--accent);
  color: var(--accent-deep);
  background: var(--accent-soft);
}

.furn-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.furn-actions .btn-delete { margin-top: 0; flex: 1; }

/* =========================================================
   TOAST
   ========================================================= */
.toast {
  position: fixed;
  bottom: 70px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--ink);
  color: white;
  padding: 10px 18px;
  border-radius: var(--r-md);
  font-size: 13px;
  font-weight: 500;
  box-shadow: var(--shadow-lg);
  z-index: 100;
  opacity: 0;
  transition: all 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
  pointer-events: none;
}

.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* Hover chip — small floating info box showing item dimensions */
.hover-chip {
  position: fixed;
  z-index: 90;
  background: var(--ink);
  color: white;
  padding: 6px 10px;
  border-radius: var(--r-sm);
  font-size: 12px;
  line-height: 1.3;
  box-shadow: var(--shadow-md);
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
  display: flex;
  flex-direction: column;
  gap: 2px;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 120ms ease, transform 120ms ease;
}

.hover-chip.visible {
  opacity: 1;
  transform: translateY(0);
}

.hover-chip-name {
  font-family: var(--f-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.005em;
}

.hover-chip-dims {
  font-family: var(--f-mono);
  font-size: 11px;
  color: rgba(255, 255, 255, 0.75);
  letter-spacing: 0.02em;
}

/* =========================================================
   CONTEXT MENU
   ========================================================= */
.ctx-menu {
  position: fixed;
  min-width: 200px;
  background: white;
  border: 1px solid var(--ink-15);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: 13px;
  animation: menuOpen 140ms ease both;
  user-select: none;
}

.ctx-item {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  padding: 7px 12px 7px 28px;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  color: var(--ink);
  position: relative;
  transition: background 120ms ease;
  font-family: inherit;
}

.ctx-item:hover {
  background: var(--surface-2);
}

.ctx-item.ctx-danger:hover {
  background: var(--warn-soft);
  color: var(--warn);
}

.ctx-item-icon {
  position: absolute;
  left: 8px;
  width: 14px;
  height: 14px;
  display: grid;
  place-items: center;
  color: var(--ink-50);
}

.ctx-item:hover .ctx-item-icon { color: var(--ink); }
.ctx-item.ctx-danger:hover .ctx-item-icon { color: var(--warn); }

.ctx-item-icon svg { width: 13px; height: 13px; }

.ctx-item-shortcut {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-40);
  margin-left: auto;
}

.ctx-item:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background: transparent !important;
}

.ctx-divider {
  height: 1px;
  background: var(--ink-10);
  margin: 4px 6px;
}

.ctx-heading {
  padding: 6px 12px 4px;
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-40);
}

/* =========================================================
   MODAL DIALOG (resize)
   ========================================================= */
.modal {
  position: fixed;
  inset: 0;
  z-index: 250;
  display: grid;
  place-items: center;
}

.modal-overlay {
  position: absolute;
  inset: 0;
  background: rgba(29, 32, 37, 0.36);
  animation: fadeIn 180ms ease both;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.modal-panel {
  position: relative;
  background: white;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  width: 320px;
  max-width: calc(100% - 32px);
  /* Never taller than the viewport, so a modal's footer (Next / Save / etc.)
     is always reachable. Uses dvh where supported so mobile browser chrome
     collapsing doesn't push the footer off-screen. */
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  display: flex;
  flex-direction: column;
  animation: modalIn 200ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
/* Head and footer stay put; only the body scrolls. */
.modal-head,
.welcome-footer,
.modal-actions {
  flex: 0 0 auto;
}
.modal-body,
.welcome-body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  min-height: 0;   /* lets the flex child actually shrink */
}

@keyframes modalIn {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 18px 8px;
}

.modal-title {
  font-family: var(--f-display);
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
}

.modal-close {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--ink-50);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0 6px;
  border-radius: var(--r-sm);
  transition: color 140ms ease;
}

.modal-close:hover { color: var(--ink); }

.modal-body {
  padding: 4px 18px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.modal-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.modal-label {
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-50);
}

.modal-hint {
  font-size: 12px;
  color: var(--ink-50);
  line-height: 1.4;
  margin-top: 4px;
}

.modal-hint code {
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 3px;
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px 16px;
  border-top: 1px solid var(--ink-10);
}

/* Share modal */
.share-state {
  padding: 4px 0;
}

.share-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 28px 0;
  color: var(--ink-70);
  font-size: 14px;
}

.share-loading-spinner {
  width: 28px;
  height: 28px;
  border: 3px solid var(--ink-15);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 800ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.share-hint {
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-70);
  margin: 0 0 16px;
}

.share-hint b { color: var(--ink); font-weight: 600; }

.share-url-row {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}

.share-url-input {
  flex: 1;
  appearance: none;
  background: var(--surface-2);
  border: 1px solid var(--ink-15);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ink);
  outline: none;
  user-select: all;
  transition: all 140ms ease;
}

.share-url-input:focus {
  background: white;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

#share-copy-btn {
  white-space: nowrap;
  min-width: 80px;
}

#share-copy-btn.copied {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}

.share-meta {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-50);
  letter-spacing: 0.04em;
}

.share-error-msg {
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  border-left-width: 4px;
  color: var(--warn);
  padding: 12px 14px;
  border-radius: var(--r-sm);
  font-size: 13px;
}

/* Welcome (Getting Started) modal */
.modal-panel-wide {
  width: 520px;
  /* Sized against the viewport, not the parent track. The .modal parent is a
     grid with place-items:center, and a center-aligned grid item is sized to
     max-content and permitted to overflow its track — so a percentage
     max-width here was being beaten by long unwrapped content, pushing the
     panel (and its close button and Next button) off the side of a phone
     screen. min() against vw can't be overflowed that way. */
  max-width: min(520px, calc(100vw - 32px));
  min-width: 0;
}

.welcome-body {
  padding: 0 24px 8px;
  position: relative;
  /* min-height keeps the steps from jumping around as content varies, but it
     must yield on short screens or the footer gets pushed out of reach. */
  min-height: 360px;
}
@media (max-height: 700px), (max-width: 600px) {
  .welcome-body { min-height: 0; }
}

.welcome-step {
  display: none;
  animation: welcomeStepIn 240ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.welcome-step-active {
  display: block;
}

@keyframes welcomeStepIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

.welcome-step-num {
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-deep);
  margin-bottom: 10px;
}

.welcome-step-title {
  font-family: var(--f-display);
  font-size: 22px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 16px;
}

.welcome-step-art {
  background: var(--surface-2);
  border: 1px solid var(--ink-10);
  border-radius: var(--r-md);
  padding: 14px;
  margin-bottom: 16px;
  display: grid;
  place-items: center;
}

.welcome-step-art svg {
  width: 100%;
  max-width: 320px;
  height: auto;
  display: block;
}

.welcome-step p {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-70);
  margin: 0 0 10px;
}

.welcome-step p b {
  color: var(--ink);
  font-weight: 600;
}

.welcome-tip {
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
  padding: 8px 12px;
  border-radius: 3px;
  font-size: 13px !important;
  color: var(--accent-deep) !important;
}

.welcome-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px 20px;
  border-top: 1px solid var(--ink-10);
  gap: 16px;
}

.welcome-progress {
  display: flex;
  gap: 6px;
}

.welcome-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-15);
  cursor: pointer;
  transition: background 160ms ease, transform 160ms ease;
}

.welcome-dot:hover {
  background: var(--ink-30);
}

.welcome-dot-active {
  background: var(--accent);
  transform: scale(1.2);
}

.welcome-nav {
  display: flex;
  gap: 8px;
}

.welcome-nav button {
  min-width: 80px;
}

.welcome-nav button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* =========================================================
   ENTRANCE ANIMATION
   ========================================================= */
.studio-bar { animation: fadeDown 400ms ease both; }
.tray { animation: slideRight 500ms ease both 80ms; }
.inspector { animation: slideLeft 500ms ease both 120ms; }
.canvas-wrap { animation: fadeUp 500ms ease both 100ms; }

@keyframes fadeDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes slideRight {
  from { opacity: 0; transform: translateX(-14px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes slideLeft {
  from { opacity: 0; transform: translateX(14px); }
  to   { opacity: 1; transform: translateX(0); }
}

/* scrollbars */
.tray::-webkit-scrollbar,
.inspector::-webkit-scrollbar { width: 6px; }
.tray::-webkit-scrollbar-track,
.inspector::-webkit-scrollbar-track { background: transparent; }
.tray::-webkit-scrollbar-thumb,
.inspector::-webkit-scrollbar-thumb { background: var(--ink-15); border-radius: 3px; }
.tray::-webkit-scrollbar-thumb:hover,
.inspector::-webkit-scrollbar-thumb:hover { background: var(--ink-40); }

/* =========================================================
   CANVAS SIZE inputs
   ========================================================= */
.canvas-size {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.canvas-size-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

.canvas-size-k {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--ink-50);
  text-transform: uppercase;
}

.canvas-size .prop-input-num {
  width: 96px;
}

.shape-presets {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  gap: 4px;
}

.shape-preset {
  appearance: none;
  background: var(--surface);
  border: 1px solid var(--ink-10);
  border-radius: var(--r-sm);
  padding: 8px 4px 5px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  cursor: pointer;
  color: var(--ink-70);
  transition: all 140ms ease;
}

.shape-preset svg { width: 28px; height: 22px; }

.shape-preset span {
  font-family: var(--f-mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.shape-preset:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-deep);
}

.shape-hint {
  margin-top: 8px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--ink-50);
  font-style: italic;
}

.canvas-presets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  margin-top: 2px;
}

.canvas-preset {
  appearance: none;
  background: var(--surface-2);
  border: 1px solid var(--ink-10);
  border-radius: var(--r-sm);
  padding: 6px 4px;
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink-70);
  cursor: pointer;
  transition: all 140ms ease;
}

.canvas-preset:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-deep);
}

/* =========================================================
   ALIGNMENT panel
   ========================================================= */
.align-section {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 12px;
}

.align-label {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-50);
  margin-top: 8px;
  margin-bottom: 2px;
}

.align-label:first-child { margin-top: 0; }

.align-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 4px;
}

.align-btn {
  appearance: none;
  background: var(--surface-2);
  border: 1px solid var(--ink-10);
  border-radius: var(--r-sm);
  padding: 8px;
  cursor: pointer;
  color: var(--ink-70);
  display: grid;
  place-items: center;
  transition: all 140ms ease;
}

.align-btn svg { width: 22px; height: 22px; }

.align-btn:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-deep);
}

/* =========================================================
   DOORS — exterior + interior
   ========================================================= */
#canvas .door-item {
  cursor: move;
}

#canvas .door-item.selected .door-hit {
  fill: var(--accent-glow);
}

#canvas .door-hit {
  fill: transparent;
}

#canvas .extdoor-hit {
  fill: transparent;
}

/* Cut-out that erases the wall behind a door/window opening.
   Uses --ds-door-cut (not --canvas) so the gap matches the ROOM FLOOR it sits
   in. --canvas is the paper color outside the building — visibly creamier than
   the white room fill, which made every opening read as a tinted patch. */
#canvas .door-slot,
#canvas .window-cut {
  fill: var(--ds-door-cut, white);
  stroke: none;
}

/* Windows */
#canvas .window-item {
  cursor: move;
}
#canvas .window-hit { fill: transparent; }
#canvas .window-item.selected .window-hit { fill: rgba(43, 92, 184, 0.15); }

/* Dimensions */
#canvas .dim-item {
  cursor: move;
}
#canvas .dim-line {
  stroke: var(--ink-70);
  stroke-width: 2;
  fill: none;
}
#canvas .dim-tick {
  stroke: var(--ink-70);
  stroke-width: 2;
}

/* Dimension-chain tool preview (while clicking points). The baseline runs
   through committed points; ticks show each click and the hover candidate. */
#canvas .chain-baseline {
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 6 4;
  pointer-events: none;
}
#canvas .chain-tick {
  stroke: var(--accent);
  stroke-width: 1.5;
  pointer-events: none;
}
/* Full-canvas dashed crosshair that follows the cursor (or the snapped
   candidate point) while the chain tool is active. Subtle so it doesn't
   compete with the design, but visible enough to sight against corners. */
#canvas .chain-crosshair {
  stroke: var(--accent);
  stroke-width: 1;
  stroke-dasharray: 4 4;
  opacity: 0.45;
  pointer-events: none;
}
#canvas .dim-text {
  font-family: var(--f-mono);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.04em;
  fill: var(--ink);
  text-anchor: middle;
  paint-order: stroke;
  stroke: var(--canvas);
  stroke-width: 6;
  user-select: none;
}
#canvas .dim-hit {
  fill: transparent;
  stroke: transparent;
  stroke-width: 28;
}
#canvas .dim-item.selected .dim-line,
#canvas .dim-item.selected .dim-tick {
  stroke: var(--accent);
}
#canvas .dim-item.selected .dim-text { fill: var(--accent-deep); }

/* Dimension endpoint handles */
#handles-layer .dim-endpoint {
  fill: white;
  stroke: var(--accent);
  stroke-width: 3;
  cursor: grab;
  filter: drop-shadow(0 2px 4px rgba(29, 32, 37, 0.18));
}

/* Overlap warning */
#canvas .has-overlap > .hallway-fill,
#canvas .has-overlap > .outdoor-fill {
  stroke: #d04036;
  stroke-width: 8;
  stroke-dasharray: 14 6;
}
#canvas .room.has-overlap .room-wall {
  stroke: #d04036;
  stroke-dasharray: 14 6;
}

#canvas .has-overlap.room .room-fill {
  fill: rgba(208, 64, 54, 0.05);
}

/* Furniture inside an overlap gets a red dropshadow */
#canvas .furn-item.has-overlap {
  filter: drop-shadow(0 0 6px rgba(208, 64, 54, 0.6));
}

#canvas .overlap-region {
  fill: url(#overlap-hatch);
  pointer-events: none;
}

/* Tray validation section */
.check-row {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  padding: 4px 0;
  user-select: none;
}

.check-row input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.check-box {
  width: 16px;
  height: 16px;
  border-radius: 3px;
  border: 1px solid var(--ink-30);
  background: white;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  transition: all 140ms ease;
}

.check-box::after {
  content: "";
  width: 9px;
  height: 5px;
  border-left: 2px solid white;
  border-bottom: 2px solid white;
  transform: rotate(-45deg) translate(1px, -1px);
  opacity: 0;
  transition: opacity 140ms ease;
}

.check-row input:checked + .check-box {
  background: var(--accent);
  border-color: var(--accent);
}

.check-row input:checked + .check-box::after { opacity: 1; }

.check-row:hover .check-box { border-color: var(--ink-50); }

.check-label {
  font-size: 13px;
  color: var(--ink);
}

.overlap-summary {
  margin-top: 10px;
  padding: 8px 10px;
  background: rgba(208, 64, 54, 0.08);
  border: 1px solid rgba(208, 64, 54, 0.25);
  border-left: 3px solid #d04036;
  border-radius: var(--r-sm);
  font-size: 12px;
  color: #8a2922;
  line-height: 1.4;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.overlap-summary-head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
}

.overlap-summary-head::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #d04036;
  animation: pulse-red 1.8s ease-in-out infinite;
}

@keyframes pulse-red {
  0%, 100% { box-shadow: 0 0 0 0 rgba(208, 64, 54, 0.6); }
  50% { box-shadow: 0 0 0 5px rgba(208, 64, 54, 0); }
}

.overlap-summary-list {
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
  font-size: 11px;
  color: #8a2922;
  opacity: 0.85;
}

.overlap-summary-list li {
  padding: 2px 0;
  font-family: var(--f-mono);
}

/* Alignment guides during drag */
#guides-layer .align-guide {
  stroke: #d946ef;
  stroke-width: 4;
  stroke-dasharray: 12 8;
  pointer-events: none;
  opacity: 0.95;
}

#guides-layer .align-dot {
  fill: #d946ef;
  stroke: white;
  stroke-width: 1;
  pointer-events: none;
}

/* Multi-select outline on items */
#canvas .multi-selected .building-fill,
#canvas .multi-selected .hallway-fill,
#canvas .multi-selected .outdoor-fill {
  stroke: var(--accent);
  stroke-width: 7;
  stroke-dasharray: 8 5;
}
#canvas .multi-selected .room-wall {
  stroke: var(--accent);
  stroke-width: 7;
  stroke-dasharray: 8 5;
}

/* Key item: the last-clicked item in a multi-selection becomes the
   "anchor" that alignment buttons align everything else to. We render its
   outline SOLID instead of dashed so the user can see at a glance which
   item the others will align to. */
#canvas .multi-selected.key-item .building-fill,
#canvas .multi-selected.key-item .hallway-fill,
#canvas .multi-selected.key-item .outdoor-fill,
#canvas .multi-selected.key-item .room-wall {
  stroke-dasharray: none;
}
/* For point items (doors, windows, furniture, text) the multi-selected
   styling is a translucent overlay rather than a stroke — give the key
   item a teal glow so it still stands out. */
#canvas .furn-item.multi-selected.key-item,
#canvas .door-item.multi-selected.key-item,
#canvas .window-item.multi-selected.key-item,
#canvas .text-item.multi-selected.key-item {
  filter: drop-shadow(0 0 4px var(--accent));
}

/* Hint paragraph inside property panels (small italic explanatory text). */
.props-hint {
  margin: 4px 0 12px;
  font-size: 11.5px;
  color: var(--ink-70);
  line-height: 1.4;
  font-style: italic;
}
.props-hint strong {
  font-style: normal;
  color: var(--accent-deep);
}

/* =========================================================
   RESPONSIVE
   ========================================================= */
@media (max-width: 1200px) {
  :root { --rail-w: 204px; --insp-w: 264px; }
}

/* 701–980px: the inspector no longer fits beside the canvas, so it moves into
   the bottom sheet (see the <=700px block, which shares the sheet rules via
   .sheet-host). The tray still fits in the grid at this width. */
@media (max-width: 980px) {
  .studio { grid-template-columns: 200px 1fr; }
  .studio-meta { gap: 12px; }
}

@media (max-width: 700px) {
  .studio { grid-template-columns: 1fr; }
  .studio-meta .meta-block { display: none; }

  /* Desktop's 80px gutter would swallow a phone screen. Keep in sync with the
     `padding` in the zoom-fit handler in app.js. */
  #canvas { margin: 12px; }

  /* The status hint is desktop guidance ("Hold Space + drag to pan") that
     doesn't apply on touch, and its wrapping made the bar 105px tall — which
     is what the sheet toggle was colliding with. Drop it and keep the zoom
     controls, so the bar stays one short row. */
  .status { bottom: 10px; gap: 10px; max-width: calc(100vw - 24px); }
  .status-l { display: none; }
  .status-r { flex: 1 1 auto; justify-content: center; }
}

/* ---- Bottom sheet ----------------------------------------------------
   Panels that no longer fit beside the canvas move into a fixed sheet at the
   bottom of the screen with a tab switcher, instead of being display:none —
   which previously left the inspector unreachable from 980px down and the
   whole editor unusable on a phone.

   Which panels participate depends on width:
     <=980px  inspector (Properties/Schedule/Layers) — tray still fits
     <=700px  tray (Tools) joins it, so the sheet gets both tabs */
@media (max-width: 980px) {
  /* The sheet is a fixed panel with the tab bar riding on its top edge. Only
     the active tab's panel is displayed, so neither needs to know the other's
     height. `.sheet-panel` is applied by app.js to whichever panels belong in
     the sheet at the current width, so this rule set isn't duplicated. */
  .sheet-panel {
    display: none;
    position: fixed;
    left: 0; right: 0;
    bottom: 0;
    width: auto;
    height: 62dvh;
    margin: 0;
    border: 0;
    border-top: 1px solid var(--ink-10);
    border-radius: 14px 14px 0 0;
    background: var(--paper, white);
    box-shadow: 0 -8px 28px rgba(29,32,37,0.16);
    /* .tray has `animation: slideRight ... both` whose filled final keyframe
       sets a transform that would beat the sheet's own translate. */
    animation: none !important;
    transform: translateY(100%);
    transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
    z-index: 90;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* Top padding clears the tab bar, which is fixed at the sheet's top edge
       (see .sheet-tabs: bottom = sheet height - bar height). */
    padding: 56px 14px calc(20px + env(safe-area-inset-bottom, 0px));
  }

  body.sheet-open.sheet-tools .tray.sheet-panel,
  body.sheet-open.sheet-props .inspector.sheet-panel {
    display: flex;
    flex-direction: column;
    gap: 14px;
    transform: translateY(0);
  }

  /* Sections must stack; without this they inherit `row` from the flex
     container above and collapse to a sliver. */
  .tray > .tray-section,
  .inspector > .insp-section {
    flex: 0 0 auto;
    width: 100%;
  }

  /* Tab bar. A sibling of the panels rather than a child, so one bar serves
     both instead of being duplicated into each. The panels are a fixed 62dvh
     tall, so anchoring the bar that far up puts it exactly on the sheet's top
     edge; --sheet-bar-h keeps that offset and the panels' top padding in sync. */
  .sheet-tabs {
    --sheet-bar-h: 50px;
    display: none;
    position: fixed;
    left: 0; right: 0;
    bottom: calc(62dvh - var(--sheet-bar-h));
    height: var(--sheet-bar-h);
    z-index: 92;
    align-items: center;
    gap: 6px;
    padding: 8px 10px;
    background: var(--paper, white);
    border-top-left-radius: 14px;
    border-top-right-radius: 14px;
    border-bottom: 1px solid var(--ink-10);
  }
  body.sheet-open .sheet-tabs { display: flex; }
  .sheet-tab {
    flex: 1 1 0;
    padding: 9px 10px;
    border: 1px solid var(--ink-10);
    border-radius: 999px;
    background: white;
    font-family: var(--f-body);
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-70);
    cursor: pointer;
  }
  .sheet-tab.is-active {
    background: var(--accent);
    border-color: var(--accent);
    color: white;
  }
  .sheet-close {
    flex: 0 0 auto;
    width: 34px; height: 34px;
    display: grid;
    place-items: center;
    border: 1px solid var(--ink-10);
    border-radius: 50%;
    background: white;
    color: var(--ink-70);
    cursor: pointer;
  }
  .sheet-close svg { width: 14px; height: 14px; }

  /* Floating button that opens the sheet. */
  .sheet-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    position: fixed;
    left: 50%;
    /* Sits above the status bar, which on mobile is one ~34px row at
       bottom:10px. Overlapping it would bury the zoom controls. */
    bottom: 56px;
    transform: translateX(-50%);
    z-index: 95;
    padding: 10px 20px;
    border: 1px solid var(--ink-10);
    border-radius: 999px;
    background: white;
    box-shadow: var(--shadow-md);
    font-family: var(--f-body);
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
    cursor: pointer;
    margin-bottom: env(safe-area-inset-bottom, 0);
  }
  .sheet-toggle svg { width: 14px; height: 14px; }
  body.sheet-open .sheet-toggle { display: none; }

  .sheet-scrim {
    display: none;
    position: fixed; inset: 0;
    background: rgba(29,32,37,0.28);
    z-index: 85;
  }
  body.sheet-open .sheet-scrim { display: block; }
}

/* Touch drag vs. scroll arbitration.
   `touch-action` is resolved by the compositor from the hit-tested element
   upward BEFORE any JS runs, so preventDefault() in a touchstart handler
   cannot win it back: a one-finger drag starting on a canvas item was claimed
   as a scroll and the touch stream was cancelled a few pixels in — the item
   would move slightly, then freeze.

   So the intent has to be declared in CSS. `none` on canvas items means a drag
   that starts on one is ours to handle; the empty canvas keeps pan-x/pan-y so
   dragging blank space still scrolls.

   IMPORTANT: touch-action is NOT inherited — the browser reads it from the
   element the touch actually hits. Items are groups (`g.room`) whose visible
   child (`rect.room-fill`) is what gets hit, so setting this on `[data-id]`
   alone left the child at `auto` and the scroller still stole the gesture:
   the drag moved a few pixels, then pointercancel fired with the finger still
   down. Hence the descendant selector. */
#canvas [data-id],
#canvas [data-id] * { touch-action: none; }

/* While a drawing tool is armed, a drag anywhere on the sheet draws rather
   than scrolls — otherwise the first drag of a new room gets eaten by the
   scroller. app.js toggles .tool-armed on <body>. */
body.tool-armed .paper-scroll,
body.tool-armed #canvas,
body.tool-armed #canvas * { touch-action: none; }

/* Chrome decides scroll-vs-drag from the nearest SCROLLABLE ancestor, not just
   the hit element — so `touch-action: none` on the item alone still let
   #paper-scroll claim the gesture and fire pointercancel a few pixels in, with
   the finger still down. It also can't be fixed from the pointerdown handler:
   by then the browser has already committed. It has to be in effect BEFORE the
   touch begins, so it keys off having a selection — i.e. exactly when a drag
   is possible. Tap to select (scrolling still normal), then drag it.
   app.js toggles .has-selection on <body>. */
body.has-selection .paper-scroll { touch-action: none; }

/* Touch gestures card (mobile first-run). */
.touch-tips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.touch-tips li {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-70);
}
.touch-tips b { color: var(--ink); }
.touch-tip-icon {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 12%, white);
  color: var(--accent);
}
.touch-tip-icon svg { width: 18px; height: 18px; }

/* A furniture tile tapped to arm it for placement (tap tile, then tap canvas).
   Needs to read as "pending" so it's obvious the next canvas tap will drop it. */
.furn.furn-armed {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, white);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent);
}

/* The sheet chrome is for narrow widths only; keep it out of the desktop
   layout even though the elements are in the DOM at every width. Must match
   the sheet's own breakpoint (980px) — when this said 701px it silently
   killed the tab bar and toggle across 701–980, where the inspector had
   already moved into the sheet and was the only way to reach Properties. */
@media (min-width: 981px) {
  .sheet-tabs, .sheet-toggle, .sheet-scrim { display: none !important; }
}

/* =========================================================
   ACCOUNT MENU + AUTH MODAL + MY DESIGNS
   ========================================================= */
.account-wrap {
  position: relative;
  display: inline-flex;
}
.btn-avatar {
  gap: 8px;
}
.avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: white;
  font-size: 12px;
  font-weight: 600;
  display: grid;
  place-items: center;
  letter-spacing: 0;
}
.account-email {
  font-size: 12px;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink-70);
}
.account-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  background: white;
  border: 1px solid var(--ink-10);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.10);
  min-width: 180px;
  padding: 6px;
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.account-menu-item {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 8px 10px;
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
  border-radius: 4px;
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  text-decoration: none;   /* for anchor variant */
}
.account-menu-item:hover {
  background: rgba(0,0,0,0.05);
}
.account-menu-item svg {
  width: 14px;
  height: 14px;
}

/* Auth modal */
.auth-tabs {
  display: flex;
  border-bottom: 1px solid var(--ink-10);
  margin-bottom: 18px;
}
.auth-tab {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 10px 16px;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-50);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.auth-tab:hover { color: var(--ink); }
.auth-tab-active {
  color: var(--accent-deep);
  border-bottom-color: var(--accent);
}
.btn-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 10px 14px;
  background: white;
  color: var(--ink);
  border: 1px solid var(--ink-10);
  border-radius: 6px;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background 140ms ease;
}
.btn-google:hover { background: rgba(0,0,0,0.03); }
.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 14px 0;
  color: var(--ink-50);
  font-size: 12px;
}
.auth-divider::before, .auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--ink-10);
}
.auth-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.auth-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-70);
}
.auth-input {
  padding: 9px 12px;
  border: 1px solid var(--ink-10);
  border-radius: 6px;
  font: inherit;
  font-size: 14px;
}
.auth-input:focus {
  outline: 2px solid var(--accent-glow);
  outline-offset: -1px;
  border-color: var(--accent);
}
.auth-submit {
  margin-top: 6px;
  padding: 10px 14px;
  background: var(--accent);
  color: white;
  border: 0;
  border-radius: 6px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background 140ms ease;
}
.auth-submit:hover { background: var(--accent-deep); }
.auth-submit:disabled { background: var(--ink-30); cursor: not-allowed; }
.auth-err {
  font-size: 13px;
  color: var(--warn);
  padding: 6px 0;
}
.auth-ok {
  font-size: 13px;
  color: var(--accent-deep);
  padding: 6px 0;
}
.auth-blurb {
  font-size: 13px;
  color: var(--ink-70);
  margin: 0;
  line-height: 1.5;
}
.auth-foot {
  text-align: center;
  font-size: 12px;
  color: var(--ink-50);
  margin-top: 4px;
}
.auth-foot a {
  color: var(--accent-deep);
  text-decoration: none;
}
.auth-foot a:hover { text-decoration: underline; }

/* My Designs list */
.designs-toolbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 14px;
}
.designs-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 60vh;
  overflow-y: auto;
}
.designs-loading,
.designs-empty {
  text-align: center;
  color: var(--ink-50);
  padding: 32px;
  font-size: 14px;
}
.design-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 6px;
  transition: background 140ms ease;
}
.design-row:hover {
  background: rgba(0,0,0,0.04);
}
.design-row-main {
  flex: 1;
  min-width: 0;
}
.design-row-name {
  font-family: var(--f-body);
  font-weight: 500;
  font-size: 14px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.design-row-meta {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-50);
  margin-top: 2px;
}
.design-row-actions {
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity 140ms ease;
}
.design-row:hover .design-row-actions,
.design-row:focus-within .design-row-actions {
  opacity: 1;
}
.design-row-btn {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 6px 10px;
  font: inherit;
  font-size: 12px;
  color: var(--ink-70);
  cursor: pointer;
  border-radius: 4px;
}
.design-row-btn:hover {
  background: rgba(0,0,0,0.07);
  color: var(--ink);
}
.design-row-btn.danger:hover { color: var(--warn); }

/* Contact form: textarea shares .auth-input styling but needs its own
   resize/height behavior. */
.contact-textarea {
  resize: vertical;
  min-height: 110px;
  font-family: var(--f-body);
  line-height: 1.5;
}

/* Honeypot field — visually and semantically removed for humans, still
   present in the DOM (and fillable) for naive bots. Not display:none, since
   some bots skip those. */
.contact-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* =========================================================
   DONATE MODAL
   ========================================================= */
.donate-blurb {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-70);
  margin: 0 0 20px;
}
.donate-methods {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.donate-method {
  border: 1px solid var(--ink-10);
  border-radius: 8px;
  padding: 14px;
  background: var(--surface-2);
}
.donate-method-head { margin-bottom: 10px; }
.donate-method-title {
  font-family: var(--f-display);
  font-weight: 500;
  font-size: 18px;
  color: var(--ink);
}
.donate-method-sub {
  font-size: 12px;
  color: var(--ink-50);
  margin-top: 2px;
}
.donate-method-body {
  display: flex;
  gap: 14px;
  align-items: center;
}
.donate-qr {
  flex: 0 0 auto;
  width: 100px;
  height: 100px;
  background: white;
  border: 1px solid var(--ink-10);
  border-radius: 4px;
  padding: 6px;
  display: grid;
  place-items: center;
}
.donate-qr svg,
.donate-qr img { width: 100%; height: 100%; display: block; }
.donate-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
.donate-actions .btn-accent {
  text-decoration: none;
  text-align: center;
}
.donate-actions .donate-copy.copied { color: var(--accent-deep); }

/* Style toggle button "active" state (Rich mode on). */
#style-toggle-btn.active {
  background: var(--accent-soft);
  color: var(--accent-deep);
}
#style-toggle-btn.active svg { color: var(--accent-deep); }

/* =========================================================
   RICH STYLE OVERRIDES
   When style=rich, we swap the flat white room fills for textured SVG
   patterns, thicken and darken walls, warm up the canvas background, and
   give furniture soft shadows. All keyed off the .style-rich class on
   the #canvas SVG element.

   Every rule here targets #canvas.style-rich to be strictly additive —
   removing the class puts you back in draft mode instantly.
   ========================================================= */

#canvas.style-rich {
  /* Rich palette CSS variables — only apply inside the rich canvas so the
     rest of the app UI stays consistent. */
  --rp-ink:         #3a2b1c;              /* warm dark brown (walls, text) */
  --rp-ink-70:      rgba(58, 43, 28, 0.75);
  --rp-ink-50:      rgba(58, 43, 28, 0.5);
  --rp-canvas:      #f5eddb;              /* warm cream background */
  --rp-wall:        #3a2b1c;
  --rp-wall-hi:     #6b4a2a;              /* wall highlight (interior side) */
  --rp-accent:      #a06a3a;              /* warm rust for selected/hover */
  --rp-accent-soft: #f0d7b8;
  --rp-outdoor:     #a9c39a;              /* soft green for outdoor areas */
}

/* Warmer canvas background. */
#canvas.style-rich .room-fill,
#canvas.style-rich .hallway-fill,
#canvas.style-rich .outdoor-fill,
#canvas.style-rich .building-fill { transition: fill 200ms ease; }

/* Rooms: textured pattern per room (set via inline `data-rich-fill` on the
   rect, applied at render time from the JS helper). We also keep a subtle
   inner border for legibility against dark walls. */
#canvas.style-rich .room-fill {
  fill: url(#rp-wood-warm);   /* fallback */
  stroke: none;
}
/* When JS stamps data-rich-fill on the rect, prefer that. */
#canvas.style-rich .room-fill[data-rich-fill="tile-hex"]    { fill: url(#rp-tile-hex); }
#canvas.style-rich .room-fill[data-rich-fill="tile-square"] { fill: url(#rp-tile-square); }
#canvas.style-rich .room-fill[data-rich-fill="wood-cool"]   { fill: url(#rp-wood-cool); }
#canvas.style-rich .room-fill[data-rich-fill="concrete"]    { fill: url(#rp-concrete); }
#canvas.style-rich .room-fill[data-rich-fill="carpet"]      { fill: url(#rp-carpet); }
#canvas.style-rich .room-fill[data-rich-fill="wood-warm"]   { fill: url(#rp-wood-warm); }
#canvas.style-rich .room-fill[data-rich-fill="deck"]        { fill: url(#rp-deck); }

/* Walls (rooms): thicker, warm dark brown.
   NOTE: no filter here. Room walls are <line> elements, whose bounding box
   has ZERO height — and a filter with percentage-based region (the SVG
   default, objectBoundingBox units) resolves to an empty region against a
   zero-height bbox, which makes the element render completely invisible.
   The shadow is applied to the whole .room group instead, where the bbox
   has real dimensions. */
#canvas.style-rich .room-wall {
  stroke: var(--rp-wall);
  stroke-width: 10;
  stroke-linecap: butt;
  stroke-linejoin: miter;
}
/* Depth for the room's walls, applied at the group level so the filter has a
   non-degenerate bounding box to work with. */
#canvas.style-rich .room {
  filter: url(#rp-wall-shadow);
}
#canvas.style-rich .room.selected .room-wall { stroke: var(--rp-accent); }
#canvas.style-rich .room:hover .room-fill { fill-opacity: 0.85; }
#canvas.style-rich .room.selected .room-fill { fill-opacity: 0.85; }

/* Standalone walls: also warm brown filled with shadow. */
#canvas.style-rich .wall-fill {
  fill: var(--rp-wall);
  filter: url(#rp-wall-shadow);
}
#canvas.style-rich .wall-item:hover .wall-fill { fill: var(--rp-accent); }
#canvas.style-rich .wall-item.selected .wall-fill { fill: var(--rp-accent); }

/* Building shell: warmer stroke + light warm cream fill. */
#canvas.style-rich .building-fill {
  fill: rgba(245, 237, 219, 0.35);
  stroke: var(--rp-wall);
  stroke-width: 6;
  stroke-dasharray: 16 8;
}
#canvas.style-rich .building-label,
#canvas.style-rich .building-section-tag {
  fill: var(--rp-ink-70);
}

/* Hallways: light warm wood, no stroke (walls handle the border via
   adjacent rooms; standalone hallways get a hint of inner border). */
#canvas.style-rich .hallway-fill {
  fill: url(#rp-wood-warm);
  fill-opacity: 0.75;
  stroke: var(--rp-ink-50);
  stroke-width: 4;
}
#canvas.style-rich .hallway-label {
  fill: var(--rp-ink-70);
}

/* Outdoor: deck pattern for patios/porches/decks; softer green as fallback. */
#canvas.style-rich .outdoor-fill {
  fill: url(#rp-deck);
  fill-opacity: 0.7;
  stroke: var(--rp-ink-50);
  stroke-width: 3;
  stroke-dasharray: 8 4;
}
#canvas.style-rich .outdoor-name { fill: var(--rp-ink); }
#canvas.style-rich .outdoor-dims { fill: var(--rp-ink-70); }

/* Room labels: warmer color. */
#canvas.style-rich .room-name       { fill: var(--rp-ink); }
#canvas.style-rich .room-dims       { fill: var(--rp-ink-70); }
#canvas.style-rich .room-area-label { fill: var(--rp-accent); }

/* Dimensions: slightly muted so they don't compete with the richer scene. */
#canvas.style-rich .dim-line,
#canvas.style-rich .dim-tick { stroke: var(--rp-ink-70); }
#canvas.style-rich .dim-text { fill: var(--rp-ink); }

/* Doors + windows: warmer palette.
   .door-slot / .window-cut are excluded — those are the strokeless rects
   that erase the wall behind an opening. Giving them a stroke would paint
   lines right where the wall is meant to disappear. */
#canvas.style-rich .door-item > rect:not(.door-hit):not(.door-slot),
#canvas.style-rich .door-item > path:not(.door-hit) {
  stroke: var(--rp-wall);
}
#canvas.style-rich .door-item[data-door-kind="garage"] rect:not(.door-hit):not(.door-slot),
#canvas.style-rich .door-item[data-door-kind="garage"] line {
  stroke: var(--warn);
}
#canvas.style-rich .door-slot,
#canvas.style-rich .window-cut {
  stroke: none !important;
}
/* Rich floors are warm, so the cut-out has to be too — a white gap would
   glare against the wood/tile patterns. Rich floors are SVG patterns, which
   a flat rect can't reproduce exactly, so we use the base fill of the most
   common one (rp-wood-warm, #e8d3b3). Only the grain lines differ, which at
   the width of a door opening is imperceptible. */
#canvas.style-rich {
  --ds-door-cut: #e8d3b3;
  /* Slightly lighter than the wood base so labels lift off the grain a
     little without looking like a white sticker. */
  --ds-label-halo: #efe2ca;
}
#canvas.style-rich .window-item {
  filter: none;
}

/* Furniture: soft drop shadow + warmer fills. Individual furniture templates
   use var(--ink)/var(--ink-*) for their strokes, so redefining those inside
   .style-rich is the cheapest way to warm those. For hardcoded `fill="white"`
   inside furniture templates, we use an attribute selector to swap the white
   for a warm off-white — this catches sofa cushions, bed sheets, appliance
   fronts, etc. that were drawn with a literal white fill. */
#canvas.style-rich .furn-item {
  --ink:    #3a2b1c;
  --ink-70: rgba(58, 43, 28, 0.7);
  --ink-50: rgba(58, 43, 28, 0.5);
  --ink-10: rgba(58, 43, 28, 0.1);
  --ink-05: rgba(58, 43, 28, 0.05);
  --warn:   #a06a3a;
  filter: url(#rp-furn-shadow);
  opacity: 0.95;
}
#canvas.style-rich .furn-item.selected { opacity: 1; }
#canvas.style-rich .furn-item [fill="white"] { fill: #f6ede0; }
#canvas.style-rich .furn-item [fill="#fff"]  { fill: #f6ede0; }

/* Doors: rich mode gets a filled panel. The cut-out itself is handled by
   --ds-door-cut above, not by these attribute selectors — it uses a var()
   fill, so [fill="white"] no longer matches it. */
#canvas.style-rich .door-item [fill="white"],
#canvas.style-rich .door-item [fill="#fff"] { fill: #f2e5d0; }

/* Windows: warm accent stroke instead of default blue. */
#canvas.style-rich .window-item [stroke="#2b5cb8"] { stroke: #6b7ea3; }
#canvas.style-rich .window-item [fill="white"],
#canvas.style-rich .window-item [fill="#fff"] { fill: #eae0d1; }

/* Canvas background rect (paper). Cream instead of grey-white. */
#canvas.style-rich #canvas-bg {
  fill: var(--rp-canvas);
  stroke: #b9a480;
}

/* =========================================================
   PRINT
   When the user hits Print (or Ctrl/Cmd+P), strip every UI chrome
   element and resize the canvas to fill the printable page area.
   The SVG renders crisp at any zoom because it's vector. A tiny
   footer (project name + date) appears below the plan.
   ========================================================= */
@media print {
  @page {
    size: landscape;
    margin: 0.5in;
  }

  /* Light background, no fancy header/sidebar — body becomes the page.
     Height is left to the content (.studio is the one thing that sets a
     concrete height) and overflow is hidden so a stray pixel of overshoot
     can't trigger a second sheet. */
  html, body {
    background: white !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden !important;
  }

  /* Hide every piece of UI chrome. */
  header.studio-bar,
  aside.tray,
  aside.inspector,
  .status,
  .ruler,
  .ruler-corner,
  .ruler-top,
  .ruler-left,
  .modal,
  .ctx-menu,
  .hover-chip,
  .toast,
  #handles-layer,
  #preview-layer,
  #guides-layer,
  #overlap-summary,
  #welcome-modal {
    display: none !important;
  }

  /* ---- Single-page layout ----
     Printable area for landscape US Letter with 0.5in margins is 10in x 7.5in.
     We make .studio a flex column of EXACTLY that height, containing the plan
     (flexible) and the footer (fixed). Because the container's height is
     capped at the page height and nothing inside can grow past it, the browser
     never has a reason to paginate.

     The earlier approach (canvas with max-height + a body::after footer) failed
     because break hints on a body pseudo-element are widely ignored — the
     footer kept spilling onto sheet 2. A real element inside a bounded flex
     container avoids that entirely. */
  .studio {
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    justify-content: flex-start !important;
    height: 7.5in !important;
    max-height: 7.5in !important;
    overflow: hidden !important;
    position: static !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .paper {
    position: static !important;
    /* Take all vertical space left over after the footer, but never more. */
    flex: 1 1 auto !important;
    min-height: 0 !important;
    width: 100% !important;
    background: white !important;
    overflow: hidden !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .paper-scroll {
    position: static !important;
    overflow: hidden !important;
    padding: 0 !important;
    width: 100% !important;
    height: 100% !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  /* The SVG scales to fit its flex parent while preserving the viewBox
     aspect ratio. Both max constraints are percentages of the parent, so
     whichever dimension binds first wins and the other scales down with it —
     wide designs are width-bound, tall/square designs are height-bound. */
  #canvas {
    display: block !important;
    width: auto !important;
    height: auto !important;
    max-width: 100% !important;
    max-height: 100% !important;
    margin: 0 !important;
    border: 1px solid #ccc;
    box-shadow: none !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  #canvas:not(.style-rich) {
    background: white !important;
  }
  /* Draft: force flat white on the bg rect. Rich: keep the cream fill. */
  #canvas:not(.style-rich) #canvas-bg {
    filter: none !important;
    fill: white !important;
    stroke: #ccc;
  }
  #canvas.style-rich #canvas-bg {
    filter: none !important; /* still drop the drop-shadow; keep the fill */
  }

  /* Project-name footer — a real element (#print-footer) that is the last
     flex child of .studio, so it's inside the height-bounded page block.
     Fixed height keeps the flex math predictable. */
  .print-footer {
    display: block !important;
    flex: 0 0 auto !important;
    height: 0.32in !important;
    line-height: 0.32in !important;
    width: 100% !important;
    text-align: center;
    font-family: 'JetBrains Mono', monospace;
    font-size: 9.5pt;
    color: #555;
    letter-spacing: 0.05em;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  /* Forced colors: keep accents readable on monochrome printers. */
  * {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
}
