/* ==========================================================================
   The rooms' themes. PLATFORM.md §4.1, BUILD-PLAN.md §3.1.

   **The island keeps its shape and changes its substance.** Same silhouette,
   same spacing, same type, same controls in the same places -- so it is
   recognisably the one console you have been using all along. Everything about
   what it is *made of* and how it *behaves* belongs to the room.

   An earlier version of this had a theme as four colours. Four colours is a
   tint. A room's personality is the island behaving like the place it is
   standing in, so a theme sets five things:

     palette    the light the room is lit by
     material   what the console is made of, and how deep it is cut
     motion     the curves and the durations -- water does not settle like stone
     reaction   what it does when touched, dragged and morphed
     arrival    how it assembles, and how it lets go; `data-entrance`/`data-exit`

   And it may set nothing else. Not the type, not the spacing, not the component
   shapes, not the layout. That is the rule that stops this becoming eight
   websites, and keeping the list to what is written here is how it is enforced
   rather than merely stated.

   ## Where the values live

   `:root` below *is* the contract, and its values are the grove's, because the
   grove is the reference every other room is measured against -- so the
   converter inherits exactly what it already had and there is nothing to keep
   in step. Every other room is a `[data-room]` block overriding only what it
   actually changes. Reading one of those blocks tells you the whole of what
   makes that room different, which is the point.

   The room palettes are also registered separately, because the dial shows
   every room at once and cannot use `[data-room]` -- that names only the room
   you are standing in.
   ========================================================================== */

/* --------------------------------------------------------------------------
   The palettes, all of them, so the dial can wear each room's own colour.
   -------------------------------------------------------------------------- */
:root {
  /* The grove, from outside, is green.

     Sampled from its own film rather than chosen: the six commonest greens in
     `background.mp4` are #56690A, #65790A, #465909, #4B6408 and #3A5607 --
     leaf olive, hue 71. This is that hue lifted until it reads through glass,
     which is the one thing the sampled value could not do at 2.4 to one.
     #7E9410 is hue 70 and clears three to one over both the grove's own dark
     film and the fog's white, which no brighter or darker green in that
     family does.

     The room still works in rust inside itself -- `--signal`, borrowed from
     the butterfly's wing, which `DESIGN.md` §6 commits to. This is the same
     split the fog has: what a room looks like from outside is not the colour
     it works in. The wing is what you are looking at; the leaves are what it
     is sitting in. */
  --room-convert-accent:    #7E9410;  /* the chrysalis grove */
  --room-photo-accent:      #D8A23C;  /* the prism canyon */
  /* The fog's mark is the fog's own ink, and it is nearly black.

     It was a lamp gold, twice: bright when the room was dark, then turned
     down when the room went light. Both were wrong for the same reason --
     this room's mark is drawn on a pale button, and what this room draws on
     pale is dark type. The gold is still the room's signal inside it, on the
     slider fills and the marked line; it is not what the room looks like from
     outside, which is white with something dark resolving out of it. */
  --room-transcribe-accent: #1B2228;  /* the listening station in fog */
  --room-docs-accent:       #4F6E8C;  /* the chalk archive */
  /* The lake: its ice. The film is one hue -- 98% of its coloured pixels
     sit between 210 and 270 degrees (OKLCh), the water's slate at 230 --
     and this is that hue lit: the glacial cyan of thin ice over the water,
     brought forward until it is a colour (scripts/music-qa/accent/). The
     same inside and out, and nothing like any other room's. */
  --room-music-accent:      #8DD0E6;  /* the still lake */
  --room-video-accent:      #8E6B3A;  /* the long valley: its wood in the light */
  --room-ask-accent:        #4FA88C;  /* the luminous hollow */

  /* Four of these moved on 20 September 2026, and the reason was measurable.
     Seven of the eight sat inside thirty degrees of orange -- convert 14,
     design 20, docs 25, video 27, sound 31, photo 39, transcribe 44, and then
     Ask alone at 161 -- with six of them between 53% and 57% lightness. Each
     was right for its own room and together they were one palette, which is
     the thing §4.1 exists to prevent, arrived at from a direction it did not
     anticipate.

     Each new one comes from its own place rather than from a wheel:

       docs    ink on chalk. The archive is warm bark and its mark is what is
               written in it, which is not another brown.
       sound   a still lake at night, which was amber. A room whose whole
               idea is dark water had the colour of a campfire.
       video   the long valley at dusk, kept warm but taken to the red end so
               it is not the canyon's gold at a slightly different angle.
       design  the unfinished quarry: the least coloured of the eight, because
               it is the room that has not been finished. A wireframe is not
               a hue yet.

     Now: video 12, photo 39, transcribe 44, convert 70, ask 161, sound 190,
     docs 207, design 210 -- and the last three separate on saturation and
     lightness rather than on hue, which is the one place three neighbours are
     honest. */
  --room-design-accent:     #7E8C99;  /* the unfinished quarry */
}

/* Each entry in the column wears the room it goes to -- its colour, its
   surface, its silhouette and the way it reacts to being touched.

   Not only its accent, which was all it had: its surface, its edge and its
   silhouette too, so the grove's button is a dark capsule, the canyon's is
   cut stone and the fog's is pale whichever room you are reading them from.
   The four values are the same ones the room itself uses -- `--film-shell`,
   `--film-rule`, `--film-raise-hi` and the idle island's radius -- restated
   here because a button in the top bar cannot ask a `[data-room]` block that
   is not on the page.

   The surfaces are tinted towards their own rooms -- the canyon and the lake
   cool, the archive warm -- rather than all being one neutral dark, because
   two near-black discs with two warm marks are the same button twice however
   different their shapes are.

   The grove is the exception and it is the exception on purpose: its button
   is a bead of its water rather than a surface, with its leaves growing in
   round it (below). Water does not hold contrast the way a surface does, so
   its mark and its name carry a halo as well -- the same answer the title
   and the eyebrow use where they stand over the film, rather than a new one
   invented here.

   And the dark rooms deepen under the pointer where the light one brightens.
   A highlight is more light, so every one of these lightened -- which on a
   dark button walks the surface up towards the luminance of its own mark and
   took the grove's rust from 3.5 to one down to 2.3. Deepening moves it the
   other way, reads just as clearly as a change of state with the lift under
   it, and is what a warm dark surface does when something comes near it
   anyway.

   How light they can go is not a taste question. A rust mark needs its
   surface darker than about rgb(58) to clear the 3:1 a graphic needs, gold
   needs rgb(90), the lake's amber rgb(84) and the hollow's green rgb(76).
   That is the ceiling on "make it look like the island", and the grove's
   island -- pale glass over a bright film -- is well above it. What the
   button can be is the grove's *colour* at a weight its own mark survives.

   The surface is the room's own island, solid enough to read on any backdrop.
   A button sits in the top bar with whichever room you are standing in behind
   it, so the island's real translucency would make the grove's button
   invisible over the fog and the fog's invisible over the grove. These are
   what each island looks like, made opaque.

   Each carries its own colour on its edge as well as on its mark. Two rooms
   whose surfaces are both near-black and whose marks are both twenty-four
   pixels of warm stroke are, at a glance, the same button twice -- and the
   glance is all this column gets. A ring in the room's own colour is the
   cheapest thing that makes them tell apart at that size, and it is the
   colour the room actually uses rather than a decoration invented for the
   dial.

   The dark rooms' surfaces are nearly opaque here, where in their own rooms
   they are translucent glass over a film. A button is 40px of a top bar and
   what is behind it is whichever room you are standing in -- so at the panel's
   own 0.58 the grove's button over the fog came out mid grey, and its rust
   mark measured 1.28 to one against it. A room's colour is only its colour if
   the surface under it is the room's as well.

   The silhouettes are scaled where the island's own radius would not read at
   forty pixels: the canyon's 16 becomes 11 and the valley's 12 becomes 9, so
   that a corner still says "cut" rather than "round" at this size. The square
   rooms keep their exact numbers, because 2px is 2px at any scale. */
/* The grove's button: a bead of its water with two leaves holding it -- the
   owner's choice of 7 October 2026 (ROOM-IDENTITY.md §4, candidate B). It
   replaces the window into the film that stood here as a stopgap.

   The body is the grove's water, not the island's white glass. The island can
   be white because it always sits over the grove's own dark film; a button in
   the top bar sits over whichever room you are in, and white glass over the
   fog is white on white. So it is water the colour of the shade under the
   leaves: darker above, where it reflects the canopy, and lighter below,
   where it gathers the light it focuses, as a drop of dew on a leaf does. Two
   soft warm points behind the mark are the film's out-of-focus sun. The rim
   is a glass edge, bright on the upper right where the low sun is, the side
   the mark's own gradient is lit from, and dark on the lower left.

   The leaves are drawn in `marks.js` (`frameFor`) and moved below. The mark
   stands on the water with the dark edge it always had. Over the fog the
   body is about rgb(78 84 66), so the mark's lit end still holds; over the
   dark rooms it is near the film's own shade. */
.dial__room[data-room="convert"],
[data-room="convert"] .dial__face {
  --accent: var(--room-convert-accent);
  --dial-radius: 999px;
  --dial-face: rgba(16, 21, 6, 0.72);
  --dial-film:
    radial-gradient(closest-side, transparent 62%, rgba(6, 8, 2, 0.42) 94%, rgba(6, 8, 2, 0.22) 100%),
    radial-gradient(7px 7px at 30% 66%, rgba(218, 214, 197, 0.16), transparent),
    radial-gradient(5px 5px at 64% 74%, rgba(196, 170, 96, 0.20), transparent),
    radial-gradient(54% 30% at 44% 86%, rgba(150, 170, 50, 0.34), transparent 76%),
    linear-gradient(180deg, rgba(10, 13, 4, 0.2), rgba(70, 88, 20, 0.24));
  /* The rim, inside the hairline: light on the sun's side, shade on the
     other. The grove still rises under the pointer, as its controls do. */
  --dial-edge: inset -1px 1px 0 rgba(244, 240, 206, 0.22), inset 1px -1px 1px rgba(4, 6, 2, 0.45);
  --dial-lift: inset -1px 1px 0 rgba(244, 240, 206, 0.30), inset 1px -1px 1px rgba(4, 6, 2, 0.45), var(--lift-1);
  --dial-sheen: rgba(255, 255, 255, 0.17);
  --dial-rule: rgba(214, 216, 203, 0.16);
  --dial-hover: rgba(214, 216, 203, 0.05);
  --dial-ink: rgba(238, 241, 240, 0.92);
  --dial-halo: drop-shadow(0 0 1px rgba(6, 9, 12, 0.95)) drop-shadow(0 0 2px rgba(6, 9, 12, 0.85)) drop-shadow(0 1px 6px rgba(6, 9, 12, 0.7));
  /* The leaves' own pace, restated here because a column button cannot read
     the grove's block while you are standing in another room. */
  --grove-lean: 520ms;
  --grove-ease: cubic-bezier(.22, .72, .22, 1);
}
/* The leaves. Laid over the whole button, its 1px edge included, so they
   are drawn in its outer 40 x 40, and clipped to its round silhouette: they
   grow in from outside the rim. Under the mark and the name, and never in
   the way of a click. */
.grove {
  position: absolute;
  inset: -1px;
  z-index: 0;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
}
/* Each half keeps to its own end, so the pill that opens into the name has
   a leaf at each end of it, and the left one travels with the left edge. */
.grove__side {
  position: absolute;
  top: 0;
  width: 20px;
  height: 40px;
  overflow: visible;
}
.grove__side--l { left: 0; }
.grove__side--r { right: 0; }
.grove .clump,
.grove .dew {
  transition: transform var(--grove-lean) var(--grove-ease);
}
.grove__side--l .clump { transform-origin: 4px 40px; }
.grove__side--r .clump { transform-origin: 36px 40px; }

/* At rest, a little life and nothing more: the right-hand leaf stirs, and
   now and then the sun catches the drop on it. Both are CSS on a few
   elements, nothing runs a frame loop, and while the column is shut its
   button holds still, because it is there only clear. */
.grove__side--r .g1 > .sway { animation: grove-stir 7s ease-in-out infinite alternate; }
.grove .glint { animation: grove-glint 9s ease-in-out infinite; }
@keyframes grove-stir {
  from { transform: rotate(-1.6deg); }
  to   { transform: rotate(1.6deg); }
}
@keyframes grove-glint {
  0%, 78%, 100% { opacity: 0.7; transform: scale(1); }
  86%           { opacity: 1; transform: scale(1.7); }
}
.dial:not([data-open="true"]) .dial__room .grove *,
.dial:not([data-open="true"]) .dial__room[data-room="convert"]::after { animation-play-state: paused; }
/* And every other room's button in the shut column with it -- the canyon's
   turning light, the fog's breath: out of sight, they were still repainted
   every frame for as long as the page was open. */
.dial:not([data-open="true"]) .dial__room,
.dial:not([data-open="true"]) .dial__room *,
.dial:not([data-open="true"]) .dial__room::before,
.dial:not([data-open="true"]) .dial__room::after,
.dial:not([data-open="true"]) .dial__room *::before,
.dial:not([data-open="true"]) .dial__room *::after { animation-play-state: paused; }

/* Under the pointer the leaves close in round the mark, toward the hand, and
   the drop rolls a little way down its leaf. */
:is(.dial__room[data-room="convert"], [data-room="convert"] .dial__face):is(:hover, :focus-visible) .grove__side--l .clump { transform: rotate(5deg) scale(1.05); }
:is(.dial__room[data-room="convert"], [data-room="convert"] .dial__face):is(:hover, :focus-visible) .grove__side--r .clump { transform: rotate(-5deg) scale(1.05); }
:is(.dial__room[data-room="convert"], [data-room="convert"] .dial__face):is(:hover, :focus-visible) .grove .dew { transform: translate(0.5px, 1.6px); }
/* In the column the button opens into its name, and the left leaf bends
   back out of the way to let the name through rather than standing in
   front of it. */
.dial__room[data-room="convert"]:is(:hover, :focus-visible) .grove__side--l .clump { transform: rotate(-7deg) scale(0.94); }

/* These selectors outrank the column's own reduced-motion rules, so they
   say it again: nothing stirs, nothing glints, no light crosses the water
   (the column's own rule for that was outranked by the grove's, so the
   sheen used to keep drifting), and under the pointer the leaves are simply
   closer, at once. */
@media (prefers-reduced-motion: reduce) {
  .grove * { animation: none !important; transition: none !important; }
  .dial__room[data-room="convert"]::after,
  [data-room="convert"] .dial__face::after { animation: none; }
}
.dial__room[data-room="photo"] {
  --dial-radius: 11px;
  --accent: var(--room-photo-accent);
  --dial-edge: 2px 0 0 -1px rgba(216, 162, 60, 0.16), -2px 0 0 -1px rgba(90, 150, 190, 0.14);
  --dial-lift: 4px 0 0 -1px rgba(216, 162, 60, 0.30), -4px 0 0 -1px rgba(90, 150, 190, 0.26);
  --dial-face: rgba(34, 44, 56, 0.94);
  --dial-rule: rgba(237, 241, 244, 0.24);
  --dial-hover: rgba(0, 0, 0, 0.22);
  --dial-ink: rgba(237, 241, 244, 0.92);
}
/* The fog's button is a lens with rime on it.

   The station in the film is a radio mast in a blizzard, crusted white with
   rime, and that is the object: a disc of cool glass with frost gathered on
   its rim, crystals of the mast's own slate caught in the frost, and the
   middle left clear for the mark. It is not the film in a window, which was
   refused (`ROOM-IDENTITY.md`, at the top); nothing in it comes from the
   film's pixels, only from what the film is of.

   Breath, slowly. Every nine seconds a breath condenses in the middle of the
   glass and clears again, the longest cycle in the column, because fog takes
   its time.

   And the room's reaction under the pointer. Fog thickens: the frost grows
   inward from the rim, evenly all the way round, and a white halo gathers
   outside it. Nothing lifts and nothing has a direction. It takes the room's
   own 620ms hold to arrive, whichever room you are reading the column from,
   so the fog is slow in the canyon too.

   The halo is white, and white is no help over the fog's own pale film, so
   the cool slate haze under it stays at rest and on hover: it is what lets a
   pale disc be found on a pale room.

   The two shadow lists match entry for entry, inset first, so the browser
   can interpolate one into the other rather than snapping. */
.dial__room[data-room="transcribe"],
[data-room="transcribe"] .dial__face {
  --dial-radius: 999px;
  --accent: var(--room-transcribe-accent);
  --dial-face: #D3DCE2;
  --dial-film: radial-gradient(closest-side,
    rgba(255, 255, 255, 0) 36%,
    rgba(255, 255, 255, 0.32) 58%,
    rgba(255, 255, 255, 0.82) 82%,
    #FFFFFF 97%,
    rgba(255, 255, 255, 0.7) 100%);
  --dial-rule: rgba(52, 68, 80, 0.28);
  --dial-hover: rgba(255, 255, 255, 0);
  --dial-edge:
    inset 0 0 0 0 rgba(255, 255, 255, 0),
    0 0 12px 1px rgba(52, 68, 80, 0.14),
    0 0 0 0 rgba(255, 255, 255, 0);
  --dial-lift:
    inset 0 0 12px 7px rgba(255, 255, 255, 0.9),
    0 0 18px 3px rgba(52, 68, 80, 0.16),
    0 0 8px 2px rgba(255, 255, 255, 0.7);
  --dial-ink: #1B2228;
  /* The room's own hold and curve, restated: a column button cannot read
     the fog's block when you are standing somewhere else. */
  --fog-thicken: 620ms;
  --fog-ease: cubic-bezier(.24, .62, .16, 1);
}
/* The base rules transition the shadow at the current room's state speed;
   only that entry changes here, and the rest are restated as they were. */
.dial__room[data-room="transcribe"] {
  transition: opacity var(--dur-face) var(--ease),
              transform var(--dur-face) var(--ease-soft),
              background var(--dur-state) var(--ease),
              box-shadow var(--fog-thicken) var(--fog-ease);
}
[data-room="transcribe"] .dial__face {
  transition: background var(--dur-state) var(--ease),
              opacity var(--dur-face) var(--ease),
              box-shadow var(--fog-thicken) var(--fog-ease);
}
/* The rime. Fractal noise turned into flecks of slate, kept to the frosted
   band by a mask so the middle stays clear glass. The tile is drawn at twice
   its size, so on a 2x screen a crystal is one device pixel and on a 1x
   screen they average into a soft frost rather than a grit. A data image is
   a document of its own, so its filter id cannot collide with anything on
   the page. */
.dial__room[data-room="transcribe"]::before,
[data-room="transcribe"] .dial__face::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80'%3E%3Cfilter id='r'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.7' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.45 0 0 0 0 0.53 0 0 0 0 0.59 0 0 0 2 -0.95'/%3E%3C/filter%3E%3Crect width='80' height='80' filter='url(%23r)'/%3E%3C/svg%3E");
  background-size: 40px 40px;
  -webkit-mask-image: radial-gradient(closest-side, transparent 48%, rgba(0, 0, 0, 0.85) 76%, #000 92%, transparent 100%);
  mask-image: radial-gradient(closest-side, transparent 48%, rgba(0, 0, 0, 0.85) 76%, #000 92%, transparent 100%);
  opacity: 0.75;
}
/* The focus ring hugs the glass. At the column's 3px offset a dark ring on
   a dark room stood on nothing and could not be seen; touching the pale
   rim, it reads against the button itself, over any room. */
.dial__room[data-room="transcribe"]:focus-visible,
[data-room="transcribe"] .dial__face:focus-visible { outline-offset: 0; }
/* The breath. */
.dial__room[data-room="transcribe"]::after,
[data-room="transcribe"] .dial__face::after {
  background: radial-gradient(closest-side,
    rgba(255, 255, 255, 0.55),
    rgba(255, 255, 255, 0.25) 70%,
    rgba(255, 255, 255, 0) 100%);
  animation: fog-breath 9s ease-in-out infinite;
}
@keyframes fog-breath {
  0%, 100% { opacity: 0; }
  45%      { opacity: 1; }
}
/* A hair of white round the stroke, so the arcs sit in the frost rather
   than on the crystals behind them. */
.dial__room[data-room="transcribe"] > svg,
[data-room="transcribe"] .dial__face > svg {
  filter: drop-shadow(0 0 1px rgba(255, 255, 255, 0.9));
}
/* These selectors outrank the column's own reduced-motion rules, so they
   say it again. No breath: it rests half drawn. And no growth: under the
   pointer the frost is simply thicker, at once, which is still a change of
   state you can see and nothing moving to get there. */
@media (prefers-reduced-motion: reduce) {
  .dial__room[data-room="transcribe"]::after,
  [data-room="transcribe"] .dial__face::after { animation: none; opacity: 0.5; }
  .dial__room[data-room="transcribe"],
  [data-room="transcribe"] .dial__face { transition: none; }
}
/* Three sheets of the archive's old paper, the lowest one torn -- the owner's
   choice of 5 October 2026 (ROOM-IDENTITY.md §3, candidate A). The top sheet
   is the button itself: warm parchment, its grain, its edges gone a shade
   darker with age and one foxed spot. The two under it are drawn on the
   button's two pseudo-elements, laid just outside its box -- a strip below
   and a strip beside -- because a box can only cast straight shadows and the
   lowest sheet's edge is torn. (Not a border image: Chrome draws a faint seam
   at each of a border image's slices whenever the button is between whole
   pixels, which the dial's drift always is.) The strip below is a pattern
   drawn at its own size and the grain is a tile, so when the button opens
   into its name the paper is the same paper, not a picture of it pulled
   wide. The ink mark
   measures 5.6 to one on the parchment. Light, so it brightens under the
   pointer like the fog's rather than deepening. The same paper is the face
   of the dial while you stand in the archive, below. */
.dial__room[data-room="docs"],
[data-room="docs"] .dial__face {
  --dial-radius: 0;
  --accent: var(--room-docs-accent);
  --dial-edge: none;
  --dial-lift: 0 3px 8px -4px rgba(70, 46, 18, 0.45);
  --dial-face: #EFE3C4;
  --dial-film:
    radial-gradient(120% 120% at 50% 42%, rgb(138 90 32 / 0) 52%, rgb(138 90 32 / 0.20) 100%),
    radial-gradient(9px 9px at 78% 76%, rgb(154 100 40 / 0.16), rgb(154 100 40 / 0)),
    url("/media/docs-grain.svg"),
    linear-gradient(160deg, #F5EBD0 0%, #ECDDB9 60%, #E5D2A8 100%);
  --dial-hover: rgba(255, 251, 238, 0.32);
  --dial-ink: #221E19;
  --dial-rule: rgba(86, 58, 26, 0.46);
  background-size: auto;
  background-position: 0 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  /* The sheets under it lie outside the box, so the box must not clip them.
     Nothing else here needs clipping: the room has no weather in its button,
     and the name clips itself. */
  overflow: visible;
}
.dial__room[data-room="docs"]::before,
[data-room="docs"] .dial__face::before,
.dial__room[data-room="docs"]::after,
[data-room="docs"] .dial__face::after {
  content: "";
  position: absolute;
  inset: auto;
  border-radius: 0;
  opacity: 1;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
/* Positioned from the padding box, inside the 1px edge: the strip below runs
   from 2px in to the right edge, 4px tall; the one beside from 2px down to
   4px below the bottom, and carries the corner. */
.dial__room[data-room="docs"]::before,
[data-room="docs"] .dial__face::before {
  --sheet-edge: rgba(86, 58, 26, 0.48);
  left: 1px; right: -1px; top: calc(100% + 1px); height: 4px;
  box-sizing: border-box;
  padding-left: 2px;
  /* The two sheets' left ends; the second sheet and its edge; the lowest
     sheet's torn edge, a tile laid end to end from 2px further in. */
  background-image:
    linear-gradient(var(--sheet-edge), var(--sheet-edge)),
    linear-gradient(var(--sheet-edge), var(--sheet-edge)),
    linear-gradient(#E3CFA3 0 1.2px, var(--sheet-edge) 1.2px 2px, transparent 2px),
    url("/media/docs-sheet-bottom.svg");
  background-size: 0.8px 2px, 0.8px 1.4px, 100% 100%, 32px 4px;
  background-position: 0 0, 2px 1.6px, 0 0, 2px 0;
  background-repeat: no-repeat, no-repeat, no-repeat, repeat-x;
  background-clip: border-box, border-box, border-box, content-box;
}
.dial__room[data-room="docs"]::after,
[data-room="docs"] .dial__face::after {
  left: calc(100% + 1px); top: 1px; width: 4px; height: 42px;
  background-image: url("/media/docs-sheet-right.svg");
}
/* The lake's button is a pool of its still water.

   The film is a mountain lake at dusk, mirror-still under mist, dark pines
   on the shore. The button is that water seen from the bank: a round disc of
   pine-dark water, a pale band of mist lying on the horizon across its
   middle, the sky's grey above it and the reflection, fainter, below.
   Nothing in it is the film's pixels -- a window was refused
   (ROOM-IDENTITY.md) -- only what the film is of.

   And the room's reaction under the pointer: a ripple. One ring leaves the
   middle and spreads to the rim over the room's long, soft 920 ms, the way
   still water answers a single drop. */
.dial__room[data-room="music"],
[data-room="music"] .dial__face {
  --dial-radius: 999px;
  --accent: var(--room-music-accent);
  --dial-edge: 0 0 0 0 rgba(141, 208, 230, 0);
  --dial-lift: 0 0 0 3px rgba(141, 208, 230, 0.16);
  --dial-face: #0E1518;
  --dial-film:
    linear-gradient(180deg,
      rgba(199, 205, 209, 0.20) 0%,
      rgba(176, 187, 192, 0.10) 34%,
      rgba(232, 238, 240, 0.42) 49%,
      rgba(152, 164, 168, 0.16) 53%,
      rgba(72, 92, 101, 0.10) 70%,
      rgba(12, 17, 19, 0) 100%);
  --dial-rule: rgba(141, 208, 230, 0.42);
  --dial-hover: rgba(232, 238, 240, 0.03);
  --dial-ink: rgba(232, 238, 240, 0.94);
  --lake-ripple: 920ms;
  --lake-ease: cubic-bezier(.18, .92, .30, 1);
}
.dial__room[data-room="music"]::after,
[data-room="music"] .dial__face::after {
  background: radial-gradient(closest-side, rgba(232, 238, 240, 0) 62%, rgba(232, 238, 240, 0.36) 74%, rgba(141, 208, 230, 0.22) 80%, rgba(232, 238, 240, 0) 88%);
  transform: scale(0.15);
  opacity: 0;
}
.dial__room[data-room="music"]:hover::after,
.dial__room[data-room="music"]:focus-visible::after,
[data-room="music"] .dial__face:hover::after {
  animation: lake-ripple var(--lake-ripple) var(--lake-ease) 1 both;
}
@keyframes lake-ripple {
  0%   { transform: scale(0.15); opacity: 0; }
  18%  { opacity: 1; }
  100% { transform: scale(1.25); opacity: 0; }
}
/* The mark stands in the water: a breath of dark under it so the mist band
   behind cannot wash it out. */
.dial__room[data-room="music"] > svg,
[data-room="music"] .dial__face > svg {
  filter: drop-shadow(0 0 1.5px rgba(6, 10, 12, 0.85));
}
@media (prefers-reduced-motion: reduce) {
  .dial__room[data-room="music"]:hover::after,
  .dial__room[data-room="music"]:focus-visible::after,
  [data-room="music"] .dial__face:hover::after { animation: none; transform: scale(1); opacity: 0.6; }
}
/* A carriage window, made of the carriage (ROOM-IDENTITY.md §2; the owner
   chose this on 5 October 2026, recoloured from the film).

   The island's silhouette: corners knocked off rather than rounded, so the
   radius is nought and the cut is a clip-path (styles.css, with the rim and
   the hover streak). The plate is the corridor's dark wood, the rim is
   brass where the side light catches it, and one low band of the sun
   through the curtains falls across it. Every colour here was sampled from
   the film. A clip-path cuts off an outer shadow, so the edge and the lift
   are drawn inside. */
.dial__room[data-room="video"] {
  --dial-radius: 0;
  --accent: var(--room-video-accent);
  --dial-edge: none;
  --dial-lift: none;
  --dial-face: #17120D;
  --dial-film:
    linear-gradient(172deg, transparent 0 55%, rgba(178, 160, 119, 0.06) 60%, rgba(237, 225, 197, 0.20) 67%, rgba(178, 160, 119, 0.07) 76%, transparent 84%),
    linear-gradient(165deg, #33281A 0%, #1F1810 48%, #0E0C09 100%);
  --dial-rule: transparent;
  --dial-hover: rgba(255, 240, 210, 0.05);
  --dial-ink: rgba(241, 238, 233, 0.94);
}
.dial__room[data-room="ask"] {
  --dial-radius: 999px;
  --accent: var(--room-ask-accent);
  --dial-edge: 0 0 12px -2px rgba(79, 168, 140, 0.20);
  --dial-lift: 0 0 20px -2px rgba(79, 168, 140, 0.34);
  --dial-face: rgba(18, 34, 34, 0.94);
  --dial-rule: rgba(238, 241, 240, 0.22);
  --dial-hover: rgba(0, 0, 0, 0.22);
  --dial-ink: rgba(238, 241, 240, 0.92);
}
.dial__room[data-room="design"] {
  --dial-radius: 2px;
  --accent: var(--room-design-accent);
  --dial-edge: 0 0 0 1px rgba(238, 241, 240, 0.08);
  --dial-lift: 0 0 0 1px rgba(224, 113, 58, 0.55);
  --dial-face: rgba(34, 35, 37, 0.94);
  --dial-rule: rgba(126, 140, 153, 0.55);
  --dial-hover: rgba(0, 0, 0, 0.24);
  --dial-ink: rgba(238, 241, 240, 0.92);
}
/* Standing in the valley, the face is the same window: the same wood, brass
   and light as the column's button, because the film behind is the
   corridor it was sampled from. */
[data-room="video"] .dial__face {
  --dial-radius: 0;
  --dial-edge: none;
  --dial-lift: none;
  --dial-face: #17120D;
  --dial-film:
    linear-gradient(172deg, transparent 0 55%, rgba(178, 160, 119, 0.06) 60%, rgba(237, 225, 197, 0.20) 67%, rgba(178, 160, 119, 0.07) 76%, transparent 84%),
    linear-gradient(165deg, #33281A 0%, #1F1810 48%, #0E0C09 100%);
  --dial-rule: transparent;
  --dial-hover: rgba(255, 240, 210, 0.05);
}

/* The face wears wherever you are -- and what a room looks like from outside
   is not the same as the colour it works in.

   This said `--signal`, which is the colour a room uses *on itself*: the
   pressed pill, the filled part of a slider, the mark beside the line being
   spoken. In seven rooms the two happen to be the same value and nobody could
   have told. In the fog they are not: it works in a deep lamp gold, and from
   outside it is white with something dark resolving out of it. So the face
   takes the room's accent, which every room now sets, and a room whose two
   colours differ gets the right one in each place. */

/* ==========================================================================
   THE CONTRACT -- and the chrysalis grove, which is its reference

   Suspended forms mid-change, warm amber and bark. Nothing in this block is
   new: these are the values styles.css was already built on, gathered here so
   that what a room is allowed to change can be read in one place. A room that
   sets something not on this list is doing something the system forbids.
   ========================================================================== */

:root {
  /* --- palette ----------------------------------------------------------- */
  --signal:            #E2542A;
  --signal-fill:       #C6431D;
  --signal-fill-hover: #B23A17;
  --signal-ink:        #FFFFFF;
  /* The shade the whole room is lit against: the scrims over the film, the
     shadows, the drag veil and the halos behind the type all derive from it. */
  --shade-rgb: 8 11 14;
  --film-ink:   #EEF1F0;
  --film-ink-2: rgba(238, 241, 240, 0.76);
  --film-ink-3: rgba(238, 241, 240, 0.54);
  --film-rule:  rgba(238, 241, 240, 0.18);

  /* --- identity ----------------------------------------------------------

     The face of the room: the title's typeface and the silhouette of the
     island you arrive at. This group is an amendment to PLATFORM.md §4.1,
     which said a room may not change the type at all. It may change *this*
     much of it: the display face on the headline, and the shape the resting
     island takes. The workhorse face every label and control is set in, the
     mono, the scale and the spacing do not move -- which is what keeps two
     rooms the same product rather than two products.

     The reason for the amendment: the room you arrive in is the whole of the
     first impression, and eight rooms whose opening screen differs only in hue
     are eight tints again. A title in a different voice and an island with a
     different silhouette are the two cheapest ways to say "somewhere else"
     before anything has been read.

     One practical limit: a weight only exists if it was loaded. This site
     serves three faces and `fonts.css` says what cuts they have -- Outfit at
     800 only, JetBrains Mono at 400 only, Hanken Grotesk variable from 400 to
     600. Asking for a weight outside those does not fail, it falls back to the
     one that is there, which is worse than failing because it looks like it
     worked. A room wanting a face this site does not serve needs a font file
     added, and that is a licensing decision before it is a design one.

     The grove's values are the ones styles.css was built with. */
  --font-title: var(--font-display);
  --title-weight: 800;
  --title-case: uppercase;
  --title-track: var(--track-display);
  /* The island at rest: its shape, its height, and the knob on the end of it. */
  --radius-shell-idle: 999px;
  /* What the dial's face is shaped like and marked in here. A room overrides
     both, and the column's buttons each override them again with their own. */
  --dial-radius: 999px;
  --accent: var(--room-convert-accent);
  --rest-height: 58px;
  /* The line round the island at rest. The same hairline that divides a list,
     because in most rooms an island is an object sitting on the film and an
     object has an edge. A room whose idea is that things have no edges sets
     this to `transparent` and says so with light instead. */
  --shell-edge: var(--film-rule);
  --rest-pad: clamp(var(--s4), 3vw, var(--s5));
  --knob-radius: var(--radius-control);
  --knob-size: 48px;
  --rest-bar-radius: var(--radius-control);
  /* The line that types itself in it. A mono face sets the same sentence far
     wider than a grotesque does, so the size belongs to the room as much as the
     face does -- the canyon's ran off the end of its own island at the grove's
     size. */
  --rest-type-font: var(--font-ui);
  --rest-type-size: clamp(var(--t-base), 1.6vw, var(--t-md));
  --rest-type-case: none;
  --rest-type-track: 0;

  /* The account's faces: signing in, creating an account, the confirmation,
     the plans, the account itself and its settings, and the plan sheet that
     is dressed as the plans face is (styles.css, "The account's faces wear
     the room"). The checkout and the contract pages take the shapes and the
     heading voice too, though not the palette: they are paper pages, told
     which room sent them (js/page-room.js).

     A second amendment to "not the component shapes", and a narrow one: the
     owner's decision of 6 October 2026. These faces are the building's own
     furniture (account.js), the same markup in every room, and they used to
     wear the grove's shapes everywhere -- capsule buttons, capsule plan
     cards, Outfit on the heading -- with only the colours following the
     room. In the canyon or the valley that read as the converter's form
     dropped into somebody else's room. So these faces, and only these, take
     the room's heading voice and its shapes. Every other component keeps the
     building's, and the sizes do not move: a plan card is the same box in
     every room, which is what keeps the price centred in it.

       title       the heading's face, weight, case, tracking and size
       radius      buttons: the primary, the quiet one, back, the cog, a pill
       card        the plan cards
       box         the tick (the checkout's two boxes)
       field       a text field: the room's bar, unless the room cuts it
       cut         a chamfer instead of a radius, for a room whose shapes are
                   cut rather than rounded: the clip, and how deep it bites
       ring        the focus ring drawn inside a cut, which a clip would
                   otherwise take away with the corner; 0 where nothing is cut
       line/fill   the edge and the face of the quiet controls at rest, and
       glow        what they wear at rest and under the pointer

     The grove's values are the ones styles.css was built with, so the
     converter is unchanged. */
  --face-title-font: var(--font-display);
  --face-title-weight: 800;
  --face-title-case: none;
  --face-title-track: -0.028em;
  --face-title-size: var(--t-xl);
  /* How far the heading's first line drops to sit level with the 44px way
     back beside it. Nothing at the grove's size, where it never moved; a
     heading set a step smaller centres its first line on the button, and a
     second line, if it wraps, runs on below. */
  --face-title-drop: 0px;
  /* And how it breaks: as written, at the grove's size; balanced where a
     mono or capital heading runs long, so it never leaves one word below. */
  --face-title-wrap: wrap;
  --face-radius: var(--radius-control);
  --face-card-radius: var(--radius-control);
  --face-box-radius: 6px;
  --face-field-radius: var(--rest-bar-radius);
  --face-clip: none;
  --face-card-clip: none;
  --face-cut: 0px;
  --face-card-cut: 0px;
  --face-ring: 0px;
  --face-line: var(--film-rule);
  --face-line-hover: var(--film-ink-3);
  --face-fill: transparent;
  --face-fill-hover: var(--film-raise);
  /* A shadow that draws nothing rather than `none`, because the focus ring
     inside a cut is listed in front of it, and `none` cannot be in a list. */
  --face-glow: 0 0 0 0 transparent;
  --face-glow-hover: var(--react-lift);
  /* The chip in the top bar that opens these faces (Sign in, Account) wears
     their shapes too -- the owner, 6 October 2026. Its face is the console's
     raised surface, where the quiet controls are bare. */
  --face-chip-fill: var(--film-raise);
  --face-chip-fill-hover: var(--film-raise-hi);

  /* --- material ----------------------------------------------------------
     How much the console blurs what is behind it, how its edge catches light,
     whether it has a sheen and how warm, and how deep a well is cut into it. */
  --blur-shell: 26px;
  --film-shell: rgba(18, 22, 26, 0.58);
  --film-raise:    rgba(255, 255, 255, 0.10);
  --film-raise-hi: rgba(255, 255, 255, 0.17);
  --film-well: rgba(8, 11, 14, 0.34);
  --sheen-light:     rgba(255, 255, 255, 0.17);
  --sheen-light-mid: rgba(255, 255, 255, 0.055);
  --refract-width: 1.5px;
  --refract-edge:  1.25px;
  /* How hard the corners are. Stone is not paper is not water. */
  --radius-shell-open: 30px;

  /* --- motion ------------------------------------------------------------
     Deliberately unhurried here: the morph is the moment people watch. A room
     where things settle on water does not use the same curve as one where
     stone is being stacked. */
  --ease:      cubic-bezier(.22, .72, .22, 1);
  --ease-soft: cubic-bezier(.32, .78, .28, 1);
  --dur-state: 180ms;
  --dur-face:  420ms;
  --dur-morph: 780ms;
  --dur-head:  700ms;

  /* --- reaction ----------------------------------------------------------
     What the island does when it is touched, dragged and morphed. This is the
     one that carries the most personality and the one a tint cannot reach.

     The grove's answer to all of it is restraint: surfaces stay flat until you
     touch them, and nothing is decorated at rest. A room that wants more says
     so; it does not get it by default. */

  /* What a panel's edge does at rest, and while something on it is moving.
     Nothing, in the grove -- the border is the border. */
  --react-edge: none;
  --react-edge-moving: none;
  /* How far a control sinks under a press, and how far it rises under the
     pointer. DESIGN.md 1.4 bans hover scaling; the rise is a documented,
     deliberate exception and it is kept small. */
  --react-press: 0.995;
  --react-rise: 1.022;
  --react-rise-y: -2px;
  /* What a control returns on after being let go. */
  --react-settle: var(--ease-soft);
  /* The elevation a control takes under the pointer. */
  --react-lift: var(--lift-1);
  /* What a bar does when the pointer is over it. */
  --react-bar-hover: rgba(255, 255, 255, 0.035);
  /* A slider being moved: what happens to its own track, and to its thumb.
     `none` in the grove -- the value changing is the feedback. */
  --react-drag-track: none;
  --react-drag-thumb: none;
  /* How long a room holds a reaction after the input stops. */
  --react-hold: 420ms;
}

/* Selected text, in the room's own colour rather than the browser's blue.

   The room's signal fill with the ink the room already sets on it -- a pair
   every room measured when it chose them, for its primary buttons -- so a
   selection is as readable as the button is: grove 4.97 to one (white on
   rust), canyon 5.6 (dark on gold), fog 5.43 (dark on lamp gold), archive
   7.18 (white on ink), valley 8.62 (dark on brass). One rule for every
   room; each room changes only the two tokens it already has. The paper
   pages take the grove's, or the room that sent somebody to the checkout
   or the contract (js/page-room.js). The places that draw their own selection -- the PDF
   reader's text layer, the sheet's cells -- keep theirs. */
::selection {
  background: var(--signal-fill);
  color: var(--signal-ink);
}

/* ==========================================================================
   The prism canyon -- the photo room

   Low sun through crystal walls, light splitting across stone. Warm gold at
   the centre going cool at the edges: the accent is the gold, and the shade is
   a colder blue-grey than the grove's so the edges read as the cool end of
   that split.

   PLATFORM.md §4 says what the island does here, and it is two things:
   **panel borders carry a faint chromatic split**, and **dragging a slider
   shifts the hue of its own edge.** Both are below. Everything else is the
   console you have been using all along, which is the whole idea.
   ========================================================================== */

[data-room="photo"] {
  /* The dial's face is the room you are standing in, so it wears that room's
     silhouette rather than a capsule. Declared on the room and not on the
     button because the face has no `data-room` of its own -- it inherits this
     from the page. The column's buttons each set their own, which overrides
     it, so every entry still wears the room it goes to. */
  --dial-radius: 11px;
  --accent: var(--room-photo-accent);
  /* --- palette --- */
  --signal:            #D8A23C;
  --signal-fill:       #B8832A;
  --signal-fill-hover: #A67423;
  --signal-ink:        #17120A;
  --shade-rgb: 10 14 20;
  /* Ink with a trace of the cool edge in it, so type on the panel belongs to
     the same light as the panel does. */
  --film-ink:   #EDF1F4;
  --film-ink-2: rgba(237, 241, 244, 0.78);
  --film-ink-3: rgba(237, 241, 244, 0.56);
  --film-rule:  rgba(237, 241, 244, 0.20);

  /* --- identity ---
     An instrument left on a rock rather than a thing grown in a wood. The
     title is set in the mono -- the third of this site's three faces, so no
     new typeface and no new licence -- wide and light, which reads as measured
     and precise where the grove's reads as warm and heavy. The island squares
     off to match: the grove's is a pebble, the canyon's is cut stone.

     Nothing else about it moves. Same height, same knob on the same end, same
     invitation typing itself in it, same morph when you drop something on it. */
  --font-title: var(--font-mono);
  --title-weight: 400;
  --title-track: 0.02em;
  --radius-shell-idle: 16px;
  --knob-radius: 10px;
  --rest-bar-radius: 12px;
  --rest-type-font: var(--font-mono);
  --rest-type-size: clamp(var(--t-sm), 1.15vw, var(--t-base));
  --rest-type-case: lowercase;
  --rest-type-track: 0.02em;
  /* The account's faces: headed in the mono, in capitals, as the title is --
     a step down in size, because a mono line runs wide and a heading that
     wraps to three lines at a phone's width is no longer a heading -- and
     cut to the island's stone: the knob's radius on a button, the bar's on a
     plan card. */
  --face-title-font: var(--font-mono);
  --face-title-weight: 400;
  --face-title-case: uppercase;
  --face-title-track: 0.02em;
  --face-title-size: var(--t-lg);
  --face-title-drop: calc((44px - var(--t-lg) * 1.15) / 2);
  --face-title-wrap: balance;
  --face-radius: 10px;
  --face-card-radius: 12px;
  --face-box-radius: 4px;

  /* --- material ---
     Crystal, not bark: it holds its edge harder and blurs a little less, and a
     well cut into it is cut deeper because this is stone. */
  --blur-shell: 22px;
  --film-shell: rgba(16, 21, 28, 0.60);
  --film-well: rgba(6, 9, 14, 0.42);
  /* The shell as one opaque surface, for what opens above it (a drop-down's
     list, js/ui/dropdown.css): glass over glass would read nothing. */
  --film-shell-solid: #121821;
  --sheen-light:     rgba(255, 246, 228, 0.20);
  --sheen-light-mid: rgba(214, 231, 255, 0.06);
  --refract-width: 2px;
  --refract-edge:  1.6px;
  --radius-shell-open: 22px;

  /* --- motion ---
     Light through stone arrives without deliberation. A shade quicker and a
     shade sharper than the grove, and it reads as a harder material rather
     than as a faster interface. */
  --ease:      cubic-bezier(.20, .80, .22, 1);
  --ease-soft: cubic-bezier(.26, .84, .26, 1);
  --dur-state: 150ms;
  --dur-face:  380ms;

  /* --- reaction ---
     The chromatic split. Two shadows a couple of pixels either side of the
     edge, warm on one side and cool on the other, at an opacity that should
     read as the edge being *made of* split light rather than as a coloured
     outline. If it is noticeable as an effect it is too strong. */
  --react-edge:
    2px 0 0 -1px rgba(216, 162, 60, 0.16),
    -2px 0 0 -1px rgba(90, 150, 190, 0.14);
  /* While something on the panel is moving, the split opens. */
  --react-edge-moving:
    4px 0 0 -1px rgba(216, 162, 60, 0.30),
    -4px 0 0 -1px rgba(90, 150, 190, 0.26);
  --react-bar-hover: rgba(226, 238, 255, 0.045);
  /* A slider being dragged shifts the hue of its own edge -- the track picks
     up the warm end of the split, and the thumb warms with it. */
  --react-drag-track: drop-shadow(2px 0 1px rgba(216, 162, 60, 0.55))
                      drop-shadow(-2px 0 1px rgba(90, 150, 190, 0.45));
  --react-drag-thumb: 0 0 0 3px rgba(216, 162, 60, 0.22);
  --react-hold: 360ms;
}

/* ==========================================================================
   The listening station in fog -- the transcribe room

   Shapes coming out of the white as they near. Everything here is arriving:
   a recording becomes words, and the words are uncertain until they are not.
   That is the whole character, and it is the opposite of the canyon's, where
   light is split into hard bright edges by something with a shape.

   This block was a sketch for a long time -- a colour, a round island and a
   lowercase title, filed under the rooms that were not built -- and it stayed
   a sketch after the room was finished. What that looks like from the outside
   is a room with no personality at all, because the sketch covers only the
   parts a tint can reach. Everything that happens while you *work* a room --
   the reaction group below -- was the grove's, so the fog reacted like a wood.
   ========================================================================== */

[data-room="transcribe"] {
  --dial-radius: 999px;
  /* And its mark, for the same reason: the face has no `data-room` of its
     own, so without this it fell back to `--signal` -- the colour this room
     uses *inside* itself, on slider fills and the marked line -- which is not
     what the room looks like from outside. */
  --accent: var(--room-transcribe-accent);
  /* --- palette ---
     **The one light room in the building**, and this is the difference that
     was missing.

     Everything before this was a tint: a different accent, a rounder island,
     a longer easing curve, all of it true and none of it visible once you are
     working. The reason is in the contract -- a room may not change the type,
     the spacing, the component shapes or the layout -- so every room's working
     face is the same panel with the same controls in the same places, and the
     only things left to tell them apart were a hue and a blur radius. Reported
     three times as "it still looks like the converter", which it did.

     What the contract does allow is the palette and the material, and nobody
     had taken that as far as it goes. A room is not obliged to be dark. This
     one is fog in daylight: you are not looking at a lit console in a dark
     wood, you are inside the white, and the panel is a pale surface with dark
     type on it. Nothing structural moved and the rule is not bent.

     It is possible at all because `styles.css` has exactly two hardcoded
     whites in it and one of them is a token. Everything else on a surface --
     the raised rows, the wells, the rules, the scrims, the halos, the knob --
     reads a custom property, so inverting the room is a matter of giving them
     different values rather than writing a second stylesheet. That is the
     theme system paying for itself. */

  /* Deep enough to read on the pale surface. The dial keeps the bright lamp
     gold -- `--room-transcribe-accent` is its own token -- but gold as *text*
     on white is about two to one, and this has to carry slider fills, pressed
     pills and the mark beside the line being spoken. */
  --signal: #8A6A12; --signal-fill: #A8871F; --signal-fill-hover: #8F721A;
  /* Dark on the gold, not white. White on this fill measures 3.41 to one,
     which is under the line for a button label -- and a lamp with dark type
     on it is the more honest object anyway. */
  --signal-ink: #16130A;

  /* The shade the room is lit against, and in a light room the shade is
     light. Every scrim over the film, every halo behind the type and the drag
     veil are all derived from this one value -- so making it pale turns the
     wash over the backdrop from a darkening into a fog. One line does the
     whole world behind the panel. */
  --shade-rgb: 226 232 236;

  /* Ink that has been through some air on the way: nothing here is at full
     contrast, because nothing here has arrived completely. */
  --film-ink:   #1B2228;
  --film-ink-2: rgba(27, 34, 40, 0.74);
  /* Measured, not eyeballed. At 0.52 the labels and the timestamps came out
     at 3.29 to one against the pale panel -- under the 4.5 this size of text
     needs -- which is the trap in inverting a room: the alphas that read as
     "quiet" over a dark surface read as "faint" over a light one. */
  --film-ink-3: rgba(27, 34, 40, 0.64);
  --film-rule:  rgba(27, 34, 40, 0.16);

  /* Confirmation and failure, dark enough to read on a pale panel. The
     building's own are chosen for white type on a dark console and both sit
     near two to one the other way up. */
  --film-ok:  #1C6F49;
  --film-bad: #A63325;

  /* --- identity ---
     A voice with its edges off: the workhorse face rather than the display
     one, lighter, lower case, and an island rounder and a little taller than
     the grove's. */
  --font-title: var(--font-ui);
  --title-weight: 600;
  --title-case: lowercase;
  --title-track: 0.01em;
  --radius-shell-idle: 999px;
  --rest-height: 62px;
  --rest-type-case: lowercase;
  /* No line round it at all.

     This is the difference that was missing, and it was the only one that
     mattered: the island is the one thing on the opening screen you look at
     and put your hand on, and the fog's was the grove's exactly -- same
     capsule, same hairline, same knob -- so the room read as the converter
     with another photograph behind it and a different title face. A film and
     a font are not a personality.

     A station in fog has no outline. It has a place where there is more of
     the fog, so the island is drawn with light rather than with a line: no
     border, and a bloom around it instead, which the reaction group below
     opens further while you work. The knob grows to match, because a thing
     with no edge needs to be a little larger to be found. */
  --shell-edge: transparent;
  --knob-size: 52px;
  /* The account's faces: headed in the room's own voice, lower case and soft,
     and their quiet controls made of the same frost as the island. No line
     round a plan card or a quiet button, as there is none round the island:
     a pane of paler fog with condensation inside its edge, which gathers
     under the pointer the way the island's does while it is worked. Still
     capsules, because fog has no corners. The chosen card is the room's dark
     knob, as it was. */
  --face-title-font: var(--font-ui);
  --face-title-weight: 600;
  --face-title-case: lowercase;
  --face-title-track: 0.01em;
  --face-line: transparent;
  --face-line-hover: transparent;
  --face-fill: rgba(255, 255, 255, 0.30);
  --face-fill-hover: rgba(255, 255, 255, 0.46);
  --face-glow: inset 0 0 0 1px rgba(255, 255, 255, 0.62),
               inset 0 0 12px 1px rgba(255, 255, 255, 0.34),
               0 0 18px 2px rgba(52, 68, 80, 0.07);
  --face-glow-hover: inset 0 0 0 1px rgba(255, 255, 255, 0.9),
                     inset 0 0 22px 4px rgba(255, 255, 255, 0.5),
                     0 0 28px 5px rgba(52, 68, 80, 0.12);
  --face-chip-fill: rgba(255, 255, 255, 0.30);
  --face-chip-fill-hover: rgba(255, 255, 255, 0.46);

  /* --- material ---
     Fog, not glass and not stone. The most blur of any room, a shell that is
     pale rather than dark -- it is lit from inside the white rather than sat
     on top of a dark film -- and a sheen with no colour in it at all. The
     refraction thins almost to nothing: a hard bright edge is the one thing
     fog does not have. */
  --blur-shell: 34px;
  --film-shell: rgba(240, 244, 247, 0.80);
  --film-shell-solid: #EDF1F4;
  /* A raised surface on a pale panel is a *slight darkening*, not a slight
     lightening. This is the pair that has to flip or nothing else matters:
     every row, every well and every list in the building is built out of
     these two. */
  --film-raise:    rgba(27, 34, 40, 0.055);
  --film-raise-hi: rgba(27, 34, 40, 0.10);
  --film-well: rgba(27, 34, 40, 0.07);
  --sheen-light:     rgba(255, 255, 255, 0.60);
  --sheen-light-mid: rgba(255, 255, 255, 0.20);

  /* Dark on light, the other way round from everywhere else. */
  --knob-face: rgba(27, 34, 40, 0.88);
  --knob-face-hi: #121820;
  --knob-ink: #F2F6F8;
  --refract-width: 1px;
  --refract-edge:  0.75px;
  --radius-shell-open: 26px;

  /* --- motion ---
     Slow, and slower to stop than to start. Fog has no edges to arrive at, so
     nothing here snaps: the ease has a long tail and the durations are the
     longest in the building. */
  --ease:      cubic-bezier(.24, .62, .16, 1);
  --ease-soft: cubic-bezier(.28, .70, .18, 1);
  --dur-state: 240ms;
  --dur-face:  520ms;
  --dur-morph: 900ms;
  --dur-head:  820ms;
  /* A drop-down's list (js/ui/dropdown.css) is a pane of the same fog: the
     panel's own frosted surface and blur, the island's condensation on its
     edge, a halo with no direction where other rooms cast a shadow down, and
     it resolves out of the white in place rather than dropping. Rounder than
     the grove's, as the island is. */
  --uc-dd-motion: uc-dd-resolve;
  --uc-dd-motion-up: uc-dd-resolve;
  --uc-dd-dur: 320ms;
  --uc-dd-radius: 18px;
  --uc-dd-surface: rgba(240, 244, 247, 0.88);
  --uc-dd-backdrop: blur(34px) saturate(1.12);
  --uc-dd-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6),
                  inset 0 0 16px 2px rgba(255, 255, 255, 0.4),
                  0 0 40px 8px rgba(52, 68, 80, 0.16);

  /* --- reaction ---
     The half of a personality a tint cannot reach, and the half this room did
     not have.

     The canyon splits light: two coloured shadows either side of an edge,
     opening while you work. Fog cannot split anything. What fog does is
     **thicken** -- so the edge of a panel being worked does not gain a colour,
     it gains a halo, white and evenly all the way round, as though more of
     the air had gathered near the thing you are touching.

     Nothing lifts. A raised surface is a thing with a shadow under it, and a
     shadow needs a light with a direction; in fog there is light everywhere
     and no direction at all, so a control under the pointer glows rather than
     rises. The press is shallower than anywhere else for the same reason --
     there is no hard surface for it to press against.

     And on the island's own edge, condensation: the same frost the dial's
     lens carries on its rim. A bright line just inside the edge and a soft
     white gathering behind it, as though the air had settled on the glass.
     It is white only, so behind dark type it can only raise the contrast,
     never lower it, and it has no direction. While the island is worked --
     a slider moving, or the room listening, which `transcribe.css` adds --
     it thickens: the line brightens and the frost reaches further in. The
     lists match entry for entry so the shell's shadow transition can carry
     one into the other. */
  --react-edge: inset 0 0 0 1px rgba(255, 255, 255, 0.55),
                inset 0 0 14px 2px rgba(255, 255, 255, 0.34),
                0 0 34px 6px rgba(52, 68, 80, 0.07),
                0 0 0 1px rgba(27, 34, 40, 0.04);
  --react-edge-moving: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
                       inset 0 0 28px 6px rgba(255, 255, 255, 0.5),
                       0 0 52px 10px rgba(52, 68, 80, 0.14),
                       0 0 0 1px rgba(27, 34, 40, 0.07);
  --react-lift: none;
  --react-rise: 1.0;
  --react-rise-y: 0px;
  --react-press: 0.997;
  --react-bar-hover: rgba(27, 34, 40, 0.045);
  /* A slider being dragged hazes rather than colours: the same white, bloomed
     around the track and gathered around the thumb. */
  --react-drag-track: drop-shadow(0 0 3px rgba(52, 68, 80, 0.35));
  --react-drag-thumb: 0 0 0 5px rgba(52, 68, 80, 0.14);
  /* Held longest of any room. Fog takes its time to clear. */
  --react-hold: 620ms;
}

/* ==========================================================================
   The chalk archive -- the docs room

   A reading room cut into cliffs of paper, the pages laid down as strata. Bone,
   ink and oxidised iron: the film is warm and bright, the railings are rust,
   and everything you handle here is a sheet.

   **Paper, which folds rather than slides.** That is the whole character, and
   it is a material before it is a motion. The other rooms are glass of one
   kind or another -- smoked in the grove, crystal in the canyon, fog at the
   station -- and this one is not glass at all. Hardly any blur, no sheen to
   speak of, no refraction, corners you could cut yourself on. A flat thing
   that holds its shape because it is a sheet, not because it is lit.

   The second light room in the building, and light for a different reason
   from the fog. The fog is pale because you are inside the white; this is
   pale because it is paper on a desk. The two are told apart by temperature
   -- the fog is cool grey-blue and soft-edged, this is warm bone with a hard
   edge -- and by what each does when you work it, which is the reaction group
   at the bottom and the part `ROOMS.md` §2.3 says not to skip.
   ========================================================================== */

[data-room="docs"] {
  --dial-radius: 4px;
  /* What the archive looks like from outside: ink on chalk. The dial keeps
     this; inside the room the ink is taken a step deeper -- see `--signal`. */
  --accent: var(--room-docs-accent);

  /* --- palette ---
     Bone paper and the ink that is written on it. Every pair below was
     measured against the panel as it composites over the film, about
     #EFEADF, rather than against the token on its own, because the trap
     `ROOMS.md` names for a light room is exactly that: the alphas that read
     as quiet on a dark panel read as faint on a pale one.

       ink 13.8   ink-2 6.8   ink-3 5.0   signal as text 6.0
       white on the signal fill 7.2   ok 6.3   bad 6.5

     The signal is ink, deep enough to be read as text on paper and still
     the archive's own blue from outside. Oxidised iron, the colour of the
     railings in the film, is the room's word for something having gone
     wrong -- a state, not a second accent. */
  --signal:            #3C5A78;
  --signal-fill:       #3C5A78;
  --signal-fill-hover: #324D68;
  --signal-ink:        #FFFFFF;
  /* Light, because this is paper. Every scrim over the film, the drag veil
     and the halos behind the title derive from this one value, so the film
     is washed towards chalk rather than darkened -- which is what the place
     is called. */
  --shade-rgb: 238 232 220;
  /* An even wash, not the dark rooms' spotlight: a light veil strongest in
     the middle read as a washed-out centre with dark corners -- a vignette
     on the library. Nearly flat, lifted a touch behind the title. */
  --scrim-core: rgb(var(--shade-rgb) / 0.40);
  --scrim-mid:  rgb(var(--shade-rgb) / 0.36);
  --scrim-edge: rgb(var(--shade-rgb) / 0.33);
  --scrim-top:  rgb(var(--shade-rgb) / 0.30);
  --scrim-belt: rgb(var(--shade-rgb) / 0.28);
  --scrim-foot: rgb(var(--shade-rgb) / 0.32);
  --film-ink:   #221E19;
  --film-ink-2: rgba(34, 30, 25, 0.76);
  --film-ink-3: rgba(34, 30, 25, 0.66);
  --film-rule:  rgba(34, 30, 25, 0.18);
  --film-ok:      #285E40;
  --film-bad:     #8E3620;
  --film-working: #7A5414;

  /* --- identity ---
     A sheet of paper: square corners on the island and on the knob, and a
     title set like a printed heading rather than shouted in capitals --
     the grove's own face and weight, in sentence case, tight. */
  --font-title: var(--font-display);
  --title-weight: 800;
  --title-case: none;
  --title-track: -0.02em;
  --radius-shell-idle: 4px;
  --rest-height: 54px;
  --knob-radius: 3px;
  --rest-bar-radius: 3px;
  /* A paper edge is a hard line, darker than the hairline that divides a
     list, because it is the edge of an object and not a division inside one. */
  --shell-edge: rgba(34, 30, 25, 0.26);
  /* The account's faces: sheets of the same paper. The heading is set as the
     room's headings are (Create new's), and every corner is the island's
     own -- a button is cut as square as the knob, a plan card as square as
     the sheet -- with the sheet's hard edge rather than the hairline that
     divides a list. */
  --face-title-track: -0.02em;
  --face-radius: 3px;
  --face-card-radius: 4px;
  --face-box-radius: 2px;
  --face-line: var(--shell-edge);

  /* --- material ---
     Paper. Nearly opaque, because a sheet is not a window; the least blur of
     any room, because what little shows through a page is soft rather than
     sharp; a sheen so faint it is only the paper catching the light; and no
     refraction at all. A rainbow on the edge of a sheet of paper is a thing
     that has never happened. */
  --blur-shell: 8px;
  --film-shell: rgba(243, 238, 228, 0.94);
  --film-shell-solid: #EFE9DD;
  /* Raised is a slight darkening on a pale sheet, as it is in the fog. */
  --film-raise:    rgba(34, 30, 25, 0.05);
  --film-raise-hi: rgba(34, 30, 25, 0.09);
  --film-well:     rgba(34, 30, 25, 0.06);
  --sheen-light:     rgba(255, 252, 244, 0.38);
  --sheen-light-mid: rgba(255, 252, 244, 0.10);
  /* Ink on paper: the knob, the chosen pill and the button that does the
     thing are all a stamp of the room's own ink. */
  --knob-face:    rgba(42, 38, 32, 0.92);
  --knob-face-hi: #1C1915;
  --knob-ink:     #F4EFE4;
  --refract-width: 0px;
  --refract-edge:  0px;
  --radius-shell-open: 6px;

  /* --- motion ---
     Decisive. A fold is quick and it does not bounce: the curve leaves at
     once and lands flat, and the durations are the shortest of any room but
     the canyon's -- paper is light, and it does not drift. */
  --ease:      cubic-bezier(.30, .86, .30, 1);
  --ease-soft: cubic-bezier(.34, .80, .30, 1);
  --dur-state: 140ms;
  --dur-face:  360ms;
  --dur-morph: 640ms;
  --dur-head:  600ms;
  /* A drop-down's list unfolds from its fold like a sheet (js/ui/dropdown.css). */
  --uc-dd-motion: uc-dd-unfold;
  --uc-dd-motion-up: uc-dd-unfold-up;
  --uc-dd-dur: 200ms;

  /* --- reaction ---
     Paper stacks. Panels in the archive have **real edges**, PLATFORM.md §4
     says, and that is the reaction: the sheet under the island shows. At rest
     there is one, a hard unblurred offset a couple of pixels down and to the
     right, the way the second page of a stack shows past the first. While
     something on the panel is being moved the stack *shuffles* -- the sheet
     beneath slides further out and a third appears under it -- and when the
     hand comes off it squares up again.

     No blur in any of it. A soft shadow is light falling from somewhere; a
     sheet's edge is a line. Nothing grows under the pointer either, because
     paper does not swell: a control lifts by its own thickness, one pixel,
     and casts a one-pixel edge -- DESIGN.md §1.4's "1px positional shift",
     which every other room bends and this one is simply made of. The press
     is shallow and the hold is short. A sheet put down stays put down. */
  --react-edge:
    2px 2px 0 -1px rgba(236, 230, 218, 0.96),
    2px 2px 0 0 rgba(34, 30, 25, 0.20);
  --react-edge-moving:
    4px 4px 0 -1px rgba(236, 230, 218, 0.96),
    4px 4px 0 0 rgba(34, 30, 25, 0.22),
    8px 8px 0 -2px rgba(229, 222, 208, 0.94),
    8px 8px 0 -1px rgba(34, 30, 25, 0.16);
  --react-lift: 0 1px 0 0 rgba(34, 30, 25, 0.28);
  --react-rise: 1;
  --react-rise-y: -1px;
  --react-press: 0.995;
  --react-settle: var(--ease);
  --react-bar-hover: rgba(34, 30, 25, 0.04);
  /* A slider being dragged scores a line under its own track, the way a rule
     pressed into paper leaves a crease, and its thumb carries the same hard
     offset edge as everything else that is picked up here. */
  --react-drag-track: drop-shadow(0 1px 0 rgba(34, 30, 25, 0.45));
  --react-drag-thumb: 2px 2px 0 0 rgba(34, 30, 25, 0.30);
  --react-hold: 260ms;
}

/* ==========================================================================
   The rooms that are not built

   Their characters are decided in PLATFORM.md §4 and written down here while
   the reasoning is to hand, so that the day one of them is started its
   personality is not invented in a hurry. Palette and the one or two
   properties that carry the character; the rest is whatever the reference
   gives, which is the right default for a room nobody has designed yet.
   ========================================================================== */


/* ==========================================================================
   The still lake -- the music room

   Built 7 October 2026, every group at once (ROOMS.md §1: a room without a
   reaction has a colour, not a character).

   A mountain lake at dusk, mirror-still under mist, dark pines on the shore.
   Measured over the whole film (k-means in OKLab, 40 frames, 2 a second):
   pine-black #161815 20%, mist #C7CCD2 13% and #B1BBC1 11%, the water's
   slate #222929 11%, #2E3A3F 9%, #3B4C54 9%, #4C5F69 7%, #647781 6%. It is
   one hue: of the coloured pixels (a quarter of the picture) 98% lie
   between 210 and 270 degrees, the slate at 230; anything warm is under a
   tenth of a percent. So the signal is that hue lit -- the glacial cyan of
   thin ice, #8DD0E6 -- and the room is otherwise slate glass and mist.

   The idea that runs through it: **still water answers**. Nothing lifts or
   casts a shadow with a direction; a control under the pointer sends a
   ring out from itself, a panel being worked carries a faint ripple ring
   at its edge, and at rest the panel has a waterline -- one pale line low
   on it, its reflection. Everything settles slowly: the softest curve and
   the longest morph in the building.
   ========================================================================== */

[data-room="music"] {
  --dial-radius: 999px;
  --accent: var(--room-music-accent);

  /* --- palette ---
     Measured against the panel as it composites over the film: see
     ROOMS.md §2.7 for the figures. */
  --signal:            #8DD0E6;
  --signal-fill:       #8DD0E6;
  --signal-fill-hover: #70BDD8;
  --signal-ink:        #071316;
  --shade-rgb: 10 15 17;
  --film-ink:   #E8EEF0;
  --film-ink-2: rgba(232, 238, 240, 0.76);
  --film-ink-3: rgba(232, 238, 240, 0.58);
  --film-rule:  rgba(232, 238, 240, 0.14);
  --film-shell-solid: #10171A;

  /* --- identity ---
     Water: the roundest island of the eight, the largest knob, and a title
     thin and open enough to read as something floating rather than set
     down. */
  --font-title: var(--font-ui);
  --title-weight: 400;
  --title-case: lowercase;
  --title-track: 0.04em;
  /* Half the resting island's height, not a 999px capsule: one row tall it
     is the same capsule, and when the sessions grow it the corners stay
     where the knob and the rows can sit inside them. */
  --radius-shell-idle: 46px;
  --rest-height: 66px;
  --knob-size: 52px;

  /* --- material ---
     Mist: the most blur after the fog's, a cold slate glass, a sheen with
     no warmth in it, the refraction kept fine. */
  --blur-shell: 30px;
  --film-shell: rgba(14, 20, 23, 0.66);
  --film-raise:    rgba(220, 232, 238, 0.07);
  --film-raise-hi: rgba(220, 232, 238, 0.12);
  --film-well: rgba(5, 9, 11, 0.44);
  --sheen-light:     rgba(214, 226, 232, 0.15);
  --sheen-light-mid: rgba(214, 226, 232, 0.045);
  --refract-width: 1.25px;
  --refract-edge:  1px;
  --radius-shell-open: 40px;

  /* --- motion ---
     The softest curve and the longest morph: water does not stop, it
     settles. */
  --ease:      cubic-bezier(.20, .80, .30, 1);
  --ease-soft: cubic-bezier(.18, .92, .30, 1);
  --dur-state: 200ms;
  --dur-face:  480ms;
  --dur-morph: 920ms;
  --dur-head:  760ms;

  /* --- reaction ---
     Still water answers. At rest, a waterline: one pale line low on the
     panel. Worked, a ring of the ice's light at its edge. Under the
     pointer a control rings rather than rises; the press is the deepest in
     the building, and it lets go slowly. */
  --react-edge: inset 0 -1px 0 rgba(232, 238, 240, 0.06);
  --react-edge-moving:
    inset 0 -1px 0 rgba(232, 238, 240, 0.08),
    0 0 0 1px rgba(141, 208, 230, 0.16),
    0 0 0 8px rgba(141, 208, 230, 0.05);
  --react-press: 0.975;
  --react-rise: 1;
  --react-rise-y: 0px;
  --react-settle: var(--ease-soft);
  --react-lift: 0 0 0 4px rgba(141, 208, 230, 0.10);
  --react-bar-hover: rgba(220, 232, 238, 0.035);
  --react-drag-track: drop-shadow(0 0 3px rgba(141, 208, 230, 0.35));
  --react-drag-thumb: 0 0 0 6px rgba(141, 208, 230, 0.14);
  --react-hold: 700ms;
}

/* ==========================================================================
   The long valley, from a moving train -- the video room

   Built 5 October 2026, and given every group at once, because ROOMS.md §2.3
   is blunt about what a room without a reaction looks like: a colour, not a
   character. This block used to be a sketch here among the rooms not built.

   Landscape drifting past a window, warm light coming in from the side, and
   everything in motion. Tight and fast, like something read off a passing
   sign. The one idea that runs through all of it: **light trails backwards**.
   Whatever moves leaves its light behind it, six pixels to the left, which is
   the valley going past the glass.
   ========================================================================== */

[data-room="video"] {
  --dial-radius: 9px;
  /* From outside, the valley at dusk -- the red end of warm, kept apart from
     the canyon's gold (see the accents at the top of this file). Inside, the
     room works in the window light that comes through its film: an amber
     taken from the sunlit blinds in it. The same split the grove has, green
     outside and rust within. */
  --accent: var(--room-video-accent);

  /* --- palette ---
     Measured against the panel as it composites over the film, about
     #15171C: ink 15.5, ink-2 9.3, ink-3 6.2, the signal as text 6.6, the
     dark ink on the signal fill 6.8, and 5.5 on its hover. */
  --signal:            #CDAE72;
  --signal-fill:       #CDAE72;
  --signal-fill-hover: #B89A5E;
  --signal-ink:        #1A140A;
  /* Dusk blue: the shade the scrim and every halo derive from. */
  --shade-rgb: 13 11 9;
  --film-ink:   #F1EEE9;
  --film-ink-2: rgba(241, 238, 233, 0.76);
  --film-ink-3: rgba(241, 238, 233, 0.60);
  --film-rule:  rgba(241, 238, 233, 0.16);
  --film-shell-solid: #15110C;

  /* --- identity ---
     The narrowest tracking of the eight, in capitals, and an island with its
     corners knocked off rather than rounded away. */
  --font-title: var(--font-display);
  --title-weight: 800;
  --title-case: uppercase;
  --title-track: -0.05em;
  --radius-shell-idle: 12px;
  --rest-height: 56px;
  --knob-radius: 8px;
  --rest-bar-radius: 8px;
  /* The account's faces: headed in the valley's capitals at its tight
     tracking, a step down so a heading in capitals keeps to one line, and
     cut rather than rounded -- the editor's chamfer, two corners knocked
     off, as on its Export button and its island: six pixels on a button,
     twelve on a plan card. A cut edge has no border along the diagonal, so
     styles.css draws the hairline there itself. */
  --face-title-case: uppercase;
  --face-title-track: -0.04em;
  --face-title-size: var(--t-lg);
  --face-title-drop: calc((44px - var(--t-lg) * 1.15) / 2);
  --face-title-wrap: balance;
  --face-radius: 0;
  --face-card-radius: 0;
  --face-box-radius: 2px;
  --face-cut: 6px;
  --face-card-cut: 12px;
  --face-field-radius: 0;
  --face-ring: 2px;
  --face-clip: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  --face-card-clip: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);

  /* --- material ---
     A carriage window: glass with a little less blur than the grove's, so the
     valley behind it reads as moving rather than as fog, and a sheen warmed
     by the light coming in. The open island keeps the same cut corner. */
  --blur-shell: 18px;
  --film-shell: rgba(20, 16, 11, 0.70);
  --film-raise:    rgba(255, 240, 222, 0.07);
  --film-raise-hi: rgba(255, 240, 222, 0.12);
  --film-well: rgba(9, 7, 5, 0.46);
  --sheen-light:     rgba(255, 226, 190, 0.16);
  --sheen-light-mid: rgba(255, 226, 190, 0.05);
  --refract-width: 1.5px;
  --refract-edge:  1.25px;
  --radius-shell-open: 12px;

  /* --- motion ---
     The quickest state change in the building, and a curve that leaves at
     once and does not linger: a frame is a twenty-fourth of a second here. */
  --ease:      cubic-bezier(.16, .84, .24, 1);
  --ease-soft: cubic-bezier(.20, .80, .25, 1);
  --dur-state: 150ms;
  --dur-face:  300ms;
  --dur-morph: 560ms;
  --dur-head:  520ms;

  /* --- reaction ---
     Light trails backwards. At rest the panel carries one warm line on its
     leading edge, where the window light catches it. While something on it
     moves, that light streaks out behind, to the left. A control under the
     pointer casts its glow back the same way, a press is short and firm, and
     the room lets go quickly -- a train does not wait. */
  --react-edge: 1px 0 0 0 rgba(205, 174, 114, 0.16);
  --react-edge-moving:
    -6px 0 16px -6px rgba(205, 174, 114, 0.40),
    1px 0 0 0 rgba(205, 174, 114, 0.26);
  --react-press: 0.985;
  --react-rise: 1.012;
  --react-rise-y: -1px;
  --react-settle: var(--ease);
  --react-lift: -6px 2px 14px -8px rgba(205, 174, 114, 0.45), var(--lift-1);
  --react-bar-hover: rgba(255, 226, 190, 0.04);
  /* ROOMS.md: a dragged slider trails its own light backwards. */
  --react-drag-track: drop-shadow(-6px 0 4px rgba(205, 174, 114, 0.35));
  --react-drag-thumb: -6px 0 8px -2px rgba(205, 174, 114, 0.50);
  --react-hold: 240ms;
}

/* Slow bioluminescence in a hollow: a very slow pulse, light along the edges. */
[data-room="ask"] {
  --dial-radius: 999px;
  --accent: var(--room-ask-accent);
  /* --- identity ---
     The quietest of the eight. Nothing is capitalised, nothing is heavy, and
     the island is a plain rounded bar -- a room that is listening rather
     than announcing. */
  --font-title: var(--font-ui);
  --title-weight: 400;
  --title-case: lowercase;
  --title-track: 0.015em;
  --radius-shell-idle: 999px;
  --rest-height: 60px;
  --rest-type-case: lowercase;

  --signal: #4FA88C; --signal-fill: #3E8A72; --signal-fill-hover: #337460;
  --shade-rgb: 5 9 10;
  --blur-shell: 30px;
  --dur-state: 260ms; --dur-face: 560ms;
  --react-edge: 0 0 12px -2px rgba(79, 168, 140, 0.20);
  --react-edge-moving: 0 0 20px -2px rgba(79, 168, 140, 0.34);
  --react-hold: 900ms;
}

/* The unfinished quarry: wireframe first, then the surfaces fill in. Little
   blur, visible rules, a console that looks like it is still being built. */
[data-room="design"] {
  --dial-radius: 2px;
  --accent: var(--room-design-accent);
  /* --- identity ---
     Drawn rather than finished: mono throughout, tracked wide like a
     dimension on a drawing, and an island with corners as square as the grid
     it was set out on. */
  --font-title: var(--font-mono);
  --title-weight: 400;
  --title-case: uppercase;
  --title-track: 0.08em;
  --radius-shell-idle: 2px;
  --rest-height: 52px;
  --knob-radius: 2px;
  --rest-bar-radius: 2px;
  --rest-type-font: var(--font-mono);
  --rest-type-case: uppercase;
  --rest-type-track: 0.08em;

  --signal: #7E8C99; --signal-fill: #6A7784; --signal-fill-hover: #59646F;
  --shade-rgb: 13 14 15;
  --blur-shell: 12px;
  --film-rule: rgba(238, 241, 240, 0.30);
  --radius-shell-open: 4px;
  --react-edge: 0 0 0 1px rgba(238, 241, 240, 0.08);
}
