/* A dark, touch-first shell around a bright 3D view.
 *
 * Sizes are set for a finger before a cursor: 44px is the smallest thing worth
 * tapping, and the controls sit along the edges where a hand holding an iPad
 * can reach them without covering the middle of the world. */

:root {
  --bg: #14171d;
  --panel: #1c2028;
  --panel-2: #242935;
  --line: #333a48;
  --text: #eef1f6;
  --dim: #98a2b5;
  --accent: #67b6ff;
  --accent-ink: #06121e;
  --warn: #ff8a5c;
  --good: #6cd08a;
  --radius: 14px;
  --shadow: 0 10px 30px rgba( 0, 0, 0, .45 );
  --safe-t: env( safe-area-inset-top, 0px );
  --safe-b: env( safe-area-inset-bottom, 0px );
  --safe-l: env( safe-area-inset-left, 0px );
  --safe-r: env( safe-area-inset-right, 0px );
  color-scheme: dark;
}

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

html, body {
  margin: 0; height: 100%; overflow: hidden;
  background: var( --bg ); color: var( --text );
  font: 15px/1.45 ui-rounded, -apple-system, "SF Pro Rounded", "Segoe UI", Roboto, system-ui, sans-serif;
  overscroll-behavior: none;
  touch-action: none;
  -webkit-user-select: none; user-select: none;
}

#view { position: fixed; inset: 0; width: 100%; height: 100%; display: block; }

#ui { position: fixed; inset: 0; pointer-events: none; }
#ui > * { pointer-events: auto; }

button, input, select { font: inherit; color: inherit; }

button {
  background: var( --panel-2 ); border: 1px solid var( --line );
  border-radius: 11px; padding: 9px 13px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: background .12s, border-color .12s, transform .06s;
}
button:hover { background: #2c3341; }
button:active { transform: scale( .97 ); }
button[disabled] { opacity: .38; pointer-events: none; }
button.primary { background: var( --accent ); border-color: var( --accent ); color: var( --accent-ink ); font-weight: 600; }
button.primary:hover { background: #83c4ff; }
button.ghost { background: transparent; border-color: transparent; }
button.ghost:hover { background: #ffffff14; }
button.danger { color: var( --warn ); border-color: #4a3128; }
button.on { background: var( --accent ); border-color: var( --accent ); color: var( --accent-ink ); }

.icon { width: 22px; height: 22px; flex: none; display: block; }

/* --- top bar --------------------------------------------------------------- */

#topbar {
  position: absolute; top: 0; left: 0; right: 0;
  padding: calc( var( --safe-t ) + 8px ) calc( var( --safe-r ) + 10px ) 8px calc( var( --safe-l ) + 10px );
  display: flex; align-items: center; gap: 8px;
  background: linear-gradient( var( --bg ) 30%, transparent );
}
#topbar .spacer { flex: 1; }
#worldName {
  background: transparent; border: 1px solid transparent; border-radius: 10px;
  padding: 7px 10px; width: min( 34vw, 250px ); font-weight: 600;
  text-overflow: ellipsis;
}
#worldName::placeholder { color: var( --dim ); opacity: .75; }
#worldName:hover { border-color: var( --line ); }
#worldName:focus { outline: none; border-color: var( --accent ); background: var( --panel ); }

.badge {
  font-size: 12px; color: var( --dim ); background: var( --panel );
  border: 1px solid var( --line ); border-radius: 999px; padding: 4px 10px; white-space: nowrap;
}
.badge.warn { color: var( --warn ); border-color: #4a3128; }
.badge.live { color: var( --good ); }

#peers { display: flex; gap: -6px; align-items: center; }
.peer {
  width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
  font-size: 11px; font-weight: 700; color: #10141a; margin-left: -6px;
  border: 2px solid var( --bg ); position: relative;
}

/* --- tool rail ------------------------------------------------------------- */

#tools {
  position: absolute; z-index: 3;
  left: calc( var( --safe-l ) + 10px ); top: 50%; transform: translateY( -50% );
  display: flex; flex-direction: column; gap: 6px;
  background: var( --panel ); border: 1px solid var( --line );
  border-radius: 16px; padding: 6px; box-shadow: var( --shadow );
}
#tools button {
  width: 52px; height: 52px; padding: 0; flex-direction: column; gap: 2px;
  border-color: transparent; background: transparent; border-radius: 12px;
}
#tools button span { font-size: 10px; letter-spacing: .01em; }
#tools button.on { background: var( --accent ); color: var( --accent-ink ); }

/* --- bottom drawer --------------------------------------------------------- */

/* The drawer starts to the right of the tool rail rather than sliding under it.
   Overlapping them would put the bottom two tools behind the asset grid, which
   is exactly where the erase tool lives. */
#drawer {
  --mode: bottom;
  position: absolute; left: 76px; right: 0; bottom: 0;
  background: var( --panel ); border-top: 1px solid var( --line );
  padding: 10px calc( var( --safe-r ) + 12px ) calc( var( --safe-b ) + 10px ) calc( var( --safe-l ) + 12px );
  display: flex; flex-direction: column; gap: 10px;
  max-height: 46vh; box-shadow: 0 -8px 26px rgba( 0, 0, 0, .35 );
  transition: transform .18s ease;
}
/* The asset grid is `flex: 1 1 0`, so it claims whatever is left rather than
   asking for room. That only works if the drawer has a height to divide up —
   left to size itself around its content the grid would get nothing. The tool
   option panels are short and keep sizing themselves. */
#drawer.tall { height: min( 40vh, 336px ); }
#drawer.hidden { transform: translateY( 105% ); pointer-events: none; }
#drawer { z-index: 2; }
#drawerHandle {
  position: absolute; top: -32px; left: 50%; transform: translateX( -50% );
  background: var( --panel ); border: 1px solid var( --line ); border-bottom: none;
  border-radius: 12px 12px 0 0; padding: 5px 28px 7px; font-size: 14px; color: var( --text );
  line-height: 1;
}

.row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.row.tight { gap: 6px; }
.grow { flex: 1; min-width: 0; }
.label { font-size: 12px; color: var( --dim ); white-space: nowrap; }

.chips { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px; scrollbar-width: thin; }
.chips::-webkit-scrollbar { height: 4px; }
.chips::-webkit-scrollbar-thumb { background: var( --line ); border-radius: 4px; }
.chip {
  border-radius: 999px; padding: 7px 13px; white-space: nowrap; font-size: 13px;
  background: var( --panel-2 ); border: 1px solid var( --line );
}
.chip.on { background: var( --accent ); border-color: var( --accent ); color: var( --accent-ink ); font-weight: 600; }

input[type=search], input[type=text] {
  background: var( --panel-2 ); border: 1px solid var( --line ); border-radius: 11px;
  padding: 9px 12px; width: 100%; -webkit-user-select: text; user-select: text;
}
input:focus { outline: none; border-color: var( --accent ); }

input[type=range] {
  -webkit-appearance: none; appearance: none; background: transparent;
  height: 30px; flex: 1; min-width: 90px;
}
input[type=range]::-webkit-slider-runnable-track { height: 5px; background: var( --line ); border-radius: 3px; }
input[type=range]::-moz-range-track { height: 5px; background: var( --line ); border-radius: 3px; }
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 24px; height: 24px; border-radius: 50%;
  background: var( --accent ); margin-top: -10px; border: 2px solid var( --bg );
}
input[type=range]::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%; background: var( --accent ); border: 2px solid var( --bg );
}

/* --- asset grid ------------------------------------------------------------ */

#assetGrid {
  display: grid; grid-template-columns: repeat( auto-fill, minmax( 84px, 1fr ) );
  gap: 8px; overflow-y: auto; overscroll-behavior: contain;
  padding: 2px 2px 4px; touch-action: pan-y;
  /* An explicit row height, not `aspect-ratio` on the tile. A grid row is sized
     from its items' content, and these items have none — the thumbnail is a
     background and the caption is absolutely positioned — so the row collapsed
     to the height of the favourite star and every tile overflowed onto the one
     below it. */
  grid-auto-rows: 92px;
  /* A flex item will not shrink below its content unless told to, and a grid
     of two hundred tiles is a lot of content: without this the grid grows past
     the drawer and spills its tiles over the world instead of scrolling. */
  flex: 1 1 0; min-height: 0;
}
.asset {
  position: relative; height: 100%; border-radius: 12px; padding: 0;
  background: var( --panel-2 ); border: 1px solid var( --line ); overflow: hidden;
}
.asset .thumb {
  width: 100%; height: 100%; background-repeat: no-repeat;
  background-color: #2a3140; image-rendering: auto;
}
.asset .caption {
  position: absolute; left: 0; right: 0; bottom: 0; font-size: 9.5px; line-height: 1.25;
  padding: 8px 4px 3px; color: #dfe6f2; text-align: center;
  background: linear-gradient( transparent, rgba( 12, 15, 20, .88 ) );
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.asset.on { border-color: var( --accent ); box-shadow: 0 0 0 2px var( --accent ) inset; }
.asset .star {
  position: absolute; top: 3px; right: 3px; width: 22px; height: 22px; border-radius: 50%;
  background: rgba( 10, 13, 18, .6 ); border: none; padding: 0; font-size: 12px; opacity: .55;
}
.asset .star.on { opacity: 1; color: #ffcf5c; }
.empty { color: var( --dim ); font-size: 13px; padding: 22px 6px; text-align: center; grid-column: 1 / -1; }

/* --- floating panels ------------------------------------------------------- */

.card {
  background: var( --panel ); border: 1px solid var( --line ); border-radius: var( --radius );
  box-shadow: var( --shadow ); padding: 12px;
}

/* Two columns of icons, not a panel. It shows up the moment anything is placed,
   so it has to leave the thing you just placed visible. */
/* Top right, under the bar — not vertically centred. Centred put it exactly
   where the camera controls rise to when the asset drawer is open, and a delete
   button underneath a camera puck is a bug you only find by tapping. */
#selection {
  position: absolute; right: calc( var( --safe-r ) + 10px );
  top: calc( var( --safe-t ) + 56px );
  display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
  z-index: 3;
}
.selGrid {
  display: grid; grid-template-columns: repeat( 2, 42px ); gap: 5px;
  background: var( --panel ); border: 1px solid var( --line );
  border-radius: 14px; padding: 6px; box-shadow: var( --shadow );
}
.selBtn {
  width: 42px; height: 42px; padding: 0; border-radius: 10px;
  border-color: transparent; background: var( --panel-2 ); font-size: 17px; line-height: 1;
}
.selBtn.on { background: var( --accent ); color: var( --accent-ink ); }
.selCount {
  font-size: 11px; font-weight: 700; color: var( --accent-ink ); background: var( --accent );
  border-radius: 999px; padding: 2px 9px;
}
.selMore {
  display: flex; flex-direction: column; gap: 6px; width: 168px; padding: 8px;
}
.selMore button { justify-content: flex-start; }

/* `--drawer-h` is written by the UI whenever the drawer opens or closes. The
   camera controls ride above it instead of being buried under the asset grid,
   which on a tablet in portrait is most of the lower half of the screen. */
#camPuck {
  position: absolute; z-index: 3;
  right: calc( var( --safe-r ) + 14px );
  bottom: calc( var( --safe-b ) + var( --drawer-h, 0px ) + 14px );
  width: 78px; height: 78px; border-radius: 50%;
  background: radial-gradient( circle at 50% 38%, #2b3241, #191d25 );
  border: 1px solid var( --line ); box-shadow: var( --shadow );
  display: grid; place-items: center; touch-action: none;
}
#camPuck .knob {
  width: 30px; height: 30px; border-radius: 50%; background: var( --accent );
  box-shadow: 0 2px 8px rgba( 0, 0, 0, .5 ); pointer-events: none;
  transition: transform .12s ease-out;
}
#camPuck .ring { position: absolute; inset: 8px; border-radius: 50%; border: 1px dashed #ffffff1f; }
#camExtra {
  position: absolute; z-index: 3;
  right: calc( var( --safe-r ) + 100px );
  bottom: calc( var( --safe-b ) + var( --drawer-h, 0px ) + 14px );
  display: flex; flex-direction: column; gap: 6px;
}
#camExtra button { width: 44px; height: 44px; padding: 0; border-radius: 12px; }

.edgeButton {
  position: absolute; transform: translate( -50%, -50% );
  width: 38px; height: 38px; padding: 0; border-radius: 50%;
  background: rgba( 28, 32, 40, .92 ); font-size: 19px; font-weight: 600;
  border-color: var( --line ); box-shadow: var( --shadow );
}

/* Above the drawer rather than below it: the drawer is open exactly when a hint
   is most likely, and a hint you cannot read is worse than none. */
#hint {
  position: absolute; left: 50%; transform: translateX( -50% );
  top: calc( var( --safe-t ) + 56px );
  background: rgba( 20, 23, 29, .9 ); border: 1px solid var( --line );
  border-radius: 999px; padding: 7px 15px; font-size: 12.5px; color: var( --dim );
  pointer-events: none; opacity: 0; transition: opacity .25s; white-space: nowrap; max-width: 80vw;
  overflow: hidden; text-overflow: ellipsis;
}
#hint.show { opacity: 1; }

#stats {
  position: absolute; left: calc( var( --safe-l ) + 74px ); top: calc( var( --safe-t ) + 56px );
  font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var( --dim ); background: rgba( 20, 23, 29, .82 );
  border: 1px solid var( --line ); border-radius: 9px; padding: 6px 9px; white-space: pre;
  pointer-events: none;
}

#toast {
  position: absolute; left: 50%; top: calc( var( --safe-t ) + 100px ); transform: translateX( -50% );
  background: var( --panel ); border: 1px solid var( --line ); border-radius: 11px;
  padding: 9px 15px; box-shadow: var( --shadow ); opacity: 0; transition: opacity .2s, transform .2s;
  pointer-events: none; max-width: 80vw; text-align: center; font-size: 14px;
}
#toast.show { opacity: 1; transform: translateX( -50% ) translateY( 4px ); }
#toast.bad { border-color: #5b3a2c; color: #ffbfa4; }

/* --- modal ----------------------------------------------------------------- */

#modalHost {
  position: absolute; inset: 0; background: rgba( 8, 10, 14, .62 );
  display: grid; place-items: center; padding: 20px; backdrop-filter: blur( 3px );
  overflow-y: auto;
}
#modalHost.hidden { display: none; }
.modal {
  width: min( 520px, 100% ); max-height: 86vh; overflow-y: auto;
  background: var( --panel ); border: 1px solid var( --line ); border-radius: 18px;
  box-shadow: var( --shadow ); padding: 18px;
  display: flex; flex-direction: column; gap: 14px;
}
.modal h2 { margin: 0; font-size: 19px; }
.modal h4 { margin: 0 0 4px; font-size: 13px; color: var( --dim ); font-weight: 600; }
.modal .section { display: flex; flex-direction: column; gap: 8px; }
.modal .actions { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
.linkRow { display: flex; gap: 6px; align-items: center; }
.linkRow input { font-family: ui-monospace, Menlo, monospace; font-size: 11.5px; }
.hintText { font-size: 12.5px; color: var( --dim ); margin: 0; }

.swatches { display: flex; gap: 7px; flex-wrap: wrap; }
.swatch {
  width: 40px; height: 40px; border-radius: 11px; padding: 0;
  border: 2px solid transparent; box-shadow: inset 0 0 0 1px #0006;
}
.swatch.on { border-color: var( --text ); }

/* --- home screen ------------------------------------------------------------ */

#home {
  position: absolute; inset: 0; background: var( --bg ); overflow-y: auto;
  padding: calc( var( --safe-t ) + 28px ) 20px calc( var( --safe-b ) + 28px );
  display: flex; flex-direction: column; align-items: center; gap: 18px;
  -webkit-user-select: text; user-select: text;
}
#home .inner { width: min( 620px, 100% ); display: flex; flex-direction: column; gap: 16px; }
#home h1 { margin: 0; font-size: 30px; letter-spacing: -.02em; }
#home .tagline { margin: -10px 0 0; color: var( --dim ); }
.worldCard {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px; width: 100%;
  text-align: left; justify-content: flex-start; border-radius: var( --radius );
}
.worldCard .meta { font-size: 12px; color: var( --dim ); }
.worldCard .title { font-weight: 600; }
.worldCard .body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.worldCard .title, .worldCard .meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

#loading {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: var( --bg ); gap: 12px; text-align: center; color: var( --dim );
}
#loading.hidden { display: none; }
.spinner {
  width: 34px; height: 34px; border-radius: 50%; margin: 0 auto 12px;
  border: 3px solid var( --line ); border-top-color: var( --accent );
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate( 360deg ); } }

.hidden { display: none !important; }

/* Phones and narrow splits: the rail goes horizontal along the bottom and the
   floating panels become full-width sheets, or nothing fits. */
@media ( max-width: 640px ) {
  #topbar { gap: 4px; padding-left: calc( var( --safe-l ) + 6px ); padding-right: calc( var( --safe-r ) + 6px ); }
  #topbar .badge { display: none; }        /* no room, and the version is not urgent */
  #topbar .spacer { display: none; }
  /* The name takes whatever the buttons leave, instead of a fixed slice that
     pushes the last button off the screen. */
  #worldName { width: auto; flex: 1 1 40px; min-width: 0; }
  #topbar button { padding: 8px; }
  #tools {
    left: 50%; right: auto; top: auto;
    bottom: calc( var( --safe-b ) + var( --drawer-h, 0px ) + 10px );
    transform: translateX( -50% ); flex-direction: row;
  }
  #tools button { width: 48px; height: 48px; }
  #drawer { left: 0; max-height: 46vh; padding: 8px 10px calc( var( --safe-b ) + 8px ) 10px; }
  #drawer.tall { height: min( 40vh, 280px ); }
  #assetGrid { grid-template-columns: repeat( auto-fill, minmax( 68px, 1fr ) ); grid-auto-rows: 74px; gap: 6px; }
  .asset .caption { display: none; }
  /* Everything stacks above the drawer: tool rail, then the selection icons,
     then the camera. Overlapping them is the difference between a phone that
     works and one where the delete button is under the camera puck. */
  #selection {
    left: 0; right: 0; top: auto; transform: none; align-items: center;
    bottom: calc( var( --safe-b ) + var( --drawer-h, 0px ) + 78px );
  }
  .selGrid { grid-template-columns: repeat( 5, 40px ); }
  .selBtn { width: 40px; height: 40px; }
  .selMore { position: absolute; bottom: 104px; right: 10px; }
  #camPuck {
    width: 58px; height: 58px;
    bottom: calc( var( --safe-b ) + var( --drawer-h, 0px ) + 188px );
  }
  #camPuck .knob { width: 24px; height: 24px; }
  #camExtra {
    bottom: calc( var( --safe-b ) + var( --drawer-h, 0px ) + 188px );
    right: calc( var( --safe-r ) + 80px );
    flex-direction: row;
  }
  #camExtra button { width: 40px; height: 40px; }
  #hint { top: calc( var( --safe-t ) + 52px ); }
}

/* Short screens — a phone held sideways is 390 pixels tall.
 *
 * A bottom drawer would leave almost no world to look at, so the library moves
 * to a column down the right, the tool rail goes back to a vertical strip (there
 * is width to spare and no height at all), and everything sheds its labels. */
@media ( max-height: 540px ) {
  #topbar { padding-top: calc( var( --safe-t ) + 4px ); padding-bottom: 4px; }

  #tools {
    left: calc( var( --safe-l ) + 6px ); top: 50%; bottom: auto;
    transform: translateY( -50% ); flex-direction: column; padding: 4px; gap: 3px;
  }
  #tools button { width: 42px; height: 42px; }
  #tools button span:last-child { display: none; }

  #drawer {
    --mode: side;
    left: auto; right: 0; top: 0; bottom: 0; width: 236px;
    height: auto; max-height: none;
    border-top: none; border-left: 1px solid var( --line );
    border-radius: 0; box-shadow: -8px 0 26px rgba( 0, 0, 0, .35 );
    padding: calc( var( --safe-t ) + 8px ) calc( var( --safe-r ) + 8px ) calc( var( --safe-b ) + 8px ) 8px;
    gap: 6px;
  }
  #drawer.tall { height: auto; }
  #drawer.hidden { transform: translateX( 105% ); }
  #drawerHandle {
    top: 50%; left: -30px; transform: translateY( -50% );
    border: 1px solid var( --line ); border-right: none;
    border-radius: 12px 0 0 12px; padding: 26px 6px;
  }

  #assetGrid { grid-template-columns: repeat( auto-fill, minmax( 62px, 1fr ) ); grid-auto-rows: 66px; gap: 5px; }
  .asset .caption { display: none; }
  .asset .star { width: 18px; height: 18px; font-size: 10px; }
  .chip { padding: 5px 10px; font-size: 12px; }
  input[type=search], input[type=text] { padding: 6px 10px; }

  /* Clear of the side panel, and clear of each other: the selection icons at
     the top, the camera at the bottom. */
  #selection {
    top: calc( var( --safe-t ) + 40px ); bottom: auto; transform: none;
    right: calc( var( --safe-r ) + var( --drawer-w, 0px ) + 10px );
  }
  .selGrid { grid-template-columns: repeat( 2, 36px ); gap: 4px; padding: 4px; }
  .selBtn { width: 36px; height: 36px; font-size: 15px; }
  .selMore { width: 150px; }

  #camPuck {
    width: 54px; height: 54px;
    right: calc( var( --safe-r ) + var( --drawer-w, 0px ) + 12px );
    bottom: calc( var( --safe-b ) + 12px );
  }
  #camPuck .knob { width: 22px; height: 22px; }
  #camExtra {
    flex-direction: row;
    right: calc( var( --safe-r ) + var( --drawer-w, 0px ) + 74px );
    bottom: calc( var( --safe-b ) + 12px );
  }
  #camExtra button { width: 36px; height: 36px; }

  #hint { left: 40%; }
}

@media ( prefers-reduced-motion: reduce ) {
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}
