/* wel.cool
   -------------------------------------------------------------------------
   The room is warm and dim so the picture can do the lighting; everything you
   read is light and plain, because it is written for ten-year-olds and their
   teachers and has to stay easy to read.

   The old theme's pink is gone. The palette here is the room's: amber, burnt
   orange, rust and honey wood, with the laser's green as the one cold note. */

:root {
  --bg: #fbf8f2;
  --fg: #1b1710;
  --muted: #6d6459;
  --accent: #b4531c;
  --lamp: #ffb457;
  --line: #e7ded0;
  --radius: 10px;
  --measure: 40rem;
  --gap: clamp(1rem, 3vw, 2rem);
  --dim: #16110b;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #16110b; --fg: #f4ede1; --muted: #a79c8c;
    --accent: #e9a24a; --line: #2e261c;
  }
}
:root[data-theme="dark"] {
  --bg: #16110b; --fg: #f4ede1; --muted: #a79c8c;
  --accent: #e9a24a; --line: #2e261c;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 400 clamp(1rem, 0.96rem + 0.2vw, 1.0625rem)/1.65 system-ui, sans-serif;
  -webkit-text-size-adjust: 100%;
}

img, video, iframe { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

.skip { position: absolute; left: -9999px; }
.skip:focus { left: 1rem; top: 1rem; padding: .5rem 1rem; background: var(--accent); color: #fff; z-index: 40; }

/* Read out loud and never drawn. The room has no visible heading of its own:
   the room is the heading. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

:focus-visible { outline: 2px solid var(--lamp); outline-offset: 3px; }

/* -- chrome ------------------------------------------------------------- */

.site, .site-footer, main { padding-inline: var(--gap); }
.site { padding-block: 1.5rem; }
.site__logo { font-weight: 700; font-size: 1.125rem; text-decoration: none; letter-spacing: -.01em; }

.site-footer {
  margin-top: 4rem; padding-block: 2rem;
  border-top: 1px solid var(--line); color: var(--muted); font-size: .9rem;
}

/* The trail back: the room, then the zones this piece sits in. A post belongs
   to as many as it belongs to, so this is a row of ways back, not one path. */
.trail {
  max-width: 62rem; margin: 0 auto 1.5rem;
  display: flex; flex-wrap: wrap; gap: .4rem .5rem;
  font-size: .85rem;
}
.trail a {
  padding: .2rem .7rem; border: 1px solid var(--line); border-radius: 999px;
  color: var(--muted); text-decoration: none;
}
.trail a:hover { border-color: var(--accent); color: var(--accent); }

/* == the room ============================================================ */

body.is-room {
  --bg: #140f09; --fg: #f6efe3; --muted: #b0a493; --line: #33291d;
  background: radial-gradient(120% 90% at 50% 0%, #241a10 0%, #140f09 70%);
}
body.is-room .site { padding-block: 1.25rem; }
body.is-room main { padding-inline: 0; }

.room__intro {
  max-width: 62rem; margin: 0 auto 1.25rem; padding-inline: var(--gap);
}
.room__intro h1 {
  margin: 0; font-size: clamp(1.8rem, 1.2rem + 3vw, 3rem);
  line-height: 1.05; letter-spacing: -.02em;
}
.room__intro p { margin: .4rem 0 0; color: var(--muted); max-width: 34rem; }

/* The frame keeps the picture's own proportions and is scaled to fit the
   screen, so you land in the whole room rather than in the top half of it.
   Everything in the room is positioned against this box, which is why the
   shape has to come from the image and not from a guess. */
.room { position: relative; }
.room__frame {
  position: relative; overflow: hidden;
  aspect-ratio: var(--room-ar, 4 / 3);
  max-width: min(100%, calc((100svh - 11rem) * (var(--room-ar, 1.333))));
  margin-inline: auto;
  background: #0f0b06;
}
.room__layer { position: absolute; inset: 0; will-change: transform; transition: transform .35s ease-out; }
.room__layer img { width: 100%; height: 100%; object-fit: cover; }

.room__note {
  margin: .6rem auto 0; max-width: 62rem; padding-inline: var(--gap);
  color: var(--muted); font-size: .8rem;
}

/* -- hotspots -----------------------------------------------------------
   A hotspot is positioned on the box around its object and clipped to the
   object's own outline, so what takes the click is the shape of the thing and
   not a rectangle around it. The anchor itself lets the pointer through; only
   the clipped shape and the lamp catch it. */

.room__spots { position: absolute; inset: 0; }

.spot { position: absolute; pointer-events: none; text-decoration: none; }

.spot__shape {
  position: absolute; inset: 0; pointer-events: auto; cursor: pointer;
  background: radial-gradient(60% 60% at 50% 45%, rgba(255, 190, 110, .55), rgba(255, 150, 60, 0) 72%);
  opacity: 0; transition: opacity .25s ease;
  mix-blend-mode: screen;
}
.spot__art {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; opacity: 0; transition: opacity .25s ease;
  mix-blend-mode: normal;
}

/* The lamp is the affordance. It is there before you touch anything and it is
   still there with every animation off, which is why hover can never be the
   only thing marking an object as clickable. */
.spot__lamp {
  position: absolute; pointer-events: auto; cursor: pointer;
  width: 15px; height: 15px; margin: -7.5px 0 0 -7.5px;
  border-radius: 50%;
  /* A cream bead with a dark rim: the room is bright in places and nearly
     black in others, and the same dot has to be findable in both. */
  background: radial-gradient(circle at 42% 38%, #fffaf0 0%, var(--lamp) 60%, #e08a2e 100%);
  border: 2px solid rgba(28, 16, 4, .8);
  box-shadow: 0 0 0 1px rgba(255, 224, 176, .45), 0 0 16px 5px rgba(255, 170, 70, .5);
  transition: transform .25s ease, box-shadow .25s ease;
  animation: lamp 4.5s ease-in-out infinite;
}

.spot__name {
  position: absolute; left: 50%; top: 100%;
  transform: translateX(-50%); margin-top: .35rem;
  padding: .2rem .6rem; border-radius: 999px;
  background: rgba(24, 16, 8, .88); color: #fdf3e2;
  font-size: .8rem; line-height: 1.4; white-space: nowrap;
  opacity: 0; transition: opacity .2s ease, margin .2s ease;
}
/* Against a side or the floor of the picture the name hangs inward instead,
   where there is room for it. */
.spot--l .spot__name { left: 0; transform: none; }
.spot--r .spot__name { left: auto; right: 0; transform: none; }
.spot--b .spot__name { top: auto; bottom: 100%; margin: 0 0 .35rem; }

.spot:hover .spot__shape,
.spot:focus-within .spot__shape,
.spot.is-open .spot__shape { opacity: 1; }
.spot:hover .spot__art,
.spot:focus-within .spot__art,
.spot.is-open .spot__art { opacity: 1; }
.spot:hover .spot__lamp,
.spot:focus-within .spot__lamp,
.spot.is-open .spot__lamp {
  transform: scale(1.35);
  box-shadow: 0 0 18px 7px rgba(255, 170, 70, .55);
}
.spot:hover .spot__name,
.spot:focus-within .spot__name,
.spot.is-open .spot__name { opacity: 1; margin-top: .6rem; }
.spot--b:hover .spot__name,
.spot--b:focus-within .spot__name,
.spot--b.is-open .spot__name { margin: 0 0 .6rem; }

@keyframes lamp {
  0%, 100% { box-shadow: 0 0 0 1px rgba(255, 224, 176, .45), 0 0 12px 3px rgba(255, 170, 70, .35); }
  50% { box-shadow: 0 0 0 1px rgba(255, 224, 176, .6), 0 0 20px 7px rgba(255, 170, 70, .6); }
}

/* -- the panel ----------------------------------------------------------- */

.panel {
  position: fixed; z-index: 20; inset: auto 0 0 0;
  padding: var(--gap);
  display: flex; justify-content: flex-end;
  pointer-events: none;
}
.panel[hidden] { display: none; }
.has-panel .room__frame { filter: brightness(.82) saturate(.95); transition: filter .25s ease; }
.panel__inner {
  pointer-events: auto;
  width: min(26rem, 100%); max-height: min(70vh, 40rem); overflow-y: auto;
  padding: 1.25rem 1.4rem 1.6rem;
  background: rgba(26, 18, 10, .96);
  border: 1px solid #3a2d1e; border-radius: var(--radius);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .55);
  opacity: 0; transform: translateY(.6rem);
  transition: opacity .2s ease, transform .2s ease;
}
.panel.is-open .panel__inner { opacity: 1; transform: none; }
.panel__close {
  float: right; margin: -.25rem -.4rem 0 .5rem;
  padding: .2rem .6rem; border: 1px solid #453524; border-radius: 999px;
  background: none; color: var(--muted); font: inherit; font-size: .8rem;
  cursor: pointer;
}
.panel__close:hover { color: var(--lamp); border-color: var(--lamp); }
.panel .zone { margin: 0; }
.panel .zone:focus { outline: none; }

/* -- the zone list -------------------------------------------------------
   Always in the page: the keyboard route, the phone route, the no-scripting
   route and the site map, all the same list. */

.zonelist {
  max-width: 62rem; margin: 3rem auto 0; padding-inline: var(--gap);
  display: grid; gap: 2rem;
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
}
.zonelist__head {
  grid-column: 1 / -1; margin: 0; font-size: .85rem; font-weight: 600;
  text-transform: lowercase; letter-spacing: .08em; color: var(--muted);
}
.zonelist__archive { grid-column: 1 / -1; margin: 0; }
.zonelist__archive a { color: var(--accent); text-underline-offset: .2em; }

.zone { scroll-margin-top: 2rem; }
.zone__name { margin: 0; font-size: 1.15rem; letter-spacing: -.01em; }
.zone__blurb { margin: .2rem 0 .6rem; color: var(--muted); font-size: .9rem; }

.items { margin: 0; padding: 0; list-style: none; }
.items a {
  display: flex; flex-wrap: wrap; gap: 0 .5rem; align-items: baseline;
  padding: .3rem 0; text-decoration: none; border-bottom: 1px solid transparent;
}
.items a:hover { border-bottom-color: var(--line); }
.items a:hover .item__title { color: var(--lamp); }
.item__title { text-underline-offset: .2em; }
.items time, .item__kind { color: var(--muted); font-size: .8rem; }
.item--out .item__title::after { content: " ↗"; color: var(--muted); }

/* -- narrow screens ------------------------------------------------------
   The picture stops being the navigation. Nobody should be pinching about
   hunting for a 40-pixel laser cutter; the zone list below is the way in and
   the room is simply the picture at the top of it. */

@media (max-width: 47.99em) {
  .room__spots { display: none; }
  .panel {
    inset: 0; padding: 0; align-items: flex-end; justify-content: stretch;
    background: rgba(12, 8, 4, .6);
  }
  .panel__inner {
    width: 100%; max-height: 80vh;
    border-radius: var(--radius) var(--radius) 0 0; border-bottom: 0;
  }
  .zonelist { margin-top: 2rem; }
}

/* -- with animation off --------------------------------------------------
   Everything still works: the lamps stay lit, the labels still appear, and
   nothing that marks an object as clickable depended on movement. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .spot__lamp { animation: none; opacity: 1; }
  .room__layer { transform: none !important; }
}

/* == reading ============================================================= */

.hero { max-width: 62rem; margin: 2rem auto 3rem; }
.hero p { margin: 0; color: var(--muted); text-transform: lowercase; letter-spacing: .04em; }
.hero h1 { margin: .25rem 0 0; font-size: clamp(1.9rem, 1.2rem + 3.2vw, 3.4rem); line-height: 1.1; letter-spacing: -.02em; }
.hero em { color: var(--accent); font-style: normal; }

.tagbar { max-width: 62rem; margin: 0 auto 2rem; }
.tagbar ul { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0; padding: 0; list-style: none; }
.tagbar a {
  display: inline-flex; gap: .4rem; align-items: baseline;
  padding: .3rem .7rem; border: 1px solid var(--line); border-radius: 999px;
  font-size: .85rem; text-decoration: none;
}
.tagbar a:hover { border-color: var(--accent); color: var(--accent); }
.tagbar span { color: var(--muted); font-variant-numeric: tabular-nums; }

.cards {
  max-width: 62rem; margin: 0 auto; padding: 0; list-style: none;
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
}
.card a { display: block; text-decoration: none; height: 100%; }
.card__media { overflow: hidden; border-radius: var(--radius); background: var(--line); aspect-ratio: 3 / 2; }
.card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.card a:hover .card__media img { transform: scale(1.04); }
.card__text h2 { margin: .7rem 0 .2rem; font-size: 1.05rem; line-height: 1.3; }
.card__text time { color: var(--muted); font-size: .85rem; }

.post { max-width: 62rem; margin: 0 auto; }
.post__head { margin-bottom: 2rem; }
.post__head h1 { margin: 0 0 .4rem; font-size: clamp(1.7rem, 1.2rem + 2.4vw, 2.7rem); line-height: 1.15; letter-spacing: -.02em; }
.post__head time { color: var(--muted); font-size: .9rem; }

.lede { margin: 0 0 2.5rem; }
.lede img { width: 100%; border-radius: var(--radius); }

.prose { max-width: var(--measure); }
.prose > * { margin-block: 0 1.1em; }
.prose h2 { margin-block: 2.2rem .6rem; font-size: 1.35rem; letter-spacing: -.01em; }
.prose h3 { margin-block: 1.8rem .5rem; font-size: 1.15rem; }
.prose a { color: var(--accent); text-underline-offset: .18em; }
.prose figure { margin-inline: 0; }
.prose figure img, .prose figure video { border-radius: var(--radius); }
.prose blockquote {
  margin-inline: 0; padding-left: 1rem;
  border-left: 3px solid var(--accent); color: var(--muted);
}
.prose code { background: var(--line); padding: .1em .35em; border-radius: 4px; font-size: .9em; }
.prose pre { background: var(--line); padding: 1rem; border-radius: var(--radius); overflow-x: auto; }
.prose pre code { background: none; padding: 0; }
.prose ol, .prose ul { padding-left: 1.3rem; }
.prose li { margin-block: .3em; }

.embed { position: relative; aspect-ratio: 16 / 9; }
.embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; border-radius: var(--radius); }

.gallery {
  max-width: 62rem; margin: 2rem 0; padding: 0; list-style: none;
  display: grid; gap: .5rem;
  grid-template-columns: repeat(auto-fill, minmax(min(11rem, 45%), 1fr));
}
.gallery img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 6px; }

.tags { display: flex; flex-wrap: wrap; gap: .5rem; margin: 3rem 0 0; padding: 0; list-style: none; }
.tags a { padding: .25rem .65rem; border: 1px solid var(--line); border-radius: 999px; font-size: .8rem; color: var(--muted); text-decoration: none; }
.tags a:hover { border-color: var(--accent); color: var(--accent); }

/* The 3D models of the room's objects. One page each, under /model/.
   templates/model.js draws the .glb into a canvas over the still. */

.model {
  position: relative; max-width: 62rem; margin: 1.5rem 0;
  aspect-ratio: 4 / 3; overflow: hidden;
  background: #17110a; border-radius: var(--radius);
}
.model__poster, .model__canvas {
  position: absolute; inset: 0; display: block;
  width: 100%; height: 100%; object-fit: contain;
}
.model__canvas { opacity: 0; transition: opacity .45s ease; touch-action: none; cursor: grab; }
.model__canvas:active { cursor: grabbing; }
.model--drawn .model__canvas { opacity: 1; }
.model--drawn .model__poster { opacity: 0; transition: opacity .45s ease; }
.model__lede { max-width: 42rem; }
.model__note { max-width: 42rem; color: var(--muted); font-size: .9rem; }

@media (prefers-reduced-motion: reduce) {
  .model__canvas, .model--drawn .model__poster,
  .model__focus { transition: none; }
}

/* The 3D room. The front page is this and nothing else, so it fills the
   window rather than sitting in the column the writing uses. */

.room {
  position: relative;
  aspect-ratio: 16 / 10;
  max-height: calc(100dvh - 8.5rem);
  background: #0c0806;
  overflow: hidden;
}
.room .model__poster,
.room .model__canvas { object-fit: cover; }

/* Fullscreen, on whatever you are holding. A 16/10 box is most of a laptop
   and half a phone, and the rest of the phone was empty page. The front page
   carries no chrome and does not scroll, so the room can simply be the
   window. Scoped to the front page: the model pages keep their column, and
   the hotspot room keeps the frame its coordinates are measured against. */
body.is-fullscreen {
  height: 100vh; height: 100dvh;
  overflow: hidden; overscroll-behavior: none;
}
body.is-fullscreen main { height: 100%; padding: 0; }
body.is-fullscreen .room {
  aspect-ratio: auto;
  width: 100%; height: 100%; max-height: none;
}

/* Moving the furniture: the hint, and the coordinates to paste back into
   models/room.py once something has been dragged somewhere better. */

.model__moved {
  position: absolute;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .72rem;
  line-height: 1.5;
}
.model__moved {
  left: 14px; bottom: 12px;
  max-width: min(26rem, 62%);
  padding: .55rem .7rem .6rem;
  background: rgba(10, 7, 5, .88);
  color: #e9d9c0;
  border: 1px solid rgba(233, 217, 192, .22);
  border-radius: var(--radius);
}
.model__moved strong { display: block; color: rgba(233, 217, 192, .55); font-weight: 500; }
.model__moved pre { margin: .35rem 0 .5rem; white-space: pre-wrap; font: inherit; }
.model__moved button {
  font: inherit; color: inherit; background: transparent; cursor: pointer;
  border: 1px solid rgba(233, 217, 192, .3); border-radius: 3px;
  padding: .12rem .55rem;
}
.model__moved button:hover { border-color: rgba(233, 217, 192, .6); }

/* One thing at a time. Clicking an object walks the camera up to it and takes
   the light off the rest of the room; this says what is being looked at, and
   is the way back out for anyone who would rather not click beside it. */

.model__focus {
  position: absolute;
  left: 50%; bottom: 12px; transform: translateX(-50%);
  display: flex; align-items: center; gap: .7rem;
  padding: .4rem .5rem .4rem .75rem;
  max-width: min(32rem, 82%);
  flex-wrap: wrap;
  background: rgba(10, 7, 5, .88);
  color: #e9d9c0;
  border: 1px solid rgba(233, 217, 192, .22);
  border-radius: var(--radius);
  font-size: .8rem; line-height: 1.4;
  opacity: 0; pointer-events: none;
  transition: opacity .35s ease;
}
.model--focused .model__focus { opacity: 1; pointer-events: auto; }
.model__focus strong { font-weight: 500; }
.model__focus button {
  font: inherit; color: inherit; background: transparent; cursor: pointer;
  border: 1px solid rgba(233, 217, 192, .3); border-radius: 3px;
  padding: .12rem .55rem;
}
.model__focus button:hover { border-color: rgba(233, 217, 192, .6); }
/* What the thing holds: one button each, and the first is what a second click
   on the object itself opens. */
.model__focus-opens {
  position: absolute; z-index: 3;
  display: flex; flex-wrap: wrap; justify-content: center; gap: .6rem;
  width: max-content; max-width: min(32rem, calc(100% - 24px));
}
.model__focus-opens[hidden], .model__focus-opens:empty { display: none; }
/* Drawers are opened on their fronts. Retain controls for keyboard users,
   revealing only the focused control instead of covering the painted fronts. */
.model__focus-open[data-drawer]:not(:focus-visible) {
  position: absolute; width: 1px; height: 1px; min-height: 0;
  padding: 0; margin: -1px; border: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.model--focused:has([data-drawer]) .model__canvas { cursor: pointer; }
.model__focus-open {
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  min-height: 52px; max-width: 100%; padding: .55rem .9rem;
  font: inherit; font-size: .95rem; font-weight: 600; line-height: 1.3;
  color: #ffca78; background: rgba(24, 15, 8, .97);
  border: 2px solid #d79845; border-radius: 12px;
  box-shadow: 0 5px 18px rgba(0, 0, 0, .5);
  cursor: pointer; overflow-wrap: anywhere;
}
.model__focus-open svg {
  flex: 0 0 auto; width: 30px; height: 30px;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.model__focus-open[aria-pressed="true"], .model__focus-open:hover { color: #201408; background: #ffca78; }
.model__focus-open:focus-visible { outline: 3px solid #fff0d6; outline-offset: 3px; }
.model--moving .model__focus-opens, .model--app .model__focus-opens { visibility: hidden; }

/* The lamp's shade and glow can be tried in any colour. The wheel opens the
   browser's own picker, so it works with touch and keyboard as well. */
.model__lamp-color { display: inline-flex; align-items: center; gap: .4rem; }
.model__lamp-color[hidden] { display: none; }
.model__lamp-color label { display: inline-flex; align-items: center; gap: .35rem; cursor: pointer; }
.model__lamp-color input[type="color"] {
  width: 30px; height: 30px; padding: 3px; border: 0; border-radius: 50%;
  background: conic-gradient(red, #ff0, #0f0, #0ff, #00f, #f0f, red);
  cursor: pointer;
}
.model__lamp-color input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.model__lamp-color input[type="color"]::-webkit-color-swatch {
  border: 1px solid rgba(10, 7, 5, .8); border-radius: 50%;
}
.model__lamp-color input[type="color"]::-moz-color-swatch {
  border: 1px solid rgba(10, 7, 5, .8); border-radius: 50%;
}

/* The ring under a thing that has been picked up. It sits on the floor just
   below it and turns it: take it and swing it round, the way you would turn
   something on a table with a finger. */

.model__handle {
  position: absolute;
  z-index: 7;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;          /* its middle on the point it is given */
  display: grid; place-items: center;
  padding: 0; cursor: grab;
  color: #e9d9c0;
  background: rgba(10, 7, 5, .82);
  border: 1px solid rgba(233, 217, 192, .35);
  border-radius: 50%;
  touch-action: none;
}
.model__handle[hidden] { display: none; }
.model__handle:hover { border-color: var(--lamp); color: var(--lamp); }
.model__handle:active { cursor: grabbing; }
/* The paint one is a button, not something you drag. */
.model__handle--paint { cursor: pointer; }
.model__handle--paint:active { cursor: pointer; }
.model__handle svg {
  display: block; width: 19px; height: 19px;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* While something is held, the invitation to look at things is wrong. */
.model--moving .model__focus { opacity: 0; pointer-events: none; }

/* Trying a colour out: a swatch per material, and the numbers to paste back
   into the object's script. Sits opposite the coordinates a drag hands back,
   because both are the same kind of note to yourself. */

.model__paint {
  position: absolute;
  left: 14px; top: 12px;
  max-width: min(22rem, 70%);
  max-height: calc(100% - 24px);
  overflow-y: auto;
  padding: .55rem .7rem .4rem;
  background: rgba(10, 7, 5, .9);
  color: #e9d9c0;
  border: 1px solid rgba(233, 217, 192, .22);
  border-radius: var(--radius);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .72rem; line-height: 1.5;
  z-index: 6;
}
.model__paint[hidden] { display: none; }
.model__paint > strong { display: block; color: rgba(233, 217, 192, .55); font-weight: 500; }
.model__paint ul { margin: .35rem 0 .45rem; padding: 0; list-style: none; }
.model__paint li { display: flex; align-items: center; gap: .45rem; margin: .2rem 0; }
.model__paint li span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.model__paint li code { color: rgba(233, 217, 192, .6); }
.model__paint input[type="color"] {
  flex: 0 0 auto; width: 26px; height: 18px; padding: 0;
  background: none; border: 1px solid rgba(233, 217, 192, .3);
  border-radius: 3px; cursor: pointer;
}
.model__paint-buttons { display: flex; gap: .4rem; margin: 0; }
.model__paint button {
  font: inherit; color: inherit; background: transparent; cursor: pointer;
  border: 1px solid rgba(233, 217, 192, .3); border-radius: 3px;
  padding: .12rem .55rem;
}
.model__paint button:hover { border-color: rgba(233, 217, 192, .6); }

/* == what an object opens ================================================
   One object, one thing, in a panel over the room. The room stays behind it
   and stays dim, so it is clear you are still standing in front of the piano
   rather than on a different page; the button next to `sluiten` takes the
   panel edge to edge for anyone who would rather not see the room at all. */

.model__app {
  position: absolute;
  top: 5%; bottom: 7%;
  left: 50%; transform: translateX(-50%);
  width: min(54rem, 88%);
  display: flex; flex-direction: column;
  background: rgba(12, 8, 6, .95);
  border: 1px solid rgba(233, 217, 192, .22);
  border-radius: var(--radius);
  box-shadow: 0 26px 70px rgba(0, 0, 0, .6);
  overflow: hidden;
  z-index: 5;
}
.model__app[hidden] { display: none; }

/* Fixed rather than absolute: out of the room's box altogether, over the
   whole window, whatever the room is doing behind it. */
.model__app--wide {
  position: fixed; inset: 0;
  width: auto; transform: none;
  border: 0; border-radius: 0;
  z-index: 60;
}

.model__app-bar {
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .5rem .45rem .85rem;
  color: #e9d9c0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .72rem; line-height: 1.5;
  border-bottom: 1px solid rgba(233, 217, 192, .16);
}
.model__app-bar strong { font-weight: 500; margin-right: auto; }
.model__app-bar button {
  font: inherit; color: inherit; background: transparent; cursor: pointer;
  border: 1px solid rgba(233, 217, 192, .3); border-radius: 3px;
  padding: .12rem .55rem;
}
.model__app-bar button:hover { border-color: rgba(233, 217, 192, .6); }
.model__app-icon { display: grid; place-items: center; padding: .15rem .3rem; }
.model__app-icon svg {
  display: block; width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

.model__app-frame {
  flex: 1; min-height: 0;
  width: 100%; height: auto;
  border: 0; background: var(--bg);
}
.model__app-frame[hidden] { display: none; }

/* For anything that will not be framed. */
.model__app-out {
  margin: auto; padding: .6rem 1.2rem;
  color: #e9d9c0; text-decoration: none;
  border: 1px solid rgba(233, 217, 192, .35); border-radius: var(--radius);
}
.model__app-out:hover { border-color: var(--lamp); color: var(--lamp); }

/* The panel says which object it belongs to, so the plaque would say it
   twice and cover the bottom of the panel doing it. */
.model--app .model__focus { opacity: 0; pointer-events: none; }

/* On a phone there is no room to show the room around the panel. */
@media (max-width: 40rem) {
  .model__app {
    inset: 0; width: auto; transform: none;
    border: 0; border-radius: 0;
  }
}

/* == a page inside the panel ============================================
   The lists and the chords are built here and framed by the room, so they
   get the site's light palette and none of its chrome. */

body.is-app { padding: 0; }
body.is-app main { padding: 1.25rem var(--gap) 2.5rem; }
body.is-app h1 { margin: 0; font-size: 1.6rem; letter-spacing: -.01em; }
.app__intro { max-width: 34rem; margin: .4rem 0 0; color: var(--muted); }

/* -- the chords ---------------------------------------------------------
   Six strings down, four frets across, a dot per finger. Drawn from the
   numbers in akkoorden.yaml by scripts/akkoorden.mjs, in currentColor and
   the site's own accent, so it reads in either colour scheme. */

.chords {
  display: grid; gap: 1.4rem 1rem;
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
  margin: 1.5rem 0 2.5rem;
}
.chord { margin: 0; text-align: center; }
.chord__grid { display: block; width: 100%; max-width: 8rem; height: auto; margin: 0 auto .35rem; }
.chord__grid line { stroke: var(--muted); stroke-width: 1.1; }
.chord__grid .chord__nut { stroke: var(--fg); stroke-width: 4; }
.chord__open { fill: none; stroke: var(--fg); stroke-width: 1.4; }
.chord__mute { fill: none; stroke: var(--fg); stroke-width: 1.6; stroke-linecap: round; }
.chord__dot, .chord__barre { fill: var(--accent); }
.chord__finger {
  fill: #fff; text-anchor: middle; dominant-baseline: central;
  font: 600 8px/1 system-ui, sans-serif;
}
.chord__base {
  fill: var(--muted); dominant-baseline: central;
  font: 500 9px/1 system-ui, sans-serif;
}
.chord figcaption strong { display: block; font-size: 1.05rem; }
.chord figcaption span { display: block; margin-top: .15rem; color: var(--muted); font-size: .76rem; line-height: 1.35; }

/* -- reading a tab ------------------------------------------------------ */

.tab { max-width: 40rem; }
.tab h2 { margin: 0 0 .3rem; font-size: 1.15rem; }
.tab p { margin: 0 0 .8rem; color: var(--muted); }
.tab__lines {
  margin: 0; padding: .85rem 1rem; overflow-x: auto;
  border: 1px solid var(--line); border-radius: var(--radius);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .8rem; line-height: 1.5;
}
.tab__note { margin: .6rem 0 0; font-size: .85rem; }

/* -- the lists ---------------------------------------------------------- */

.lijst { max-width: 40rem; margin: 1.8rem 0 0; }
.lijst h2 { margin: 0 0 .2rem; font-size: 1.15rem; }
.lijst__over { margin: 0 0 .6rem; color: var(--muted); }
.lijst__items { margin: 0; padding-left: 1.15rem; }
.lijst__items li { margin: .3rem 0; }
.lijst__items span { color: var(--muted); }
.lijst__leeg { margin: 0; color: var(--muted); font-style: italic; }
/* The list a bay of the bookcase asked for. The page is short enough that
   everything is on screen already, so being scrolled to is not enough to say
   which one was picked. */
.lijst:target {
  border-left: 2px solid var(--accent);
  padding-left: 1rem; margin-left: -1.15rem;
}
.lijst:target h2 { color: var(--accent); }

/* -- a post, inside the panel -------------------------------------------
   The same writing as content/posts, read in a panel over the room rather
   than on a page of its own. A panel is shorter than a page, so the pictures
   are kept to a part of it — a cover that fills the frame leaves nothing to
   say it is an article at all. */

.artikel { max-width: var(--measure); }
.artikel h1 { font-size: 1.6rem; letter-spacing: -.01em; margin: 0; }
.artikel__datum { margin: .25rem 0 1.1rem; color: var(--muted); font-size: .85rem; }
.install-guide__choice {
  margin: 1.5rem 0; padding: 1rem;
  border: 1px solid var(--muted); border-radius: var(--radius);
}
.install-guide__choice label { display: block; font-weight: 600; margin-bottom: .5rem; }
.install-guide__choice select {
  width: 100%; min-height: 44px; padding: .5rem;
  font: inherit; color: var(--fg); background: var(--bg);
  border: 1px solid var(--muted); border-radius: var(--radius);
}
.install-guide__choice [role="status"] { margin: .65rem 0 0; font-size: .9rem; }
.install-guide [hidden] { display: none; }
.is-artikel .lede { margin: 0 0 1.3rem; }
.is-artikel .lede img {
  width: 100%; max-height: 42vh; object-fit: cover;
  border-radius: var(--radius);
}
.is-artikel .prose img, .is-artikel .prose video { max-height: 60vh; object-fit: contain; }

/* -- the plankjes -------------------------------------------------------
   A rack of sheets, overlapping the way things stacked on edge do. The one
   you are over comes forward and the ones beside it lean away, so the stack
   opens around your hand instead of jumping. */

.plankjes {
  display: flex; align-items: flex-end; justify-content: flex-start;
  margin: 1.5rem 0 .4rem; padding: 2rem 1rem 2.5rem;
  overflow-x: auto; overscroll-behavior-x: contain;
}
.plankje {
  flex: 0 0 auto;
  width: min(13rem, 38vw);
  margin-left: -5.5rem;
  padding: .4rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: inherit; font: inherit; cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
  transform: translateY(calc(var(--lean, 0) * 0.4rem))
             rotate(calc(var(--lean, 0) * 1.1deg));
  transition: transform .28s ease, box-shadow .28s ease;
}
.plankje:first-child { margin-left: 0; }
.plankje img {
  width: 100%; height: 9rem; object-fit: cover;
  border-radius: 3px; background: var(--line);
}
.plankje--out {
  position: relative; z-index: 2;
  transform: translateY(-1.6rem) rotate(0deg) scale(1.04);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .38);
}
.plankjes__naam {
  margin: 0; min-height: 1.5em; text-align: center;
  color: var(--muted); font-size: .9rem;
}

@media (prefers-reduced-motion: reduce) {
  .plankje { transition: none; }
}

/* Graphics comparisons keep the camera and the rest of the room untouched. */
.model__graphics {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  max-width: calc(100% - 24px); padding: .5rem .7rem;
  background: rgba(10, 7, 5, .9); color: #e9d9c0;
  border: 1px solid rgba(233, 217, 192, .3); border-radius: var(--radius);
  font-size: .85rem; line-height: 1.5;
}
.model__graphics summary { cursor: pointer; min-height: 24px; }
.model__graphics[open] { max-height: calc(100% - 24px); overflow-y: auto; overscroll-behavior: contain; }
.model__graphics label { display: flex; align-items: center; gap: .4rem; min-height: 44px; cursor: pointer; }
.model__graphics input { accent-color: #e9d9c0; width: 1rem; height: 1rem; }
.model__graphics button {
  font: inherit; color: inherit; background: transparent; cursor: pointer;
  border: 1px solid rgba(233, 217, 192, .3); border-radius: 3px;
  min-height: 36px; padding: .2rem .65rem;
}
.model__graphics :focus-visible { outline: 2px solid #e9d9c0; outline-offset: 3px; }
.model--app .model__graphics { display: none; }
.model__toolbar { position: absolute; top: 12px; left: 12px; right: 12px; display: flex; align-items: flex-start; gap: 8px; z-index: 7; pointer-events: none; }
.model__toolbar > details { pointer-events: auto; }
.model__toolbar .model__graphics { position: static; max-height: 45vh; max-width: calc(50% - 4px); }
.model__editor { box-sizing: border-box; max-width: min(20rem, calc(50% - 4px)); max-height: 45vh; overflow-y: auto; padding: .5rem .7rem; background: rgba(10,7,5,.94); color: #e9d9c0; border: 1px solid #706352; border-radius: var(--radius); font-size: .85rem; }
.model__editor summary { cursor: pointer; }
.model__editor select { width: 100%; margin: .5rem 0; }
.model__editor label { display: flex; justify-content: space-between; gap: .5rem; margin: .4rem 0; }
.model__editor input { width: 6rem; min-width: 0; }
.model__editor button { margin: .2rem; min-height: 36px; cursor: pointer; }
.model__editor p { font-size: .75rem; }
.model__editor fieldset { margin: .6rem 0 0; padding: .4rem; min-width: 0; border: 1px solid #706352; }
.model__editor fieldset select { min-width: 0; width: 60%; }
.model__editor input[type="color"] { width: 3rem; height: 2rem; }
.model__menu-section { margin: .5rem 0; border-top: 1px solid #706352; padding-top: .35rem; }
.model__menu-section > summary { font-weight: 600; padding: .3rem 0; }
.model__graphics select { min-width: 0; max-width: 65%; margin-left: auto; }
.model__toolbar .model__graphics { box-sizing: border-box; width: max-content; }
.model__toolbar .model__graphics[open] { width: 20rem; }
.model__performance { position: absolute; bottom: 12px; right: 12px; z-index: 8; padding: .6rem; max-width: calc(100% - 24px); box-sizing: border-box; background: rgba(10,7,5,.92); color: #e9d9c0; border: 1px solid #706352; border-radius: var(--radius); white-space: pre-line; font: .72rem/1.5 ui-monospace, monospace; pointer-events: none; }
.model__performance[hidden], .model--app .model__performance { display: none; }
.model__moved { z-index: 6; box-sizing: border-box; width: 38rem; max-width: calc(100% - 28px); max-height: 43%; overflow-y: auto; }
.model__moved textarea { display: block; box-sizing: border-box; width: 100%; min-height: 6rem; margin: .5rem 0; background: #17130f; color: inherit; font: inherit; border: 1px solid #706352; resize: vertical; }
.model__moved .model__paint { position: static; max-width: none; max-height: none; padding: .5rem 0 0; border: 0; }
.model--app .model__toolbar, .model--app .model__moved { display: none; }

/* Every editing overlay has the same exit, without discarding its changes. */
.model__toolbar .model__graphics, .model__editor,
.model__moved .model__paint { position: relative; inset: auto; }
.model__graphics[open] > summary, .model__editor[open] > summary,
.model__moved > strong, .model__paint > strong { padding-right: 2.2rem; min-height: 28px; }
.model__performance { padding-right: 2.8rem; }
[data-model] .model__panel-close {
  position: absolute; top: 4px; right: 4px;
  display: grid; place-items: center;
  width: 30px; height: 30px; min-height: 30px; margin: 0; padding: 4px;
  color: inherit; background: transparent;
  border: 0; border-radius: 3px; cursor: pointer; pointer-events: auto;
}
[data-model] .model__panel-close:hover { background: rgba(233, 217, 192, .15); }
.model__panel-close svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round;
}
details:not([open]) > .model__panel-close { display: none; }

/* Camera experiments are temporary; they never resize the room. */
.model__toolbar { flex-wrap: wrap; }
.model__experience {
  position: relative;
  padding: .5rem .7rem; color: #e9d9c0; background: rgba(10,7,5,.96);
  border: 1px solid #706352; border-radius: var(--radius); font-size: .85rem;
  max-height: min(70vh, 34rem); overflow-y: auto;
}
.model__experience summary { min-height: 24px; cursor: pointer; }
.model__experience[open] { width: min(22rem, calc(100vw - 24px)); }
.model__experience p { margin: .7rem 0; max-width: 28em; }
.model__view-choices { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .8rem; }
.model__experience button, .model__walkpad button {
  min-height: 44px; font: inherit; color: #ffca78; background: #21170e;
  border: 1px solid #9c7546; border-radius: 7px; padding: .4rem .65rem; cursor: pointer;
}
.model__experience button[aria-pressed="true"] { color: #21170e; background: #ffca78; }
.model__experience label { display: flex; align-items: center; gap: .5rem; margin: .8rem 0; }
.model__experience input[type="range"] { min-width: 0; flex: 1; accent-color: #ffca78; }
.model__experience :focus-visible, .model__walkpad :focus-visible { outline: 2px solid #fff0d6; outline-offset: 2px; }
.model__walkpad { position: absolute; z-index: 4; bottom: 18px; left: 18px; display: flex; gap: .35rem; touch-action: none; }
.model__walkpad[hidden] { display: none; }
.model__walkpad button { width: 48px; height: 48px; font-size: 1.35rem; }
.model--app .model__experience { display: none; }
