:root {
  color-scheme: dark;
  --bg: #15171b;
  --bg-2: #1d2026;
  --bg-3: #262a32;
  --line: #2f343d;
  --text: #e6e8ec;
  --muted: #9aa1ad;
  --accent: #5eb1ff;
  --accent-2: #7cd992;
  --danger: #ff6b6b;
  --radius: 8px;
  --rail-w: 62px;
  --panel-w: 320px;
  /* The floating chrome is glass over a lit scene rather than panelling beside
     it, so it has its own fill, edge and shadow — one set of values, used by
     the rail, the two bars, the workspace and the dock, which is what makes
     them read as one surface at four sizes. */
  --glass: rgba(24, 27, 33, .82);
  --glass-line: rgba(255, 255, 255, .09);
  --glass-shadow: 0 18px 48px rgba(0, 0, 0, .48);
  --blur: saturate(1.25) blur(18px);
  font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text); overflow: hidden; }
button, select, input { font: inherit; color: inherit; }
button { cursor: pointer; }

/* --- the design is the page -------------------------------------------------
   The viewport fills the window and NOTHING ever takes width off it — not the
   rail, not the bars, not the workspace in either of its shapes, not the dock.
   They all float over it.

   That is a rule rather than a preference. The canvas is the camera: give a
   column its own width and the whole design re-frames itself under the hand
   that was working on it, and for the quarter second the box takes to settle
   every click is aimed through a frustum that no longer matches the element.
   Overlay it and there is nothing to re-aim. */
#app { position: relative; height: 100vh; overflow: hidden; }
#viewport { position: absolute; inset: 0; min-width: 0; min-height: 0; }
#gl { display: block; width: 100%; height: 100%; outline: none; touch-action: none; }
#overlay { position: absolute; inset: 0; pointer-events: none; }

/* --- the rail: six doors, one of them a switch ---------------------------- */
#rail {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%); z-index: 20;
  display: flex; flex-direction: column; gap: 2px; padding: 6px;
  background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--glass-line); border-radius: 18px; box-shadow: var(--glass-shadow);
}

/* --- the two bars: corners, not a wall --------------------------------------
   One bar across the whole width is a wall over the roofline whatever is in it,
   with a stretch of nothing in the middle holding its two halves apart. Two
   bars are each the width of their own contents and leave the top of the design
   showing between them. */
#bar-left, #bar-right {
  position: absolute; z-index: 20; top: 12px; max-width: calc(50% - 40px);
  display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--glass-line); border-radius: 14px; box-shadow: var(--glass-shadow);
}
#bar-left { left: calc(var(--rail-w) + 20px); overflow-x: auto; scrollbar-width: none; }
#bar-left::-webkit-scrollbar { display: none; }
#bar-right { right: 12px; transition: right .22s ease; }
/* The right bar makes room for a pinned column — chrome moving over chrome,
   which costs the design nothing. The left one never has to: the column is on
   the other side. */
body.panel-open.panel-pinned #bar-right { right: calc(var(--panel-w) + 12px); }

/* A bar is one line. A storey called "Ground floor" wrapping to two words
   doubles its height and pushes it down over the design. */
#bar-left .btn, #bar-left summary, #bar-right .btn { white-space: nowrap; }
.brand { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; letter-spacing: .3px; }
.brand svg { width: 17px; height: 17px; stroke: var(--accent); fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.spacer { flex: 1; }
.group { display: flex; align-items: center; gap: 4px; }
.group > label { color: var(--muted); margin-right: 2px; }
/* One control made of three: which camera you are looking through is a single
   choice, and three separate buttons say it is three. */
.seg { gap: 0; padding: 2px; background: rgba(0,0,0,.28); border-radius: 9px; }
.seg .btn { background: transparent; border-color: transparent; border-radius: 7px; }
.seg .btn:hover { background: rgba(255,255,255,.07); border-color: transparent; }
.seg .btn.active { background: var(--accent); border-color: var(--accent); }
/* Whoever is signed in, at the end of the bar, at the width their name needs. */
.btn.who { max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.btn { background: var(--bg-3); border: 1px solid var(--line); border-radius: var(--radius); padding: 5px 10px; line-height: 1.2; }
.btn:hover { border-color: #444a55; background: #2c313a; }
.btn.active { background: var(--accent); border-color: var(--accent); color: #0b1a2a; font-weight: 600; }
.btn.danger { color: var(--danger); }
/* The one action a panel is asking for, when a panel is asking for one. */
.btn.primary { background: var(--accent); border-color: var(--accent); color: #0b1a2a; font-weight: 600; }
.btn.primary:hover { filter: brightness(1.08); }
.btn.small { padding: 3px 7px; font-size: 12px; }
.btn:disabled { opacity: .45; cursor: default; }
/* A button whose whole label is a drawing. The size has to be stated: an inline
   SVG with no width of its own is laid out at whatever the browser decides,
   which is how undo, redo, frame and save came out as four grey dots in a row
   with nothing to say for themselves. Each one carries its name in `title` and
   `aria-label` — see iconButton in ui.js. */
.btn.icon { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 26px; padding: 0; }
.btn.icon svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* A rail button. The name is under the icon and stays there: six icons are
   learnable, but only once you have been told what they are, and a tooltip is
   not telling anybody anything on a touch screen. */
.tool { position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px; width: 50px; padding: 8px 2px 6px; border-radius: 12px; border: 1px solid transparent; background: transparent; color: var(--muted); font-size: 9.5px; letter-spacing: .2px; transition: background .14s ease, color .14s ease; }
.tool svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.tool:hover { background: rgba(255,255,255,.08); color: var(--text); }
.tool.active { background: var(--accent); color: #0b1a2a; font-weight: 600; }
.tool kbd { display: none; }
.tool-sep { height: 1px; background: var(--line); margin: 4px 6px; }
/* Edit mode, armed. A mode with nothing on screen saying it is on is a mode
   people are surprised by, and this one changes what a click means. The dot
   rides the corner of the Design button whether or not its panel is showing —
   the panel is where you are, the dot is what state the app is in. */
.tool.armed { color: var(--accent-2); }
.tool.armed.active { color: #0b1a2a; }
.tool.armed::after {
  content: ''; position: absolute; top: 6px; right: 6px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent-2); box-shadow: 0 0 0 2px rgba(24,27,33,.75);
}

/* --- the menus on the bar -----------------------------------------------------
   Two of them, and they are <details>: Escape closes them and the summary
   toggles them without a line of script. */
details.menu { position: relative; }
details.menu > summary { list-style: none; display: inline-flex; align-items: center; gap: 6px; }
details.menu > summary::-webkit-details-marker { display: none; }
details.menu > summary svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.menu-body {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 40; min-width: 210px; padding: 6px;
  background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--glass-line); border-radius: 12px; box-shadow: var(--glass-shadow);
}
.menu-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 6px 9px; border: 0; border-radius: 8px; background: transparent; text-align: left; }
.menu-item:hover { background: rgba(255,255,255,.09); }
.menu-item kbd { color: var(--muted); font: inherit; font-size: 11px; }
.menu-head { padding: 6px 9px 2px; font-size: 10px; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); }
.menu-sep { height: 1px; margin: 5px 4px; background: var(--glass-line); }
.menu-check { display: flex; align-items: center; gap: 8px; padding: 5px 9px; border-radius: 8px; color: var(--muted); cursor: pointer; white-space: nowrap; }
.menu-check:hover { background: rgba(255,255,255,.07); }
.menu-check.on { color: var(--text); }
.menu-check input { accent-color: var(--accent); }
.menu-row { display: flex; align-items: center; gap: 8px; padding: 5px 9px; }
.menu-row label { flex: 0 0 auto; color: var(--muted); }
.menu-row input[type=range] { flex: 1 1 auto; }

select.btn, input.btn { padding: 4px 6px; }
input[type=range] { width: 100%; accent-color: var(--accent); }
input[type=number], input[type=text] { width: 100%; background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 4px 6px; }
input[type=color] { width: 100%; height: 28px; border: 1px solid var(--line); border-radius: 6px; background: var(--bg); padding: 2px; }

.panel-title { font-weight: 600; font-size: 14px; margin: 4px 0 10px; }
.panel-section { margin-bottom: 14px; }
.panel-section h4 { margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); }
.field { display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: 6px; margin-bottom: 6px; }
.field label { color: var(--muted); }
.field .value { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
.field.wide { grid-template-columns: 1fr; }
.row { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; }
.row .btn { flex: 1 1 auto; }
.hint { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
/* The bottom of a swatch carries its name inside the tile, so the next control
   needs a gap or it reads as part of the grid. */
.swatches { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-bottom: 10px; }
.swatch { aspect-ratio: 1; border-radius: 6px; border: 2px solid transparent; background-size: cover; cursor: pointer; position: relative; }
.swatch.active { border-color: var(--accent); }
.swatch span { position: absolute; left: 0; right: 0; bottom: 0; font-size: 9px; text-align: center; background: rgba(0,0,0,.55); border-radius: 0 0 4px 4px; padding: 1px 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Two columns, not three. At the panel's width three cells overflow it and the
   third is cut off — with no visible scrollbar to say so, which reads as a
   missing column rather than a hidden one. Two fit, and the thumbnails get
   bigger, which is what a picker is for. */
.library { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
.asset { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 4px; border-radius: 6px; border: 1px solid var(--line); background: var(--bg); cursor: pointer; font-size: 10px; text-align: center; }
.asset:hover { border-color: var(--accent); }
.asset.active { border-color: var(--accent); background: #1b2733; }
.asset canvas, .asset .thumb { width: 72px; height: 56px; border-radius: 4px; background: #0f1114; display: block; }
.asset .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100%; }
.tags { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; }
.houses { display: flex; flex-direction: column; gap: 6px; }
.house { display: flex; flex-direction: column; border-radius: 6px; border: 1px solid var(--line); background: var(--bg); overflow: hidden; }
.house-body { display: flex; flex-direction: column; gap: 2px; padding: 7px 8px; }
/* The picture keeps its box before it loads, so a scroll does not jump under
   the pointer as each lazy image arrives. */
.house-shot { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; background: var(--panel); }
.house:hover { border-color: var(--accent); }
.house.busy { opacity: 0.6; }
.house-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.house .facts { color: var(--muted); font-size: 11px; }
.house .blurb { font-size: 11px; line-height: 1.35; }
.tag { background: var(--bg-3); border-radius: 999px; padding: 2px 8px; font-size: 11px; cursor: pointer; border: 1px solid transparent; }
.tag.active { border-color: var(--accent); color: var(--accent); }

/* AI Studio. The notice sits above the box and is deliberately not `.hint`
   grey: what leaves the browser is the one line in this panel a reader must not
   skim past. */
.ai-notice { color: var(--text); font-size: 11px; line-height: 1.4; margin: 0 0 8px; padding: 5px 8px; border-radius: 6px; border: 1px solid var(--line); border-left: 2px solid var(--accent); background: var(--bg); }
textarea.ai-prompt { width: 100%; background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 6px; resize: vertical; min-height: 54px; font: inherit; color: inherit; }
textarea.ai-prompt:focus { outline: none; border-color: var(--accent); }
textarea.ai-prompt:disabled { opacity: .5; }
.ai-result { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
.ai-said { margin: 0 0 4px; font-size: 12px; line-height: 1.45; }
.ai-bad { margin: 6px 0 0; font-size: 12px; line-height: 1.45; color: var(--danger); }
.ai-dropped { margin-top: 6px; font-size: 11px; color: var(--muted); }
.ai-dropped summary { cursor: pointer; }
.ai-dropped ul { margin: 4px 0 0; padding-left: 16px; }
.ai-dropped li { margin-bottom: 2px; line-height: 1.35; }

.level-tabs { display: flex; gap: 2px; }
.level-tabs .btn { padding: 4px 9px; }

.stat { font-variant-numeric: tabular-nums; }
.stat b { color: var(--text); font-weight: 600; }
/* The walking hint bar lives at the FOOT of the design, not its top left corner:
   the rail and the bar are up there now, and a line of keys behind a rail is a
   line of keys nobody can read. At the bottom it is also where the eye already
   is — a walker is looking at the floor in front of them, not at the sky. */
.hud { position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); max-width: min(92vw, 900px); text-align: center;
  background: rgba(20,23,28,.86); border: 1px solid var(--glass-line); border-radius: 999px; padding: 6px 14px; font-size: 12px; color: var(--muted); pointer-events: none; }
.hud b { color: var(--text); }
/* The one pressable thing in the hint bar. The bar itself stays click-through so
   it can never swallow a look-around, so the switch has to ask for events back —
   and it is only pressable at all with the cursor free, which is why it names
   its key too. */
.hud .walk-edit { pointer-events: auto; margin-left: 10px; padding: 1px 8px; font: inherit; font-size: 11px;
  color: var(--muted); background: rgba(255,255,255,.06); border: 1px solid var(--line); border-radius: 999px; cursor: pointer; }
.hud .walk-edit:hover { color: var(--text); background: rgba(255,255,255,.12); }
.hud .walk-edit.on { color: #cfe9ff; border-color: rgba(143,208,255,.55); background: rgba(143,208,255,.14); }
/* Clear of the hint bar and of the walking keys, both of which are standing at
   the bottom of the screen and neither of which a toast may cover. */
.toast { position: absolute; left: 50%; bottom: 58px; transform: translateX(-50%); background: rgba(20,23,28,.92); border: 1px solid var(--line); border-radius: var(--radius); padding: 6px 12px; font-size: 12px; pointer-events: none; }

/* The pointer, and what it is on.

   While walking, the browser takes the cursor away so the mouse can aim the
   head — so the ray goes through the middle of the screen and this dot is the
   only thing that says so. It is drawn ONLY then (`body.aiming`): everywhere
   else there is a real arrow, and a second cursor painted in the middle of the
   screen would be pointing at the wrong thing all day.

   The crosshair is a dot and a ring rather than a cross: a cross of hairlines
   disappears against a white wall and against a window frame, which is most of
   what an interior is. */
.reticle { position: absolute; left: 50%; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 -11px; display: none; pointer-events: none; }
body.aiming .reticle { display: block; }
.reticle i { position: absolute; left: 50%; top: 50%; width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 50%;
  background: #fff; box-shadow: 0 0 0 1.5px rgba(10,12,16,.65); transition: width .12s ease, height .12s ease, margin .12s ease; }
.reticle u { position: absolute; inset: 0; border-radius: 50%; border: 1.5px solid rgba(255,255,255,0);
  transition: border-color .14s ease, transform .14s ease; transform: scale(.6); }
/* On something selectable: the ring closes in on it. Motion is what the eye
   catches at the edge of attention, which is where a reticle lives. */
.reticle.on-target i { width: 3px; height: 3px; margin: -1.5px 0 0 -1.5px; }
.reticle.on-target u { border-color: rgba(143,208,255,.95); transform: scale(1); }

/* The label for the thing under the pointer. Follows the arrow when there is
   one and sits under the reticle when there is not — see the note in ui.js. */
/* No max-width: the row is `nowrap`, so a cap would leave the text running out
   past its own background rather than wrapping. Overrunning the viewport is
   what `placeChip` clamps against instead. */
.hover-chip { position: absolute; left: 0; top: 0; display: flex; align-items: baseline; gap: 7px; white-space: nowrap;
  background: rgba(20,23,28,.92); border: 1px solid var(--line); border-radius: var(--radius); padding: 4px 9px; font-size: 12px;
  pointer-events: none; opacity: 0; transition: opacity .12s ease; }
.hover-chip.shown { opacity: 1; }
.hover-chip b { color: var(--text); font-weight: 600; }
.hover-chip .facts { color: var(--muted); font-variant-numeric: tabular-nums; }
.hover-chip .act { color: #8fd0ff; font-size: 11px; }

/* The selection's own controls while walking. Above the toast line, and the one
   thing in the overlay that takes the pointer back. */
.walk-actions { position: absolute; left: 50%; bottom: 58px; transform: translateX(-50%); display: flex; align-items: center; gap: 8px;
  background: rgba(20,23,28,.92); border: 1px solid var(--line); border-radius: var(--radius); padding: 6px 10px; font-size: 12px; pointer-events: auto; }
.walk-actions b { color: var(--text); font-weight: 600; margin-right: 2px; }

/* With the cursor handed back mid-walk, it is still aiming a ray at the house
   rather than pointing at a document. */
body.walking:not(.aiming) #gl { cursor: crosshair; }

/* The compass. The overlay ignores the pointer, so this asks for it back — it
   is a control (click it to face north), not a readout. */
.compass { position: absolute; left: 12px; bottom: 14px; width: 96px; pointer-events: auto; cursor: pointer; user-select: none; opacity: .82; transition: opacity .18s ease; }
.compass:hover { opacity: 1; }
.compass-dial { display: block; width: 96px; height: 96px; }
.compass-face { fill: rgba(20,23,28,.72); stroke: var(--line); stroke-width: 1; }
.compass-tick { stroke: #5a626f; stroke-width: 1; }
.compass-tick.major { stroke: #8a93a3; stroke-width: 1.4; }
.compass-letter { fill: var(--muted); font: 600 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; text-anchor: middle; dominant-baseline: central; }
.compass-letter.north { fill: #ff8080; }
.compass-needle { fill: #8a93a3; opacity: .5; }
.compass-needle.north { fill: #ff6b6b; opacity: .9; }
.compass-sun { fill: #ffd166; stroke: rgba(20,23,28,.9); stroke-width: 1; }
.compass-moon { fill: #cfd8ea; stroke: rgba(20,23,28,.9); stroke-width: 1; }
/* Below the horizon it is still worth showing — that is how you watch the sun
   come round to a facade — but as an outline, so it cannot be mistaken for one
   that is up. */
.compass-sun.below, .compass-moon.below { fill: none; stroke: #ffd166; stroke-width: 1.4; }
.compass-moon.below { stroke: #8f9bb3; }
.compass-readout { margin-top: 2px; text-align: center; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* A phone has no room for it beside the tool strip. */
@media (max-width: 820px) { .compass { display: none; } }
/* The floating workspace reaches down past the compass's corner, and a dial
   half behind a card reads as a rendering fault rather than as an instrument.
   It comes back the moment the workspace is put away — which is the moment
   anybody is looking at the design and would want it. */
body.panel-float.panel-open .compass { display: none; }

.grades { display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px; margin-bottom: 8px; }
.grade { display: flex; flex-direction: column; gap: 2px; padding: 3px; border-radius: 6px; border: 1px solid var(--line); background: var(--bg); font-size: 10px; color: var(--muted); }
.grade img { width: 100%; height: 16px; border-radius: 3px; display: block; object-fit: cover; }
.grade.active { border-color: var(--accent); color: var(--text); }
.grade:hover { border-color: #444a55; }

/* --- the workspace ----------------------------------------------------------
   ONE surface, every section, in the same place, AT THE SAME SIZE.

   Floating (the default) it is a fixed rectangle over the design: from the rail
   to the right edge, from under the two bars to above the dock. It does not
   grow with a wall of house tiles and it does not shrink to four fields — a
   card that resizes itself per tab makes pressing two rail buttons in a row
   look like two different applications, and it is the reason this pass exists.
   Pinned it is a column glued to the right edge.

   Both are the same DOM and neither costs the design a pixel of resolution:
   nothing here ever takes width off #viewport. */
#panel {
  position: absolute; z-index: 25; display: flex; flex-direction: column; overflow: hidden;
  background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--glass-line);
  right: 0; top: 0; bottom: 0; width: var(--panel-w);
  border-width: 0 0 0 1px; box-shadow: -14px 0 34px rgba(0,0,0,.4);
}
body:not(.panel-open) #panel { display: none; }
/* Out of the way while a gesture is live — see applyLayout in ui.js. */
body.panel-float.panel-busy #panel { display: none; }
body.panel-float #panel {
  /* All four edges pinned, so the box is a fact about the window rather than
     about the tab. `bottom` clears the dock and the hint pill. */
  left: calc(var(--rail-w) + 20px); right: 16px; top: 70px; bottom: 70px;
  width: auto; height: auto; max-width: none; max-height: none;
  border-radius: 18px; border-width: 1px; box-shadow: var(--glass-shadow);
}
.panel-body { min-height: 0; }

/* The workspace's own bar: what this is, what else is in it, where it goes. */
.panel-head { display: flex; align-items: center; gap: 12px; padding: 10px 12px 9px; border-bottom: 1px solid var(--glass-line); }
.panel-name { font-weight: 700; font-size: 14px; letter-spacing: .2px; flex: 0 0 auto; }
.panel-acts { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.icon-btn { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; padding: 0; border: 1px solid transparent; border-radius: 8px; background: transparent; color: var(--muted); }
.icon-btn:hover { background: rgba(255,255,255,.09); color: var(--text); }
.icon-btn.on { color: var(--accent); border-color: rgba(94,177,255,.4); background: rgba(94,177,255,.12); }
.icon-btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* The tabs. They scroll rather than wrap: the head is one line high in both
   sizes of workspace, and a head that grows a second row moves everything
   under it every time you change section. */
.tabs { display: flex; align-items: center; gap: 2px; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tab { flex: 0 0 auto; padding: 5px 11px; border: 0; border-radius: 999px; background: transparent; color: var(--muted); white-space: nowrap; }
.tab:hover { background: rgba(255,255,255,.08); color: var(--text); }
.tab.active { background: rgba(94,177,255,.16); color: #cfe9ff; font-weight: 600; }

/* Pinned, the head is 320 px wide and the name, six tabs and two buttons do not
   share it. The tabs drop to a line of their own — the only thing in the head
   that is a list, and the only one that can be scrolled sideways. */
body:not(.panel-float) .panel-head { flex-wrap: wrap; gap: 8px; }
body:not(.panel-float) .tabs { order: 3; flex: 1 0 100%; }

.panel-body { flex: 1 1 auto; overflow-y: auto; padding: 12px 14px 18px; }

/* Floating, the body is a wall of cards rather than one long column: a form of
   fields in a 1100 px box would be a metre of white space and a column of
   inputs down the left of it. Sections that BROWSE — houses, furniture, the
   people in the room — take the full width, because their tiles are the point
   of the extra room. */
body.panel-float .panel-body {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: 14px; align-content: start;
}
body.panel-float .panel-body > .panel-section {
  margin: 0; padding: 12px 14px; border: 1px solid var(--glass-line); border-radius: 12px;
  background: rgba(255,255,255,.028);
}
/* A card drawn round the only thing in the card is a frame round a frame — and
   it takes ONE track of the grid, which in a fixed-size workspace leaves four
   fifths of the card empty beside a column of swatches. It is the whole width,
   and what it holds spreads across that width. */
body.panel-float .panel-body > .panel-section:only-child {
  border: 0; background: none; padding: 0; grid-column: 1 / -1;
}
/* The browsing tabs are ONE grid of tiles rather than a card in a column of
   cards: the tiles are what wants the room, and a 290 px track round them would
   be a picture frame inside a picture frame. `[data-section]` catches all nine
   Furnish tabs at once. */
body.panel-float #panel[data-tab="houses"] .panel-body > .panel-section,
body.panel-float #panel[data-section="furnish"] .panel-body > .panel-section,
body.panel-float #panel[data-tab="share"] .panel-body > .panel-section:first-child {
  grid-column: 1 / -1;
}
/* The AI Studio is a conversation, and a conversation is read at a paragraph's
   width however much room there is around it. */
body.panel-float #panel[data-tab="ai"] .panel-body { grid-template-columns: minmax(0, 720px); justify-content: center; }

/* Tiles, at whatever size the workspace turned out to be. The column had room
   for two; the card has room for as many as fit, and they get bigger with it. */
body.panel-float .library { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 10px; }
body.panel-float .asset { padding: 8px; gap: 6px; }
body.panel-float .asset canvas, body.panel-float .asset .thumb { width: 100%; height: auto; aspect-ratio: 4 / 3; }
body.panel-float .swatches { grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); }
/* Grids of tiles want every pixel; a labelled field, a row of buttons and a
   paragraph do not. A number input stretched across 1200 px is a worse control
   than the same input at 200 px, and a line of prose that wide cannot be read
   at all — the eye loses the start of the next line. So the tiles spread and
   the reading matter stops. */
body.panel-float .panel-body .field,
body.panel-float .panel-body .row,
body.panel-float .panel-body > .panel-section > input,
body.panel-float .panel-body > .panel-section > textarea { max-width: 520px; }
body.panel-float .panel-body .hint,
body.panel-float .panel-body .facts { max-width: 68ch; }
body.panel-float .figures { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); }
body.panel-float .houses { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
body.panel-float .grades { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }

/* The live tool's own instruction, over the design where the tool is used.
   Not while walking: the walking keys take this spot, and they are the more
   urgent of the two — nothing else on screen says how to get back out. */
body.walking .hint-bar { display: none; }
.hint-bar {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 16px; max-width: min(620px, 76vw);
  padding: 6px 14px; border-radius: 999px; font-size: 12px; color: var(--muted);
  background: rgba(20,23,28,.86); border: 1px solid var(--glass-line);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; pointer-events: none;
}

.geek .field { grid-template-columns: 1fr auto; }
.geek .field .value { font-weight: 600; color: var(--text); }

/* --- the dock: share, and the two people you can talk to ---------------------
   Bottom right, opposite the compass. A design is something you show somebody,
   and the two chats — the room's thread and the studio — are the same gesture
   answered by a person and by a machine. */
#dock {
  position: absolute; z-index: 22; right: 12px; bottom: 14px;
  display: flex; align-items: center; gap: 4px; padding: 5px 6px;
  background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--glass-line); border-radius: 14px; box-shadow: var(--glass-shadow);
  transition: right .22s ease;
}
body.panel-open.panel-pinned #dock { right: calc(var(--panel-w) + 12px); }
/* Walking, the whole bottom of the screen is the walking keys and the reticle
   is where the attention is; the dock would be a row of buttons no cursor can
   reach anyway, because the pointer is locked. */
body.walking #dock { display: none; }
.dock-btn {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px;
  border: 1px solid transparent; border-radius: 10px; background: transparent; color: var(--muted);
  white-space: nowrap; transition: background .14s ease, color .14s ease;
}
.dock-btn:hover { background: rgba(255,255,255,.09); color: var(--text); }
.dock-btn.active { background: rgba(94,177,255,.16); color: #cfe9ff; border-color: rgba(94,177,255,.34); }
.dock-btn svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
/* Somebody else is here. Green rather than the accent, because the accent means
   "this is the thing you are looking at" everywhere else in the app. */
.dock-btn.live { color: var(--accent-2); }
.dock-btn.studio svg { fill: currentColor; stroke: none; }

/* The mode switch on the workspace's own bar. A lamp and a word: the lamp is
   what you see from across the room, the word is what you read when you have
   walked over to it. */
.edit-switch {
  display: inline-flex; align-items: center; gap: 6px; margin-right: 4px; padding: 4px 9px;
  border: 1px solid var(--glass-line); border-radius: 999px; background: transparent;
  color: var(--muted); font-size: 11px; white-space: nowrap;
}
.edit-switch i { width: 7px; height: 7px; border-radius: 50%; background: #5c636f; }
.edit-switch:hover { color: var(--text); background: rgba(255,255,255,.07); }
.edit-switch.on { color: var(--accent-2); border-color: rgba(124,217,146,.42); background: rgba(124,217,146,.12); }
.edit-switch.on i { background: var(--accent-2); box-shadow: 0 0 6px rgba(124,217,146,.7); }

/* What the Share page looks like before you have an account: everything that is
   on offer, switched off. `inert` in the markup takes it off the pointer and
   out of the tab order; this is only what that looks like. */
.panel-section.locked { opacity: .45; }
.panel-section.locked .panel-title::after {
  content: 'sign in'; margin-left: 8px; padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--glass-line); font-size: 10px; font-weight: 400; color: var(--muted);
  text-transform: uppercase; letter-spacing: .5px; vertical-align: middle;
}

@media (max-width: 1100px) {
  :root { --panel-w: 290px; }
}

/* --- phones and narrow tablets ---------------------------------------------
   The rail stays where it is — seven buttons down the left edge is a shape a
   thumb reaches on a phone too, and it costs 62 px rather than the horizontal
   strip's whole bottom row. What changes is the workspace: floating, it is
   very nearly the screen, because on 390 px there is no useful middle ground
   between "a card" and "everything". */
@media (max-width: 820px) {
  :root { --rail-w: 54px; }
  #rail { padding: 4px; border-radius: 14px; gap: 0; }
  .tool { width: 44px; padding: 7px 2px 5px; font-size: 9px; }
  .tool svg { width: 19px; height: 19px; }
  /* The left bar carries a dozen controls; let them scroll rather than wrap it
     into three rows over the design it is there to serve. The right one holds
     six and keeps its corner. */
  /* THE TWO BARS STACK rather than share a line. Side by side they need about
     420 px between them; on 375 they simply overlapped, and the account button
     ran off the right edge. One row each, both able to scroll their own
     contents, and the workspace starts below the pair. */
  #bar-left, #bar-right { max-width: calc(100% - var(--rail-w) - 22px); overflow-x: auto; scrollbar-width: none; }
  #bar-left::-webkit-scrollbar, #bar-right::-webkit-scrollbar { display: none; }
  #bar-left { left: calc(var(--rail-w) + 12px); right: 8px; top: 8px; }
  #bar-right { right: 8px; top: 52px; }
  #bar-left .brand span { display: none; }
  body.panel-open.panel-pinned #bar-right { right: 8px; }
  body.panel-float #panel { left: calc(var(--rail-w) + 12px); right: 8px; top: 96px; bottom: 62px; border-radius: 14px; }
  /* A column on a phone is a drawer over the design rather than a strip beside
     it: 320 px taken out of 390 is not a design any more. */
  body:not(.panel-float) #panel { width: min(330px, 88vw); }
  body.panel-open.panel-pinned #dock { right: 8px; }
  .dock-btn span { display: none; }
  .dock-btn { padding: 8px; }
  /* Touch targets: 28 px is a comfortable mouse button and a poor thumb. */
  .icon-btn { width: 34px; height: 34px; }
  .btn { padding: 7px 11px; }
  .btn.small { padding: 5px 9px; }
}

/* --- the Share panel: people, areas, history and the room's own thread ---- */

/* One row per person, wherever people are listed — in the room now, on the
   grant list, or in the history. The same shape in three places on purpose:
   they are the same people. */
.person { display: flex; align-items: baseline; gap: 7px; padding: 3px 0; }
.person-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The colour is derived from the account id, so it is the same on every screen
   and matches the shirt on that person's body in the scene. The ring round it
   is how loudly they are talking. */
.person-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; transition: box-shadow .12s linear; }

/* A door's finish, beside its row in the doors list: bigger than a presence dot
   and square, because it is showing a material rather than identifying a person,
   and a 9 px circle of oak is not a thing anyone can recognise. */
.finish-chip { flex: 0 0 auto; width: 16px; height: 16px; border-radius: 3px; background-size: cover; border: 1px solid rgba(0, 0, 0, .25); }
.person.active { background: var(--sel, rgba(94, 177, 255, .14)); border-radius: 4px; padding-left: 4px; padding-right: 4px; }

/* The figure picker. Four to a row on the panel's width, each one a silhouette
   over its name — the silhouette is what carries the difference between two
   entries whose names are one word apart. */
.figures { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; margin: 4px 0 8px; }
.figure { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 5px 2px 4px; cursor: pointer;
  background: rgba(255, 255, 255, .04); border: 1px solid transparent; border-radius: 5px; color: inherit; font: inherit; }
.figure:hover { background: rgba(255, 255, 255, .08); }
.figure.active { border-color: var(--accent, #5eb1ff); background: rgba(94, 177, 255, .14); }
.figure svg { width: 34px; height: 48px; fill: currentColor; stroke: none; opacity: .8; }
.figure.active svg { opacity: 1; }
/* A modelled person's portrait sits in the same box as a built figure's
   silhouette, so the two kinds line up in one grid rather than reading as two
   different pickers. */
.figure img { width: 34px; height: 48px; object-fit: cover; object-position: top center; border-radius: 3px; display: block; }
.figure span { font-size: 10px; line-height: 1.15; text-align: center; }
.figure small { font-size: 9px; color: var(--muted); }

.areas { display: flex; flex-direction: column; gap: 5px; margin-top: 6px; }
.area { display: flex; align-items: flex-start; gap: 7px; cursor: pointer; }
.area input { margin-top: 3px; }
.area b { display: block; font-weight: 600; }
.area .facts { display: block; color: var(--muted); font-size: 11px; line-height: 1.3; }

/* The thread. Bounded and scrolled, because a panel that grows with the
   conversation pushes everything else off the screen. */
.chat-log { max-height: 190px; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; margin-bottom: 6px; }
.chat-line { font-size: 12px; line-height: 1.35; }
.chat-line b { color: var(--muted); margin-right: 5px; font-weight: 600; }
