/* ============================================================
   ROOM — walls, shapes, floors, doors, coin-slot caches
   Floor classes are `.floor-<id>` where <id> is `floor` in data/rooms.json.
   Adding a floor there requires adding a rule here.
============================================================ */
.room{
  position:absolute;inset:0;
  margin:auto;
  /* WALL THICKNESS. Halved from 10px + a 4px ring.

     The wall is drawn in ROOM space and the camera then magnifies the whole
     room to fill the stage (fitScale() in js/camera.js, up to 2.6x), so the
     smallest rooms — the round observatory, a closet — wore the thickest
     walls on screen: 14 room px became nearly 30. It read as a picture frame
     around a keyhole rather than as a room you are standing in. Everything
     else that must not balloon in a small room divides by --fit; the wall
     deliberately does NOT, because a big room genuinely should have a heavier
     wall than a broom cupboard. It just should not have three times one. */
  border:5px solid var(--wall);
  border-radius:14px;
  box-shadow:0 0 0 2px var(--wall-dark), 0 16px 34px rgba(0,0,0,.5);
  transform-origin:center center;
}
.room .floor{position:absolute;inset:0}
.room.shape-round{border-radius:50%}
.room.shape-round .floor{border-radius:50%}
.room.shape-hex{border:none;box-shadow:none;background:transparent}
.room.shape-hex::before{
  content:"";position:absolute;inset:0;background:var(--wall);
  clip-path:polygon(25% 0,75% 0,100% 50%,75% 100%,25% 100%,0 50%);
  filter:drop-shadow(0 14px 30px rgba(0,0,0,.5));
}
.room.shape-hex .floor{
  /* Matches the rect/round wall above: 5px + the 2px outer ring. */
  inset:7px;
  clip-path:polygon(25% 0,75% 0,100% 50%,75% 100%,25% 100%,0 50%);
}

/* ---------- FLOORS ---------- */
.floor-kitchen{background:repeating-conic-gradient(#d9a06a 0% 25%, #cf9257 0% 50%) 0 0/56px 56px;}
.floor-garage{background:
  linear-gradient(rgba(255,255,255,.03),rgba(0,0,0,.06)),
  repeating-linear-gradient(0deg,#9a9a98 0 110px,#8b8b89 110px 114px),#9a9a98;}
.floor-bathroom{background:repeating-conic-gradient(#bfe0dd 0% 25%, #aed4d0 0% 50%) 0 0/44px 44px;}
.floor-office{background:repeating-linear-gradient(90deg,#a5744a 0 64px,#996a41 64px 66px);}
.floor-bedroom{background:radial-gradient(circle at 30% 30%, #c793a2, #b97f90 70%);}
.floor-closet{background:repeating-linear-gradient(90deg,#6d5138 0 48px,#63482f 48px 50px);}
.floor-attic{background:repeating-linear-gradient(0deg,#8d6a45 0 74px,#7d5c3a 74px 78px);}
.floor-shed{background:
  radial-gradient(circle at 20% 30%, rgba(0,0,0,.06) 4px, transparent 5px) 0 0/70px 70px,
  #9a8a5f;}
.floor-secret{background:radial-gradient(circle at 50% 40%, #7d6a9e, #5d4d7a 75%);}
/* Foam play mat: big interlocking squares, the one floor in the house that is
   deliberately louder than the clutter on it. */
.floor-playroom{background:
  repeating-conic-gradient(#c98a86 0% 25%, #b9776f 0% 50%) 0 0/92px 92px;}
/* Studio parquet, laid on the diagonal. */
.floor-music{background:
  repeating-linear-gradient(45deg,#7a5a3d 0 26px,#6d4f34 26px 52px),#7a5a3d;}
/* Rubber matting, dimpled. */
.floor-mudroom{background:
  radial-gradient(circle at 50% 50%, rgba(0,0,0,.10) 3px, transparent 4px) 0 0/26px 26px,
  #6f6f6a;}
/* Cellar flagstones: staggered courses of cut stone. */
.floor-cellar{background:
  repeating-linear-gradient(0deg,rgba(0,0,0,.22) 0 2px,transparent 2px 58px),
  repeating-linear-gradient(90deg,rgba(0,0,0,.22) 0 2px,transparent 2px 84px),
  #6b6258;}
/* The tower: a dark chamber with the dome's light coming down the middle. */
.floor-observatory{background:
  radial-gradient(circle at 50% 45%, #4a4a76 0%, #33335a 55%, #24243f 100%);}

/* ---------- DREAM FLOORS ----------
   Deeper and cooler than any house floor, so the difference registers before
   you have read a single label. No patterns with straight repeating edges
   except the Hall's: a dream should not look laid out in courses.
   These are still `.floor-<id>` from the room def — floors stay per-room, not
   per-theme, because a theme is a set of rooms and each still wants its own
   ground. See css/themes.css for what the theme itself changes. */
.floor-nebula{background:
  radial-gradient(circle at 32% 28%, rgba(233,160,255,.40) 0%, transparent 46%),
  radial-gradient(circle at 72% 66%, rgba(120,190,255,.34) 0%, transparent 50%),
  radial-gradient(circle at 50% 50%, #3a2b63 0%, #241a45 70%, #170f30 100%);}
/* Dirty ice: a grey-blue field with debris scattered through it. */
.floor-comet{background:
  radial-gradient(circle at 22% 34%, rgba(255,255,255,.13) 3px, transparent 4px) 0 0/64px 64px,
  radial-gradient(circle at 68% 72%, rgba(255,255,255,.09) 2px, transparent 3px) 0 0/47px 47px,
  linear-gradient(160deg, #3d4a6b, #2b3450 70%);}
/* The one built room in the dream: dark stone, and star-points on a grid the
   way a chart is ruled. */
.floor-constellation{background:
  radial-gradient(circle at 50% 50%, rgba(255,244,200,.55) 1.5px, transparent 2.5px) 0 0/58px 58px,
  repeating-linear-gradient(0deg,rgba(255,255,255,.045) 0 1px,transparent 1px 58px),
  repeating-linear-gradient(90deg,rgba(255,255,255,.045) 0 1px,transparent 1px 58px),
  #22203f;}
/* Concentric orbits around a bright centre. */
.floor-orbit{background:
  repeating-radial-gradient(circle at 50% 50%,
    transparent 0 26px, rgba(180,205,255,.16) 26px 28px),
  radial-gradient(circle at 50% 50%, #4b3a72 0%, #2c2150 60%, #1b1436 100%);}

/* ---------- FRAT FLOORS ----------
   These are house floors that something happened to. Each one is a domestic
   surface plus the specific stain that room earned, so the frat reads as a
   place people live in badly rather than as a different building. */
/* Basement concrete, and the rings a cup leaves when it lives on the floor. */
.floor-pong{background:
  radial-gradient(circle at 30% 40%, rgba(120,80,30,.16) 8px, transparent 9px) 0 0/78px 78px,
  radial-gradient(circle at 70% 75%, rgba(120,80,30,.12) 6px, transparent 7px) 0 0/53px 53px,
  linear-gradient(#8f8b82,#7c7871);}
/* Rental carpet, low pile, gone shiny in the traffic lane. */
.floor-pit{background:
  repeating-linear-gradient(45deg, rgba(0,0,0,.05) 0 3px, rgba(255,255,255,.03) 3px 6px),
  linear-gradient(#6f6152,#5f5245);}
/* Laundry-room vinyl: big soft speckle, the cheapest floor in any house. */
.floor-laundry{background:
  radial-gradient(circle at 25% 25%, rgba(255,255,255,.10) 2px, transparent 3px) 0 0/22px 22px,
  radial-gradient(circle at 70% 60%, rgba(0,0,0,.07) 2px, transparent 3px) 0 0/31px 31px,
  #8d8f8a;}
/* The one room with a rug in it, because the trophies live here. */
.floor-trophy{background:
  repeating-linear-gradient(90deg, rgba(0,0,0,.07) 0 14px, transparent 14px 28px),
  radial-gradient(circle at 50% 50%, #6b3d3f, #4e2c2f 78%);}
/* Kitchen lino on the diagonal, worn through to the grey underneath. */
.floor-kitchenette{background:
  repeating-conic-gradient(#b8ac93 0% 25%, #a89c84 0% 50%) 0 0/46px 46px;}
/* Laminate boards. The tidiest floor in the house and nobody walks on it. */
.floor-study{background:
  repeating-linear-gradient(90deg,#9d7c56 0 58px,#916f4b 58px 61px);}
/* Patchy lawn: grass, then the bald spot where the grill stands. */
.floor-yard{background:
  radial-gradient(circle at 62% 58%, rgba(120,100,60,.55) 0%, transparent 34%),
  repeating-linear-gradient(28deg, rgba(0,0,0,.05) 0 5px, rgba(255,255,255,.04) 5px 11px),
  linear-gradient(#5e7a3e,#4c6733);}

/* ---------- SHIP FLOORS ----------
   Cold, lit from inside, and built rather than laid. The palette runs teal
   against the house's browns so a Zorb level is legible from the doorway. */
/* Deck plate: a grid of panels with the seams lit. */
.floor-specimen{background:
  repeating-linear-gradient(0deg, rgba(120,255,230,.10) 0 1px, transparent 1px 46px),
  repeating-linear-gradient(90deg, rgba(120,255,230,.10) 0 1px, transparent 1px 46px),
  radial-gradient(circle at 50% 45%, #1c4a48 0%, #123331 70%, #0b2322 100%);}
/* Frost creeping in from the walls of a hex room. */
.floor-cryo{background:
  radial-gradient(circle at 18% 22%, rgba(200,240,255,.22) 0%, transparent 38%),
  radial-gradient(circle at 82% 78%, rgba(200,240,255,.16) 0%, transparent 42%),
  linear-gradient(#204a5c,#12303c 75%);}
/* Zorb's idea of a carpet, reconstructed from footage. Nearly right. */
.floor-replica{background:
  repeating-conic-gradient(#4a6b5e 0% 25%, #3f5d51 0% 50%) 0 0/64px 64px;}
/* The bright room: a ring of instrument light around a dark centre. */
.floor-instrument{background:
  repeating-radial-gradient(circle at 50% 50%,
    transparent 0 30px, rgba(110,235,215,.13) 30px 32px),
  radial-gradient(circle at 50% 50%, #0e3a38 0%, #0a2726 68%, #061b1a 100%);}
/* Containment: hazard chevrons under a dim floor, because of what is stored. */
.floor-anomaly{background:
  repeating-linear-gradient(135deg, rgba(220,190,60,.10) 0 14px, transparent 14px 28px),
  linear-gradient(#2b3330,#1b2320);}
/* Grow beds seen through standing water. */
.floor-hydro{background:
  repeating-linear-gradient(0deg, rgba(90,220,150,.13) 0 3px, transparent 3px 34px),
  linear-gradient(#17493a,#0e3128 80%);}

/* ---------- TOWER FLOORS ----------
   Stone, and a great deal of candle. Warm where the ship is cold, so the two
   never read as the same "not a house". */
/* Flagstone, laid in a ring the way a round tower is built. */
.floor-library{background:
  repeating-radial-gradient(circle at 50% 50%,
    rgba(0,0,0,.20) 0 2px, transparent 2px 44px),
  repeating-linear-gradient(90deg, rgba(0,0,0,.16) 0 2px, transparent 2px 70px),
  #5a4a63;}
/* Spilled everything, four hundred years of it, on tile. */
.floor-apothecary{background:
  radial-gradient(circle at 34% 62%, rgba(150,230,120,.14) 0%, transparent 24%),
  radial-gradient(circle at 71% 30%, rgba(200,120,220,.14) 0%, transparent 26%),
  repeating-conic-gradient(#5f5068 0% 25%, #55475d 0% 50%) 0 0/50px 50px;}
/* Straw over board. The only soft floor in the tower. */
.floor-roost{background:
  repeating-linear-gradient(72deg, rgba(210,180,90,.16) 0 3px, transparent 3px 9px),
  repeating-linear-gradient(18deg, rgba(210,180,90,.10) 0 2px, transparent 2px 7px),
  #6a5a44;}
/* A dark round room with one thing worth looking at in the middle. */
.floor-scrying{background:
  radial-gradient(circle at 50% 48%, rgba(190,150,255,.34) 0%, transparent 40%),
  radial-gradient(circle at 50% 50%, #3b2b57 0%, #261a3c 62%, #170f28 100%);}
/* Bare boards in a cupboard, and a lot of dust. */
.floor-broom{background:
  repeating-linear-gradient(0deg,#6b543c 0 40px,#5e4933 40px 43px);}
/* Cut slabs and a worn processional line down the middle of the hex. */
.floor-reliquary{background:
  linear-gradient(90deg, transparent 42%, rgba(230,205,140,.10) 50%, transparent 58%),
  repeating-linear-gradient(0deg, rgba(0,0,0,.24) 0 2px, transparent 2px 62px),
  repeating-linear-gradient(90deg, rgba(0,0,0,.24) 0 2px, transparent 2px 88px),
  #4e4550;}

/* ---------- ZOO FLOORS ----------
   Every one of these is an ENCLOSURE floor rather than a room floor: sand,
   sawdust, wet concrete, straw. The zoo is the only theme where the ground is
   the animal's and the tidier is the visitor. */
/* Sand, raked, with bird grit through it. */
.floor-aviary{background:
  repeating-linear-gradient(12deg, rgba(255,255,255,.06) 0 2px, transparent 2px 8px),
  radial-gradient(circle at 40% 60%, rgba(0,0,0,.07) 1.5px, transparent 2.5px) 0 0/19px 19px,
  #b09a6e;}
/* The one built floor: hut boards, scuffed. */
.floor-keeper{background:
  repeating-linear-gradient(90deg,#7d6242 0 52px,#6f563a 52px 55px);}
/* Spilled grain over concrete. */
.floor-feedstore{background:
  radial-gradient(circle at 30% 40%, rgba(225,200,120,.30) 2px, transparent 3px) 0 0/27px 27px,
  radial-gradient(circle at 72% 66%, rgba(225,200,120,.22) 1.5px, transparent 2.5px) 0 0/17px 17px,
  #6e6a61;}
/* Hot rock under a heat lamp: the only floor in the game with a hot spot. */
.floor-reptile{background:
  radial-gradient(circle at 50% 42%, rgba(255,170,70,.26) 0%, transparent 44%),
  repeating-linear-gradient(56deg, rgba(0,0,0,.10) 0 6px, transparent 6px 15px),
  linear-gradient(#6b5340,#54402f);}
/* Sawdust and straw, thick, over a big flat pen. */
.floor-enclosure{background:
  repeating-linear-gradient(38deg, rgba(215,185,110,.20) 0 4px, transparent 4px 12px),
  repeating-linear-gradient(-24deg, rgba(215,185,110,.13) 0 3px, transparent 3px 10px),
  #7a6a4a;}
/* Poured concrete and a vault ring. Nothing lives in here; bananas do. */
.floor-vault{background:
  repeating-radial-gradient(circle at 50% 50%,
    transparent 0 22px, rgba(240,210,90,.16) 22px 25px),
  radial-gradient(circle at 50% 50%, #59544a 0%, #403c35 72%, #2e2b26 100%);}

/* ---------- A FINISHED ROOM ----------
   This used to drop a decorative prop on the floor (a cat, a lit lamp, a
   coffee). It read as clutter you couldn't pick up, which in a game about
   picking things up looks like a bug. Gone. The room itself goes gold
   instead — the walls are already the frame, so they may as well say it. */
/* Static, not a breathe. A finished room is a settled state — it shouldn't
   keep asking for attention, and nothing on screen should pulse forever. */
.room.done{
  border-color:#c9a24a;
  box-shadow:
    0 0 0 2px rgba(245,197,66,.5),
    0 0 26px 6px rgba(245,197,66,.28),
    0 16px 34px rgba(0,0,0,.5);
}
/* Hex rooms draw their walls with a clip-path, not a border. */
.room.shape-hex.done::before{filter:drop-shadow(0 0 14px rgba(245,197,66,.6))}

/* ---------- CACHES (coin-slot boxes) ---------- */
.cache{
  position:absolute;z-index:4;cursor:pointer;
  background:linear-gradient(#544935,#453b28);
  border:2px solid #2e2718;border-radius:6px;
  display:flex;align-items:center;justify-content:center;gap:4px;
  box-shadow:0 3px 6px rgba(0,0,0,.3);
}
.cache{container-type:size}
.cache .cico{font-size:clamp(7px, 55cqh, 12px);opacity:.8;filter:grayscale(.3)}
.cache .slotline{width:11px;height:3px;background:#161009;border-radius:2px}
.cache.drophot{box-shadow:0 0 0 3px rgba(245,197,66,.75), 0 3px 6px rgba(0,0,0,.3)}
.cache.fullhit{animation:furnfull .4s}
.cache.goldhit{animation:furngold .9s}

/* ---------- DOOR LABELS ---------- */
/* --fit is set per room in buildRoomEl(). Everything sized in px inside a
   room divides by it, so the camera's auto-fit doesn't make furniture-scale
   chrome balloon in small rooms.

   WHY THESE MOVE THEMSELVES.

   The obvious fix for a label landing on a furniture name plate is to put it
   outside the room, on the dark background beside its door, where nothing
   competes for the pixels. There is nowhere to put it: the camera's auto-fit
   exists to make the room fill the stage, and measured across all 407 rooms
   the game can generate, the widest gap between the room's edge and the
   stage's is 13px. A label needs about 45.

   So they stay inside and dodge instead. It was not bad luck that they
   collided — the anchors in data/furniture.json are a fixed grid, and the old
   flat 34px inset dropped the N label straight onto the `soft` grid's first
   anchor (33,12) and the E label onto its fourth (56,41), in every round room
   in the game. placeDoorLabels() in js/main.js measures the real boxes after
   layout and nudges each label to the nearest clear spot near its door, via
   the two custom properties below. The CSS still owns the default position;
   JS only ever contributes an offset, so a label with no room to dodge into
   sits exactly where it always did. */
.doorlab{
  position:absolute;
  /* ABOVE THE CLUTTER. Items are z-index 6 and are appended after the labels,
     so at the same index DOM order handed every tie to the clutter — a label
     reading "...NICS" with an emoji sitting on the rest of it. The dodge
     deliberately does NOT treat floor items as obstacles: there are up to 170
     of them, they move on every pickup, and a label that re-homed itself
     every time you tidied something would be worse than one you occasionally
     cannot read. Paint order is the right tool for clutter, the dodge is for
     the furniture, which does not move. 8 clears items and stays under
     nothing except doors, which the dodge already avoids. pointer-events:none
     means the item underneath is still perfectly pickable through it. */
  z-index:8;pointer-events:none;
  font-size:calc(clamp(6px, 1.1vmin, 10px) / var(--fit,1));
  font-weight:800;letter-spacing:.18em;text-transform:uppercase;
  color:rgba(255,255,255,.8);
  text-shadow:0 1px 3px rgba(0,0,0,.7);
  white-space:nowrap;
  /* Its own plate, and now an opaque one. At .42 alpha a label that did land
     on something read as a smudge over it rather than as a sign in front of
     it — and the dodge cannot always win, so the fallback has to be legible
     on its own. */
  padding:calc(2px / var(--fit,1)) calc(6px / var(--fit,1));
  border-radius:999px;
  background:rgba(16,11,6,.86);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.09);
}
/* --nx / --ny are the dodge, in room px, written by placeDoorLabels(). */
.doorlab.N{top:calc(34px / var(--fit,1));left:50%;
  transform:translateX(-50%) translate(var(--nx,0px), var(--ny,0px))}
.doorlab.S{bottom:calc(34px / var(--fit,1));left:50%;
  transform:translateX(-50%) translate(var(--nx,0px), var(--ny,0px))}
.doorlab.E{right:calc(36px / var(--fit,1));top:50%;
  transform:translateY(-50%) translate(var(--nx,0px), var(--ny,0px))}
.doorlab.W{left:calc(36px / var(--fit,1));top:50%;
  transform:translateY(-50%) translate(var(--nx,0px), var(--ny,0px))}

/* ---------- DOORS ---------- */
.door{
  position:absolute;z-index:7;
  background:linear-gradient(#8a623c,#6d4b2c);
  /* EVERY px ON A DOOR DIVIDES BY --fit, edges included.

     The door's size already did (see the .N/.S/.E/.W block below), but its
     3px border and 3px surround did not — so in a room the camera magnifies
     2x the frame came out 12px thick around a 28px door, which is most of
     the door. Same number of on-screen pixels in every room now, and half
     what it was in a small one. */
  border:calc(3px / var(--fit,1)) solid #3a2a17;
  border-radius:calc(5px / var(--fit,1));
  box-shadow:0 0 0 calc(3px / var(--fit,1)) var(--wall-dark),
             0 calc(3px / var(--fit,1)) calc(8px / var(--fit,1)) rgba(0,0,0,.4);
  display:flex;align-items:center;justify-content:center;
  /* The doorknob (a 6px gold dot on ::before) is gone — at this size it read
     as a smudge, not a knob. The directional chevron carries the affordance.

     Same --fit story as the border, and worse: `clamp(9px, 52cqh, 17px)`
     clamps in ROOM px, so in a magnified room 52cqh fell under the 9px floor
     and the chevron was pinned there — 19 on-screen px inside a 28px door,
     a triangle almost as tall as the doorway. A flat fit-normalised size is
     the same arrow in every room, and about half the old one. */
  color:rgba(255,240,210,.95);
  font-size:calc(9px / var(--fit,1));
  font-weight:900;
  text-shadow:0 1px 3px rgba(0,0,0,.6);
  cursor:pointer;
  container-type:size;
}
.door::after{content:"";position:absolute;inset:calc(-10px / var(--fit,1))}
.door.N,.door.S{width:20%;height:calc(28px / var(--fit,1));left:40%}
.door.N{top:calc(-17px / var(--fit,1))}
.door.S{bottom:calc(-17px / var(--fit,1))}
.door.E,.door.W{width:calc(28px / var(--fit,1));height:20%;top:40%}
.door.E{right:calc(-17px / var(--fit,1))}
.door.W{left:calc(-17px / var(--fit,1))}

/* A door you haven't been through yet beckons. Free wayfinding: doors into
   unexplored rooms pull, doors back into tidied ones don't. */
.door.unvisited{animation:doorbeckon 2.6s ease-in-out infinite}
@keyframes doorbeckon{
  50%{box-shadow:0 0 0 calc(3px / var(--fit,1)) var(--wall-dark),
      0 0 calc(14px / var(--fit,1)) calc(3px / var(--fit,1)) rgba(245,197,66,.55),
      0 calc(3px / var(--fit,1)) calc(8px / var(--fit,1)) rgba(0,0,0,.4)}
}

/* ---------- LOCKED DOORS ---------- */
.door.locked{
  background:linear-gradient(#231a10,#150f08);
  color:var(--gold);
  flex-direction:column;gap:calc(4px / var(--fit,1));
  /* The key emoji, not a chevron — bigger than the arrow because it has to be
     identifiable, still fit-normalised so it is the same in every room. */
  font-size:calc(11px / var(--fit,1));
  border-color:rgba(245,197,66,.55);
}
.door.locked.N,.door.locked.S{flex-direction:row;gap:calc(6px / var(--fit,1))}
.door .pips{display:flex;flex-direction:column;gap:calc(3px / var(--fit,1))}
.door.locked.N .pips,.door.locked.S .pips{flex-direction:row}
.door.locked.drophot{box-shadow:0 0 0 calc(3px / var(--fit,1)) rgba(245,197,66,.8)}
.door.locked.goldhit{animation:furngold .9s}
.door.locked.fullhit{animation:furnfull .4s}
