/* VENUE: the ROOM SHELL of TWIST TONGUE — what every room of the palace stands
   in: the floor, the three walls, the blacklight, and the two chips every room
   carries (the master mute and the coin token). It moved out of index.html on
   2026-09-02 when the palace grew a second room (the pinball room, D-0113); a
   room shell copied into two pages is two rooms that drift apart.

   What is NOT here: anything a particular room hangs on these walls — the
   carousel, the TV, the vending machine, the pinball machines, the doorway.
   Those stay with their room. */

/* --cabar: the standing cabinet's h/w aspect. Must equal FULL_CAB in
   venue/fullcab.js — palace.js throws at boot if they drift. */
:root { color-scheme: dark; --cabar: 1.6667; }
* { margin: 0; padding: 0; box-sizing: border-box; }
/* NO SCROLLBARS, ANY SCREEN (D-0043). The room is a room: you see what is
   in view. Clipping the bottom of a cabinet plinth is the accepted cost —
   and this is also what finally kills the ~112px sideways scroll the wing
   cabinets caused at phone widths (a bug that predated the TV work).
   Sizes below are tuned so an iPhone SE (375x667) still shows whole
   cabinets; smaller than that, the plinths clip rather than scroll. */
html { overflow: hidden; }
/* THE ROOM IS THE SAME ROOM EVERY VISIT. The background used to be repainted
   from the front cabinet's palette on every rotation (bodyTint) — the room
   took its colour from whatever machine you happened to be looking at, which
   is why it never had one of its own. TWIST TONGUE's walls are its walls. */
body {
  background: hsl(276 46% 4%);
  color: #e8e8ff;
  font-family: ui-monospace, 'Cascadia Mono', 'Fira Mono', monospace;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: clamp(10px, 3vh, 30px) 10px 14px;
  overflow: hidden;
}
/* the palace's own authored neon logotype replaces the plain-text headline;
   the h1 stays for screen readers */
header.palacehead {
  display: flex;
  justify-content: center;
  width: 100%;
  position: relative;
  z-index: 2;
}
header.palacehead canvas { display: block; width: min(94vw, 560px); height: auto; }
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* THE FLOOR. An 80s arcade carpet the whole room stands on. The tile is
   generated (venue/rug.js) and handed in as --rugtile; the perspective is
   CSS, and the far edge is masked away so the floor recedes into the dark
   instead of ending on a hard line.

   It is FIXED to the viewport, not absolute inside main.room. As a child of
   the room it was a 42%-tall band inside a box that does not reach the
   window, so the floor stopped short of the bottom and both sides and the
   flat page background showed through — a room with a rug in the middle of
   it instead of a floor. Anchored to the viewport it always reaches all four
   edges, whatever the room above it is doing. */
.rug {
  position: fixed;
  /* the plane is only a little wider than the window: its far edge has to
     land INSIDE the room, or there are no diagonals and no walls to show —
     which is what "the carpet just covers from that line to the bottom
     edge" was describing. Its near edge still runs off both sides, so the
     floor a visitor is standing on has no visible end. */
  left: -12%;
  right: -12%;
  bottom: 0;
  height: 80vh;
  z-index: 0;
  pointer-events: none;
  background-image: var(--rugtile);
  background-repeat: repeat;
  background-size: 128px 128px;
  /* the projection is tuned by LOOKING: at 64° / 420px the plane collapsed
     to a ~100px strip along the bottom edge — technically a floor, visually
     a skirting board. 54° over a 560px perspective gives it real depth AND
     enough foreshortening that the far edge is half the width of the near
     one, which is what makes the side walls exist at all. */
  transform: perspective(560px) rotateX(54deg);
  transform-origin: 50% 100%;
  /* only the last breath of the far edge is feathered now: the floor ENDS
     at a wall, and a wall is a line on purpose */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 7%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 7%);
}
/* the four corners of the floor PLANE, as projected. Zero-size markers the
   painter reads with getBoundingClientRect — the walls have to meet the
   floor exactly, and re-deriving the perspective maths in JS would be a
   second source of truth that silently drifts from the CSS. */
.rug i { position: absolute; width: 0; height: 0; }
.rug i[data-corner="tl"] { left: 0; top: 0; }
.rug i[data-corner="tr"] { left: 100%; top: 0; }
.rug i[data-corner="bl"] { left: 0; top: 100%; }
.rug i[data-corner="br"] { left: 100%; top: 100%; }
/* THE WALLS. The floor is drawn in perspective, so its far edge is a
   horizon and its sides run off diagonally — and a room that shows those
   diagonals and nothing behind them is a floating plane, not a room. The
   back wall stands on the horizon; the two side walls take the wedges
   outside the floor's own edges. Their shapes are cut from the floor's
   measured corners (venue/room.js), never from a second guess at the maths. */
.walls { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.wall { position: absolute; }
.wall.back {
  left: 0;
  right: 0;
  top: 0;
  /* height: set from the measured horizon */
  background:
    linear-gradient(to bottom, hsla(279, 62%, 19%, 0.5), hsla(276, 50%, 7%, 0.92) 58%, hsl(276 42% 3%)),
    repeating-linear-gradient(90deg, hsla(280, 45%, 62%, 0.05) 0 1px, transparent 1px 46px);
}
/* THE SKIRTING: a stripe of UV paint everywhere a wall meets the floor —
   across the back, and down BOTH diagonals. It is the single thing that
   turns three flat gradients into a room, because it is the only line that
   tells you where the floor stops and the wall starts. Placed and rotated
   from the measured floor edge, so it lands on the junction and not near
   it. Each side skirt is anchored at the far corner and swung down the
   floor's own edge. */
.skirt {
  position: absolute;
  left: 0;
  top: 0;
  height: 6px;
  transform-origin: 0 0;
  background: linear-gradient(to bottom, hsl(283 100% 72%), hsl(283 85% 30%));
  box-shadow: 0 0 16px 2px hsla(283, 100%, 62%, 0.4);
  opacity: 0.85;
}
/* the two vertical corners of the room, where the side walls meet the back */
.corner {
  position: absolute;
  top: 0;
  width: 1px;
  background: linear-gradient(to bottom, hsla(281, 70%, 74%, 0.22), hsla(281, 70%, 74%, 0.04));
}
.wall.side {
  inset: 0;
  /* clip-path: set from the measured floor edge */
  background:
    linear-gradient(to bottom, hsla(279, 58%, 17%, 0.55), hsl(276 42% 3%) 72%),
    repeating-linear-gradient(90deg, hsla(280, 45%, 62%, 0.04) 0 1px, transparent 1px 38px);
}
/* THE BLACKLIGHT. A UV tube across the ceiling, the cone of light it throws,
   the green bounce the carpet kicks back, and the vignette that says the
   light has an edge. Everything the room owns sits ABOVE the floor and BELOW
   the objects in it: the fixtures carry their own light, the air does not. */
.uv {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(125% 78% at 50% -20%, hsla(278, 100%, 62%, 0.22), transparent 62%),
    radial-gradient(92% 42% at 50% 106%, hsla(142, 92%, 55%, 0.09), transparent 72%),
    radial-gradient(130% 105% at 50% 44%, transparent 52%, rgba(0, 0, 0, 0.55));
}
/* the fixture itself — a lit tube is what makes a room read as lit BY
   something rather than merely dark */
.uv::before {
  content: '';
  position: absolute;
  left: 8%;
  right: 8%;
  top: 0;
  height: 2px;
  border-radius: 2px;
  background: hsl(283 100% 74%);
  box-shadow:
    0 0 18px 4px hsla(283, 100%, 62%, 0.55),
    0 0 64px 20px hsla(283, 100%, 55%, 0.26);
}
/* THE TOKEN: your money as a real object (Phase 5). The vending machine
   stopped showing a balance — a machine is where coins come from, not
   where they live. On desktop the token stands on the rug at the left of
   the room, opposite the machine; on phones it takes the bottom-right
   corner and is the way in to the machine, exactly as the coins counter
   was. Pressing it means "I want more coins" at both widths. */
button.coinchip {
  display: block;
  position: absolute;
  left: max(72px, 7%);
  bottom: 0;
  z-index: 4;
  width: calc(var(--cabw) * 0.23);
  background: none;
  border: none;
  padding: 0;
  line-height: 0;
  cursor: pointer;
}
button.coinchip canvas {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
  filter: drop-shadow(0 3px 7px rgba(0, 0, 0, 0.65));
}
button.coinchip:focus-visible { outline: 2px solid #7f7fff; outline-offset: 4px; border-radius: 50%; }
@keyframes tokenpulse {
  0%, 100% { filter: drop-shadow(0 3px 7px rgba(0, 0, 0, 0.65)); }
  50% { filter: drop-shadow(0 3px 7px rgba(0, 0, 0, 0.65)) drop-shadow(0 0 9px rgba(255, 190, 70, 0.8)); }
}
button.coinchip.pulse canvas { animation: tokenpulse 1.2s ease-in-out infinite; }
/* the room's master mute: one glyph, bottom-left, every width — a struck
   note means the whole room (cabinets, machine, TV) is silent */
button.mutechip {
  display: block;
  position: fixed;
  left: 12px;
  bottom: 12px;
  z-index: 55;
  font-family: inherit;
  font-size: 15px;
  line-height: 1;
  color: #e8e8ff;
  background: rgba(6, 6, 12, 0.72);
  border: 1px solid rgba(232, 232, 255, 0.4);
  border-radius: 20px;
  padding: 7px 11px;
  cursor: pointer;
}
button.mutechip.muted { color: #666688; text-decoration: line-through; }
button.mutechip:focus-visible { outline: 2px solid #7f7fff; outline-offset: 3px; }
/* phones: the token takes the bottom-right corner at every room */
@media (max-width: 560px) {
  body { position: relative; }
  button.coinchip {
    position: fixed;
    left: auto;
    right: 12px;
    bottom: 12px;
    width: 58px;
    z-index: 55;
  }
}
/* A DOORWAY between rooms (D-0113, venue/doorway.js). It lives INSIDE the
   room's stacking context on purpose: on a phone the wing cabinets overlap
   the narrow side walls, and a doorway drawn on the wall layer would be
   hidden behind a dimmed cabinet edge nobody can see past. At z 2 it sits
   above the wings and below the front cabinet at every width; on desktop it
   never shares a column with a cabinet at all. The hit area is the opening
   plus its sign (clip-path, set from the same geometry), not the box. */
.doorspill { position: absolute; z-index: 0; pointer-events: none; display: block; }
a.doorway {
  position: absolute;
  z-index: 2;
  display: block;
  line-height: 0;
  cursor: pointer;
  -webkit-user-drag: none;
  user-select: none;
}
a.doorway canvas { display: block; }
a.doorway:hover canvas, a.doorway:focus-visible canvas { filter: brightness(1.3); }
a.doorway:focus-visible { outline: 2px solid #7f7fff; outline-offset: 3px; }
/* THE DOOR CHIP (D-0118): on a phone the side walls are slivers behind the
   wing cabinets, so the wall doorway is a slot nobody finds (Q47). The chips
   are the phone's affordance language already — the mute, the token, the
   account door — and the way to the other room is one more of them: a lit
   doorway standing above the coin token, bottom right, in both rooms. Not
   drawn on desktop, where the wall carries the real door. */
a.doorchip { display: none; }
@media (max-width: 560px) {
  /* the wall door is a slot behind the wing on a phone: the chip replaces it */
  a.doorway, .doorspill { display: none; }
  a.doorchip {
    display: block;
    position: fixed;
    right: 12px;
    bottom: 84px;
    width: 56px;
    z-index: 55;
    line-height: 0;
    -webkit-user-drag: none;
    user-select: none;
  }
  a.doorchip canvas {
    display: block;
    width: 100%;
    height: auto;
    image-rendering: pixelated;
    filter: drop-shadow(0 3px 7px rgba(0, 0, 0, 0.65));
  }
  a.doorchip:focus-visible { outline: 2px solid #7f7fff; outline-offset: 3px; }
}
