/* Pixelated Elegance — public domain (CC0 1.0), fontspace.com. Its design grid
   is 9 px to the em (cap height 7, x-height 6, descender 1), so it only renders
   crisp at whole multiples of 9px: 18px here, which is 2x. Anything between
   multiples puts the glyph edges between screen pixels and blurs them. */
@font-face {
  font-family: "Pixelated Elegance";
  src: url("assets/fonts/PixelatedElegance.ttf") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Sampled from the earth border baked into the artwork, so the page blends
     into the frame instead of showing a hard edge. */
  --den-brown-dark: #401809;
  --den-brown-night: #240d04;

  /* The art grid. Every asset -- room, overlays, mouse -- is authored at this
     resolution, so one art pixel is one art pixel everywhere on screen.
     Percentages below are art-pixel measurements divided by these. */
  --art-w: 384;
  --art-h: 216;

  /* One unit of the interface's own pixel grid, sized to sit near the room's
     own art pixel on a typical screen. */
  --px: 3px;
}

* {
  box-sizing: border-box;
}

/* The ground colour lives on the root so it fills the whole canvas: put it on
   `body` instead and it only covers the body box, which centring shrinks to
   the width of the den. It follows the room's own lights, so the edge of the
   frame never becomes visible when the den dims. */
html {
  background-color: var(--den-brown-dark);
  transition: background-color 1.6s ease;
}

html.night {
  background-color: var(--den-brown-night);
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  font: 18px/1 "Pixelated Elegance", ui-monospace, "Courier New", monospace;
}

/* Holds the room and its controls together so the pair stays centred. The
   max-width matters: without it the button row sets the stage's width and
   pushes itself off the side of a narrow screen instead of wrapping. */
.stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  /* An explicit width, not a max-width: a content-sized column takes the width
     of its widest child, so the button row would set the stage's width and push
     itself off the side of a narrow screen instead of wrapping inside it. */
  width: 100%;
  min-width: 0;
}

.den {
  position: relative;
  /* Bigger art pixels are the point, so let the room take the screen it can.
     100% is the stage, which already sits inside the body's padding — using a
     vw figure here instead overflows by the width of that padding. */
  width: min(1440px, 100%, calc(82vh * 16 / 9));
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

/* Every layer is positioned in art-pixel percentages of the frame and drawn
   with nearest-neighbour scaling, which is what keeps the pixels square.
   Stacking order is explicit because one layer has to move: see .fire below. */
.layer {
  position: absolute;
  display: block;
  pointer-events: none;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
}

.room       { z-index: 1; }
.tv-off     { z-index: 2; }
.fire       { z-index: 3; }
.zone       { z-index: 4; }
.scene      { z-index: 6; }
.scene-fade { z-index: 9; }
.tv-movie   { z-index: 10; }
/* Above the holiday decorations (11) too: they are set dressing on the floor
   and walls, and the mouse walking in front of a pumpkin is what keeps the
   room's depth. It is hidden whenever a painted scene is up, so this never
   puts it over one. */
.mouse      { z-index: 12; }

/* The cocoa scenes carry the room's own flame, pixel for pixel -- 2 art pixels
   of 3315 differ from the background. So the flicker can keep running over
   them, which just means lifting it above the scene. The fire scenes repaint
   the flame themselves, and there the overlay stays put and stops. */
.den.scene-keeps-fire .fire {
  z-index: 8;
}

/* `.layer` sets display, so the hidden attribute needs to win back. */
.layer[hidden] {
  display: none;
}

.room {
  inset: 0;
  width: 100%;
  height: 100%;
}

/* --- Registered overlay crops (art-pixel boxes on the 320x180 grid) --- */

/* fire: x 93, y 77, 65x51 */
.fire {
  left: 24.2188%;
  top: 35.6481%;
  width: 16.9271%;
  height: 23.6111%;
}

/* tv-off: x 118, y 63, 18x16 */
.tv-off {
  left: 30.7292%;
  top: 29.1667%;
  width: 4.6875%;
  height: 7.4074%;
}

/* A scene is the whole room again, with the mouse painted into it, and it cuts
   straight in. The mouse has already walked to where the scene shows it, so
   there is nothing to dissolve between — a fade here would only blur the swap. */
.scene {
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}

.den.scene-on .scene {
  opacity: 1;
}

/* The one fade in the piece. Both frames are the same painting of the mouse
   sitting on the couch, one with the lamps up and one with them down, so the
   dissolve is only the light changing around a mouse that is already there. */
.scene-fade {
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 1.6s ease;
}

.den.night .scene-fade {
  opacity: 1;
}

/* The television's glass alone, animating over the movie frame. It comes up on
   the same curve as the lamps go down, so the picture is never brighter than
   the room it is lighting. Art rect (118, 65) 17x12 of 384x216 — the lit glass
   only, measured off the grid; anything larger drags the bezel with it. */
.tv-movie {
  left: 30.7292%;
  top: 30.0926%;
  width: 4.4271%;
  height: 5.5556%;
  opacity: 0;
  transition: opacity 1.6s ease;
}

.den.night .tv-movie {
  opacity: 1;
}

/* Holiday mode swaps the generated frames for the real film. Only one of the
   two is ever displayed, so they share the fade. The film runs a little past
   the glass onto its rim, fitted to the "Movie Fit Example" mockup: art box
   (118.6, 63.3) 17.1x14.2. The set is seen from its left, so the file is
   pre-warped to a keystone -- left edge full height from 2.8% down, right edge
   from the top to 90.8% -- and the clip trims the corners the warp leaves
   behind. Keep the polygon in step with the perspective= numbers the video
   was cut with, and use fill, not cover: cover would crop off those corners. */
.tv-video {
  display: none;
  left: 30.8854%;
  top: 29.3056%;
  width: 4.4531%;
  height: 6.5741%;
  object-fit: fill;
  clip-path: polygon(0 2.8%, 100% 0, 100% 90.8%, 0 100%);
}

.den.holiday .tv-video {
  display: block;
}

.den.holiday #tv-movie {
  display: none;
}

/* --- Holiday decorations ---
   Hung above every other layer, including the painted scenes, because they are
   things added to the room rather than a state of it: the scenes are the same
   den without them, so anything lower would have the decorations vanish the
   moment the mouse sat down. Their boxes are art-pixel measurements printed by
   tools/build_assets.py — paste them, don't recompute them. */
/* The switch is a switch: the decorations are simply there or not, with no fade
   either way. Nothing in the room dissolves except the lamps, and a half-present
   pumpkin on the way in read as a glitch rather than as a transition. */
.decor-set {
  position: absolute;
  inset: 0;
  z-index: 11;
  pointer-events: none;
  display: none;
}

.den.holiday .decor-set {
  display: block;
}

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

.decor img {
  position: absolute;
  display: block;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
}

/* The same pieces at the room's measured 0.55 dim, crossfading in on the same
   1.6s curve as the lamps so the decorations go down with the room rather than
   staying lit on top of a dark painting. */
.decor-dark {
  opacity: 0;
  transition: opacity 1.6s ease;
}

.den.night .decor-dark {
  opacity: 1;
}

.decor .decor-hat-1 { left: 67.8385%; top: 25.4630%; width: 3.9062%; height: 7.8704%; }
.decor .decor-portrait-1 { left: 54.6875%; top: 26.8519%; width: 2.0833%; height: 6.4815%; }
.decor .decor-portrait-2 { left: 38.6719%; top: 29.6296%; width: 2.3438%; height: 6.0185%; }
.decor .decor-pumpkin-1 { left: 66.4062%; top: 33.7963%; width: 2.6042%; height: 5.0926%; }
.decor .decor-cauldron-1 { left: 25.1302%; top: 49.5370%; width: 5.9896%; height: 12.0370%; }
.decor .decor-pumpkin-2 { left: 27.4740%; top: 55.0926%; width: 4.4271%; height: 8.7963%; }
.decor .decor-pumpkin-3 { left: 48.9583%; top: 58.7963%; width: 3.1250%; height: 6.0185%; }
.decor .decor-pumpkin-4 { left: 69.6615%; top: 59.2593%; width: 3.9062%; height: 7.8704%; }
.decor .decor-pumpkin-5 { left: 36.1979%; top: 72.2222%; width: 4.1667%; height: 8.3333%; }


/* --- Controls ---
   Deliberately quiet: the room should hold the eye, not the buttons. They sit
   below the frame in the art's own browns so they read as part of the piece. */
/* The activity group and the sound switch, spaced apart so they do not read as
   one row of equal choices. */
/* Quiet, single-line credit for Part 1's materials list. Sits under the
   controls rather than beside them so it never reads as a sixth button. */
.materials {
  text-align: center;
  margin-top: 4px;
}

.materials a {
  color: rgba(255, 216, 178, 0.55);
  font-size: 13px;
  letter-spacing: 0.5px;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 216, 178, 0.3);
}

.materials a:hover,
.materials a:focus-visible {
  color: rgba(255, 228, 198, 0.9);
  border-bottom-color: rgba(255, 216, 178, 0.7);
}

.controlbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 10px 26px;
  max-width: 100%;
}

.controls {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  max-width: 100%;
}

/* --- The holiday switch ---
   Pinned to the window's top right rather than sitting in the stage: the row
   under the frame is what the mouse can be asked to do, and this is a setting of
   the room. It lands on the dark vignette in the corner of the art, which is why
   it can sit over the frame without fighting it. Above the decorations' own
   z-index 11, since they are in the same stacking context. */
.holidaybar {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  /* Right-aligned so the pair grows leftwards off the corner and the switch
     nearest the edge stays put as labels change length. */
  justify-content: flex-end;
}

.holiday-toggle {
  --edge: rgba(255, 205, 150, 0.32);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border: none;
  border-radius: 0;
  /* Dark enough to hold the label wherever the corner art happens to be. */
  background: rgba(30, 14, 6, 0.62);
  color: rgba(255, 216, 178, 0.72);
  font: inherit;
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: pointer;
  /* Same four-shadow outline as the buttons below the frame, so the corners stay
     notched and square rather than rounding the way a border would. */
  box-shadow:
    0 calc(-1 * var(--px)) 0 0 var(--edge),
    0 var(--px) 0 0 var(--edge),
    calc(-1 * var(--px)) 0 0 0 var(--edge),
    var(--px) 0 0 0 var(--edge);
  transition: background-color 0.35s ease, color 0.35s ease,
              box-shadow 0.35s ease;
}

.holiday-toggle:hover,
.holiday-toggle:focus-visible {
  --edge: rgba(255, 205, 150, 0.6);
  color: rgba(255, 228, 198, 0.95);
}

.holiday-toggle[aria-pressed="true"] {
  --edge: rgba(255, 190, 120, 0.85);
  color: #ffeed9;
}

/* The track is sized in whole units of the interface's own pixel grid, so it
   steps down with everything else on a narrow screen instead of blurring. */
.holiday-switch {
  position: relative;
  flex: none;
  width: calc(var(--px) * 14);
  height: calc(var(--px) * 7);
  background: rgba(0, 0, 0, 0.45);
  box-shadow:
    0 calc(-1 * var(--px)) 0 0 var(--edge),
    0 var(--px) 0 0 var(--edge),
    calc(-1 * var(--px)) 0 0 0 var(--edge),
    var(--px) 0 0 0 var(--edge);
}

/* Four steps rather than a glide: the knob lands on whole pixels the whole way
   across, which is the same reason the walk cycle is choppy on purpose. */
.holiday-knob {
  position: absolute;
  top: var(--px);
  left: var(--px);
  width: calc(var(--px) * 5);
  height: calc(var(--px) * 5);
  background: rgba(255, 216, 178, 0.55);
  transition: left 0.2s steps(4), background-color 0.2s steps(4);
}

.holiday-toggle[aria-pressed="true"] .holiday-knob {
  left: calc(var(--px) * 8);
  background: #ffb765;
}

/* Both switches are real two-state switches now, so they share one rule: the
   knob slides on aria-pressed, whatever is being switched. */

/* The outline is drawn with four offset shadows rather than a border, which
   leaves the corners notched out — square, stepped, and the same shape the room
   is drawn in. A real border would round or antialias them. */
.control {
  --edge: rgba(255, 205, 150, 0.3);
  padding: 9px 15px;
  border: none;
  border-radius: 0;
  background: rgba(255, 190, 130, 0.05);
  color: rgba(255, 216, 178, 0.72);
  font: inherit;
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow:
    0 calc(-1 * var(--px)) 0 0 var(--edge),
    0 var(--px) 0 0 var(--edge),
    calc(-1 * var(--px)) 0 0 0 var(--edge),
    var(--px) 0 0 0 var(--edge);
  transition: background-color 0.35s ease, color 0.35s ease,
              box-shadow 0.35s ease;
}

.control:hover,
.control:focus-visible {
  --edge: rgba(255, 205, 150, 0.55);
  background: rgba(255, 190, 130, 0.12);
  color: rgba(255, 228, 198, 0.95);
}

.control[aria-pressed="true"] {
  --edge: rgba(255, 190, 120, 0.85);
  background: rgba(255, 170, 90, 0.2);
  color: #ffeed9;
}

/* --- Interaction zones (soft nudge affordance, no hard demands) --- */
.zone {
  position: absolute;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
}

/* While a close-up covers the room, its zones are invisible -- so they must
   stop being targets too. */
.den.scene-on .zone {
  pointer-events: none;
}

/* No hover outline: the room is a painting and a dashed box drawn across it on
   the way past reads as a seam in the art. Keyboard focus still shows, because
   without it a keyboard visitor cannot tell which zone they are on at all --
   and unlike hover, it only ever appears when someone is actually tabbing. */
.zone:focus-visible {
  outline: 2px dashed rgba(255, 220, 180, 0.4);
  outline-offset: 2px;
}

.zone-tv        { left: 30.0%; top: 28.0%; width: 6.5%; height: 10.0%; }
.zone-fireplace { left: 24.4%; top: 38.0%; width: 13.8%; height: 20.0%; }
.zone-rug       { left: 39.1%; top: 55.6%; width: 20.3%; height: 19.4%; }
.zone-couch     { left: 59.4%; top: 42.2%; width: 15.6%; height: 20.0%; }

/* --- Mouse sprite ---
   Height and aspect-ratio are set per pose from JS, since the poses have
   different art dimensions. Anchored by `bottom` so the character's ground
   contact sits on the floor line regardless of how the scene is scaled, and
   centred on `left` so a pose's width never shifts where it stands. */
.mouse {
  position: absolute;
  /* The character is scenery, not a target: without this it paints over the
     zones and swallows clicks whenever it stands in front of one. */
  pointer-events: none;
  transform: translateX(-50%);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
  /* No transition: the walk moves it in hops from JS, one per walk frame. */
}

/* A scene carries its own painted mouse, so the walking one gets out of the way
   the moment the scene lands -- no fade, or the two briefly overlap. */
.den.scene-on .mouse {
  visibility: hidden;
}

/* Five buttons need a little less room each before they will sit two rows deep
   on a phone rather than three. */
@media (max-width: 560px) {
  :root {
    --px: 2px;
  }

  .controlbar {
    gap: 8px 14px;
  }

  .control {
    padding: 7px 10px;
    letter-spacing: 0;
  }

  .controls {
    gap: 8px;
  }

  .holidaybar {
    top: 8px;
    right: 8px;
  }

  .holiday-toggle {
    gap: 8px;
    padding: 6px 9px;
    letter-spacing: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .scene-fade, .tv-movie, html { transition-duration: 0.01ms; }
  /* .decor-set is not listed: it switches with display now, not a transition. */
  .decor-dark, .holiday-knob { transition-duration: 0.01ms; }
}
