/* ═══════════════════════════════════════════════════════════════════════════
   BRADFORD CARLTON — THE WORLD LAYER
   Rooms, cast, furniture, the hero, the roster and the ROI console.

   HOMEPAGE ONLY. Load it AFTER office.css:
     <link rel="stylesheet" href="/css/office.css">
     <link rel="stylesheet" href="/css/office-world.css">

   Split out of office.css on 2026-08-06, for quarantine rather than bytes.
   The mobile floor-clearance block at the foot of this file overrides inline
   padding with !important, sized for a room that contains characters. Interior
   pages that borrowed .scene inherited 300px of dead space on mobile with no
   way to opt out. Keeping the whole room layer here makes that class of bug
   structurally impossible on the other 43 pages.

   Interior pages that want a character use .cameo (in office.css) — one sprite
   in normal flow, no stage, no floor, no clearance. Do NOT use .scene, .cast,
   .stage or .prop outside the homepage.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The motion switch now lives in office.css and shows on every page, because the
   state it sets is site-wide (html[data-office] + localStorage) and the shared
   nav has to be identical everywhere. Only the mobile sizing stays here-adjacent
   in core; nothing switch-related remains in this file. */

/* ══ THE WORLD — full-bleed rooms, not strips ══════════════════════════════
   A scene is a full-width stage the content sits inside or beside. The previous
   build quarantined the cast in a band at the bottom of each section, which read
   as clip-art trim. Now the room IS the section. */

.scene {
  position: relative; overflow: hidden;
  background:
    linear-gradient(to bottom,
      var(--wall-deep) 0%,
      var(--wall) 40%,
      var(--wall) calc(100% - var(--floor-h, 150px)),
      var(--floor) calc(100% - var(--floor-h, 150px)),
      var(--floor-dark) 100%);
}
/* Warm light pooling from above — what makes a flat band read as an interior. */
.scene::before {
  content: ''; position: absolute; left: 50%; top: -30%;
  width: 130%; height: 150%; transform: translateX(-50%);
  background: radial-gradient(ellipse at 50% 34%, rgba(255,214,150,.15) 0%, rgba(255,214,150,0) 60%);
  pointer-events: none; z-index: 0;
}
/* Floorboards + the skirting line where wall meets floor. */
.scene::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--floor-h, 150px);
  background:
    linear-gradient(to bottom, rgba(0,0,0,.5) 0 3px, rgba(255,220,170,.09) 3px 6px, transparent 6px),
    repeating-linear-gradient(90deg, transparent 0 62px, rgba(0,0,0,.19) 62px 65px);
  pointer-events: none; z-index: 0;
}
.scene > * { position: relative; z-index: 2; }

.tint-lobby  { --wall: #24342B; --wall-deep: #182720; }
.tint-board  { --wall: #1E322C; --wall-deep: #142621; }
.tint-money  { --wall: #2A2C22; --wall-deep: #1D2018; --floor: #7A5836; }
.tint-break  { --wall: #2C2A22; --wall-deep: #201E18; }
.tint-hall   { --wall: #202A2E; --wall-deep: #161E22; }
.tint-cast   { --wall: #1B2A33; --wall-deep: #121D24; }
.tint-doors  { --wall: #26302A; --wall-deep: #1A231D; }
.tint-corner { --wall: #123027; --wall-deep: #0C231C; --floor: #6A4C2E; }

/* ══ THE CAST ══════════════════════════════════════════════════════════════
   Baked WebP strips animated by CSS steps(). Zero runtime JS.

   .cast is a POSITIONED WRAPPER holding the sprite, its name tag and its line.
   The previous build positioned each of those independently by percentage, so
   bubbles drifted away from the character they belonged to. Anchoring all three
   to one parent makes that class of bug impossible. */

.cast {
  position: absolute; bottom: var(--stand, 22px);
  width: var(--u);
  display: flex; flex-direction: column; align-items: center;
  z-index: 3;
}
.cast--sm { --px: 3; }
.cast--bg { --px: 3; }
.cast:has(.says) { z-index: 6; }

.sprite {
  display: block;
  width: var(--u); height: var(--v);
  background-image: var(--sheet); background-repeat: no-repeat;
  background-size: calc(var(--frames) * var(--u)) var(--v);
  background-position-x: calc(var(--from, 0) * var(--u) * -1);
  image-rendering: pixelated;
  filter: drop-shadow(0 4px 0 rgba(0,0,0,.32));
  order: 3;
}
@keyframes step-cycle {
  from { background-position-x: calc(var(--from) * var(--u) * -1); }
  to   { background-position-x: calc((var(--to) + 1) * var(--u) * -1); }
}
.sprite[data-anim] { animation: step-cycle var(--dur, 1.5s) steps(var(--n)) infinite; }
.sprite[data-flip] { transform: scaleX(-1); }

/* Tag and line sit above the sprite in the same flex column, so they travel
   with it at every breakpoint and can never drift. */
.tag {
  order: 2; margin-bottom: 7px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 9.5px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--amber);
  background: rgba(11,17,14,.88); border: 1px solid var(--line-hot);
  padding: 3px 8px; border-radius: 2px; white-space: nowrap;
}
.says {
  order: 1; margin-bottom: 9px;
  font-family: 'Outfit', sans-serif; font-style: italic;
  font-size: 13.5px; line-height: 1.34; text-align: center;
  color: var(--ink); background: var(--cream);
  padding: 8px 13px; border-radius: 3px;
  width: max-content; max-width: 235px;
  position: relative; box-shadow: 4px 4px 0 rgba(0,0,0,.28);
}
.says::after {
  content: ''; position: absolute; left: 50%; bottom: -6px;
  width: 12px; height: 12px; transform: translateX(-50%) rotate(45deg);
  background: var(--cream);
}

/* Furniture — same integer grid as the cast, so people and props share a world. */
.prop {
  position: absolute; bottom: var(--stand, 22px);
  width: calc(var(--fw) * var(--px) * 1px);
  height: calc(var(--fh) * var(--px) * 1px);
  background-image: var(--art); background-size: 100% 100%; background-repeat: no-repeat;
  image-rendering: pixelated; z-index: 2; pointer-events: none;
}
.prop--sm { --px: 3; }
.prop--xs { --px: 2; }
.prop--bg { --px: 3; }
.prop--wall { bottom: auto; top: var(--hang, 14%); }

/* ══ STAGE LAYER ═══════════════════════════════════════════════════════════
   The cast and furniture must anchor to the ROOM's floor, not to wherever their
   wrapper happens to land in the flow. `.scene > *` gives every direct child
   position:relative, which silently turned the zero-height sprite wrapper into
   the positioning context — so characters rendered immediately under the last
   paragraph instead of on the floor, and walked straight through the press
   plaques. An explicit full-bleed layer removes the ambiguity. */
.stage {
  position: absolute; inset: 0;
  z-index: 1; pointer-events: none;
}

/* Cast members are anchored by their CENTRE, so a name tag or spoken line is
   always directly over the character it belongs to. */
.cast { transform: translateX(-50%); }
.coda-orc, .who .sprite { transform: none; }

/* ══ HERO ══════════════════════════════════════════════════════════════════ */

.hero { --floor-h: 200px; min-height: 100svh; display: flex; align-items: center; padding: 8rem 0 210px; }
.hero-grid { display: grid; grid-template-columns: 1.12fr .88fr; gap: 3rem; align-items: end; }
.hero-copy { padding-bottom: 5.5rem; }

/* The one thing people will remember: a REAL photograph of Bradford standing
   inside a 16-bit office, lit the same way. Real and pixel in one frame. */
.hero-photo-stage { position: relative; display: flex; justify-content: center; align-items: flex-end; }
.hero-photo { filter: drop-shadow(0 26px 38px rgba(0,0,0,.62)) contrast(1.04); max-height: 70vh; width: auto; }
.hero-photo-stage::after {
  content: ''; position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%);
  width: 74%; height: 24px; border-radius: 50%;
  background: radial-gradient(ellipse, rgba(0,0,0,.6), transparent 70%);
}
.hero-cast { position: absolute; inset: auto 0 0 0; height: 200px; pointer-events: none; }
@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; gap: 1.5rem; }
  .hero-copy { padding-bottom: 1rem; }
  .hero-photo { max-height: 44vh; }
}

/* Final-CTA photograph, anchored to the floor of the corner office. */
.corner-photo {
  position: absolute; bottom: 0; right: 3%;
  height: 76%; width: auto; max-width: none; z-index: 3;
  filter: drop-shadow(0 20px 30px rgba(0,0,0,.6));
  transform: scaleX(-1);
}

/* ══ MEET THE OFFICE — the shareable moment ════════════════════════════════ */

.roster { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--line); border: 1px solid var(--line); }
@media (max-width: 1000px) { .roster { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px)  { .roster { grid-template-columns: 1fr; } }
.who { background: var(--deep); padding: 1.9rem 1.1rem 1.5rem; text-align: center; transition: background .25s ease; position: relative; }
.who:hover { background: var(--raised); }
.who .sprite { margin: 0 auto .9rem; order: 0; }
.who h4 { font-family: 'Playfair Display', serif; font-weight: 800; font-size: 1.3rem; margin: 0 0 .2rem; letter-spacing: -.02em; }
.who .role { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; letter-spacing: .13em; text-transform: uppercase; color: var(--amber); }
.who .line { margin: .85rem 0 0; font-size: .9rem; font-style: italic; color: var(--cream-dim); line-height: 1.42; }

/* Cross-link: hovering a face highlights their words — three disconnected grids
   become one instrument. Only genuinely-mapped pairs are wired; an unmapped
   portrait stays decorative rather than being given a false attribution.
   NOTE: currently inert by design. The page carries no [data-person] pairs,
   because most pictured clients never gave a quote and wiring them would imply
   an attribution nobody made. Kept, not deleted, so the mechanism survives if
   real pairs are ever supplied. */
.wall-dim .quote:not(.linked) { opacity: .28; }
.wall-dim .face:not(.linked)  { opacity: .32; }

/* ══ ARCADE CONSOLE (the ROI calculator) ═══════════════════════════════════ */

.console { background: var(--cream); color: var(--ink); border-radius: 4px; padding: clamp(1.6rem, 3.4vw, 3rem); box-shadow: 10px 10px 0 var(--pine); }
.dials { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; }
@media (max-width: 800px) { .dials { grid-template-columns: 1fr; gap: 1.4rem; } }
.dial label { display: flex; justify-content: space-between; align-items: baseline; font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: .7rem; }
.dial label b { font-family: 'Playfair Display', serif; font-size: 1.5rem; color: var(--amber-deep); }
.rng {
  -webkit-appearance: none; appearance: none; width: 100%; height: 8px;
  background: linear-gradient(to right, var(--amber-deep) var(--fill, 40%), rgba(20,16,11,.15) var(--fill, 40%));
  outline: none;
}
.rng::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; height: 30px; background: var(--ink); border-radius: 2px; cursor: pointer; box-shadow: 2px 2px 0 rgba(0,0,0,.28); }
.rng::-moz-range-thumb { width: 22px; height: 30px; background: var(--ink); border: 0; border-radius: 2px; cursor: pointer; }
.rng:focus-visible { outline: 3px solid var(--amber-deep); outline-offset: 4px; }

.readouts { display: grid; grid-template-columns: 1fr 1fr; gap: 1.15rem; margin-top: 2.1rem; }
@media (max-width: 640px) { .readouts { grid-template-columns: 1fr; } }
.readout { background: rgba(20,16,11,.05); border: 1px solid rgba(20,16,11,.13); border-radius: 3px; padding: 1.35rem; text-align: center; }
.readout .n { font-family: 'Playfair Display', serif; font-weight: 800; font-size: clamp(2rem, 4.2vw, 3.1rem); line-height: 1; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.readout .cap { font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .11em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: .55rem; }
.readout .sub { font-size: .78rem; color: var(--ink-soft); margin-top: .5rem; }
.stackmeter { display: flex; align-items: flex-end; justify-content: center; gap: 5px; height: 38px; margin-top: .8rem; }
.stackmeter i { display: block; width: 22px; background: var(--amber-deep); border-radius: 1px 1px 0 0; transform-origin: bottom; animation: pop .32s cubic-bezier(.2,1.6,.4,1) both; }
@keyframes pop { from { transform: scaleY(0); } to { transform: scaleY(1); } }
.threshold { max-height: 0; opacity: 0; overflow: hidden; transition: max-height .45s ease, opacity .35s ease .08s; }
.threshold.open { max-height: 280px; opacity: 1; }

/* ══ COMPLIANCE CODA ═══════════════════════════════════════════════════════ */
/* The orc stands in the content flow, not on a stage. */
.coda-in { padding: 2.2rem 0; display: flex; align-items: center; gap: 2rem; }
.coda-orc { flex-shrink: 0; --px: 3; display: block; }
.coda-orc .sprite { width: var(--u); height: var(--v); }
@media (max-width: 620px) { .coda-orc { display: none; } }

/* Slagathore's line sits on --raised, where --cream-faint lands at 4.48:1 — a hair
   under the 4.5:1 AA floor for body text. Lift just this one. */
.coda-in .faint { color: var(--cream-dim); }

/* ══ SIGNATURE MOTION (world) ══════════════════════════════════════════════ */
/* The roster deals itself out like a hand of cards. */
.who { opacity: 0; transform: translateY(22px) scale(.97); transition: opacity .55s ease, transform .55s cubic-bezier(.2,1,.3,1), background .25s ease; }
.roster.in .who { opacity: 1; transform: none; }
.roster.in .who:nth-child(1) { transition-delay: .04s; }
.roster.in .who:nth-child(2) { transition-delay: .11s; }
.roster.in .who:nth-child(3) { transition-delay: .18s; }
.roster.in .who:nth-child(4) { transition-delay: .25s; }
.roster.in .who:nth-child(5) { transition-delay: .32s; }
.roster.in .who:nth-child(6) { transition-delay: .39s; }
.roster.in .who:nth-child(7) { transition-delay: .46s; }
.roster.in .who:nth-child(8) { transition-delay: .53s; }

/* ══ MOTION STATES (world) ═════════════════════════════════════════════════ */

html[data-office="calm"] .sprite { animation: none !important; }
html[data-office="off"] .scene::before,
html[data-office="off"] .scene::after,
html[data-office="off"] .cast,
html[data-office="off"] .prop,
html[data-office="off"] .hero-cast { display: none; }
html[data-office="off"] .scene { background: var(--deep); }
/* The roster IS content, not decoration — it keeps its characters with the lights off,
   but they stop moving, because "off" means no motion anywhere. */
html[data-office="off"] .who .sprite { display: block; animation: none !important; }

@media (prefers-reduced-motion: reduce) {
  .sprite { animation: none !important; }
  .stackmeter i { animation: none !important; }
  .who { transform: none !important; opacity: 1 !important; transition: none !important; }
}

/* ══ RESPONSIVE (world) ════════════════════════════════════════════════════ */

@media (max-width: 980px) {
  :root { --px: 3; }
  .scene { --floor-h: 112px; }
  .hero { --floor-h: 132px; min-height: auto; padding-top: 6rem; }
  .hero-cast { height: 132px; }
}
@media (max-width: 620px) {
  .says { max-width: 175px; font-size: 12px; }
}

/* ══ MOBILE FLOOR CLEARANCE ════════════════════════════════════════════════
   Each room sets its bottom padding inline, sized for desktop. On a narrow screen
   the copy reflows much taller while the cast keeps its own height, so speech
   bubbles started colliding with the paragraph above them. Inline padding can only
   be beaten with !important — the alternative is threading a custom property
   through every section, which is a lot of churn for the same result.

   Budget at --px:3 => sprite 144 + tag/bubble ~70 + stand 22 = ~236px of cast,
   so the floor needs ~300px of clearance before the copy starts.

   THIS BLOCK IS WHY THE WORLD LAYER IS A SEPARATE FILE. It is correct for a room
   with people in it and badly wrong for a short interior page. Keeping it here
   means no page can inherit it by accident. */
@media (max-width: 980px) {
  .scene { padding-bottom: 300px !important; }
  .hero  { padding-bottom: 250px !important; }
  .says  { max-width: 200px; font-size: 12.5px; padding: 7px 11px; }
  .tag   { font-size: 9px; }
}
@media (max-width: 620px) {
  .scene { padding-bottom: 320px !important; }
  .hero  { padding-bottom: 230px !important; }
  .says  { max-width: 172px; font-size: 12px; }
}
