/* Tokens lifted from bascans.com */
:root {
  --white: #ffffff;
  --black: #000000;
  --g3: #dedede;
  --g4: #c4c4c4;
  --g5: #a8a8a8;
  --g6: #909090;
  --ease: cubic-bezier(.16, 1, .3, 1);
  --font: "Satoshi", "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0; overflow: hidden;
  background: var(--black); font-family: var(--font); -webkit-font-smoothing: antialiased;
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  height: 100dvh;                     /* excludes the Safari toolbar */
  height: var(--vh, 100dvh);          /* exact pixels, set by the viewer */
}
/* Browsers that keep the page out of the notch area (Chrome on iPhone in
   landscape) fill it with the page colour: white behind the white intro,
   black once the scan is showing, never a mix of both. */
html:not(.scene), html:not(.scene) body { background: var(--white); }
/* The canvas covers the whole screen, safe areas included, and CSS is the
   only thing that sizes it. Width/height are never set from JS. */
canvas#app {
  display: block; touch-action: none;
  position: fixed; inset: 0;
  width: 100vw !important;
  height: 100dvh !important;
  max-width: none !important; max-height: none !important;
  margin: 0 !important;
  object-fit: fill;
}
@supports not (height: 100dvh) {
  canvas#app { height: var(--vh, 100vh) !important; }
}
/* The white intro must cover the full viewport too, or it leaves a black
   band under it while the toolbar is animating. */
#overlay { position: fixed; inset: 0; width: 100vw; height: 100dvh; }
@supports not (height: 100dvh) { #overlay { height: var(--vh, 100vh); } }

/* Stop iOS long-press selecting anything and popping the Copy / Search bar.
   That popup steals frames and hijacks the touch that should be walking.
   Blanket rule, then text is re-enabled only inside the menu. */
*, *::before, *::after {
  -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
#menu p, #menu a, #menu button { -webkit-user-select: text; user-select: text; }

/* No double-tap zoom, no pinch, no rubber-band scroll on the walkthrough. */
html, body { touch-action: none; overscroll-behavior: none; -webkit-text-size-adjust: 100%; }
#menu, #help { touch-action: pan-y; }

#ver { position: fixed; right: 4px; bottom: 2px; z-index: 60; font: 9px ui-monospace, monospace; color: rgba(255,255,255,.28); pointer-events: none; }
button { font: inherit; cursor: pointer; }

/* ---------------------------------------------------------- overlay */
#overlay {
  position: fixed; inset: 0; z-index: 30; background: var(--white); color: var(--black);
  opacity: 1; transition: opacity .9s var(--ease);
}
#overlay.out { opacity: 0; pointer-events: none; }
/* Low Power Mode, found at the start: top of the intro and loading screens, a red "!" for attention. */
#lp-note {
  position: absolute; z-index: 2; top: max(14px, env(safe-area-inset-top)); left: 50%; transform: translate(-50%, -8px);
  display: none; align-items: center; gap: 10px; width: max-content; max-width: calc(100vw - 32px);
  padding: 9px 16px 9px 10px; border-radius: 999px; background: #fff1f1; border: 1px solid #f3c2c2;
  color: #5a1414; font-size: 14px; line-height: 1.35; text-align: left; pointer-events: none;
}
#lp-note.on { display: flex; animation: lp-in .6s var(--ease) forwards; }
@keyframes lp-in { from { opacity: 0; transform: translate(-50%, -8px); } to { opacity: 1; transform: translate(-50%, 0); } }
#lp-note .lp-mark {
  flex: none; width: 24px; height: 24px; border-radius: 50%; background: #d92d20; color: #fff;
  display: grid; place-items: center; font-weight: 700; font-size: 15px; line-height: 1;
  animation: lp-pulse 1.8s ease-in-out infinite;
}
@keyframes lp-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(217,45,32,.45); } 50% { box-shadow: 0 0 0 7px rgba(217,45,32,0); } }
.screen {
  position: absolute; inset: 0; display: none; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 8vh 24px; gap: 14px;
  animation: rise .9s var(--ease) both;
}
.screen.on { display: flex; }
@keyframes rise { from { opacity: 0; } to { opacity: 1; } }

.wordmark { width: min(62vw, 420px); height: auto; }
.wordmark.small-mark { width: min(38vw, 220px); margin-bottom: 8px; }

.eyebrow { margin: 0; font-size: 12px; letter-spacing: .24em; text-transform: uppercase; color: var(--g5); font-weight: 500; }
h1 { margin: 6px 0 0; font-weight: 400; font-size: clamp(1.8rem, 5vw, 3.2rem); letter-spacing: .04em; }
h2 { margin: 0; font-weight: 400; font-size: clamp(1.3rem, 3.4vw, 2rem); letter-spacing: .02em; }
.small { margin: 0; color: var(--g6); font-size: 15px; font-weight: 400; max-width: 34ch; }
.label { margin: 0; font-size: 11px; letter-spacing: .22em; text-transform: uppercase; color: var(--g5); }
.tap { position: absolute; bottom: max(28px, env(safe-area-inset-bottom)); left: 0; right: 0; margin: 0;
       font-size: 11px; letter-spacing: .22em; text-transform: uppercase; color: #4a4a4a; font-weight: 500; animation: blink 2.4s ease-in-out infinite; }
@keyframes blink { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }

.attrib {
  position: absolute; left: 0; right: 0; bottom: max(8px, env(safe-area-inset-bottom)); margin: 0;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--g5); text-align: center; pointer-events: none;
}
#s-name .tap, #s-how .tap, #s-power .tap { bottom: max(52px, calc(env(safe-area-inset-bottom) + 32px)); }

/* Low Power Mode slide (phones and tablets): the iPhone's Low Power Mode
   switch, drawn in black and white, turning itself off over and over. */
.pw-switch {
  position: relative; width: 64px; height: 38px; margin: 22px 0 26px;
  border: 2px solid var(--black); border-radius: 999px;
  animation: pw-track 2.8s linear infinite;
}
.pw-switch .knob {
  position: absolute; top: 4px; left: 4px; width: 26px; height: 26px; border-radius: 999px;
  background: var(--white); border: 2px solid var(--black);
  transform-origin: left center;
  animation: pw-knob 2.8s linear infinite;
}
/* On (filled), a glide off (empty), a pause, then back on. The fill fades with
   the travel, and the knob stretches a little as it goes, the way the iPhone's
   own switch does. */
@keyframes pw-track {
  0%, 20% { background-color: #000; }
  48%, 80% { background-color: #fff; }
  100% { background-color: #000; }
}
@keyframes pw-knob {
  0%, 20%  { transform: translateX(26px) scaleX(1);    animation-timing-function: cubic-bezier(.36, 0, .5, .4); }
  28%      { transform: translateX(22px) scaleX(1.07); animation-timing-function: cubic-bezier(.3, 0, .5, 1); }
  36%      { transform: translateX(14px) scaleX(1.1);  animation-timing-function: linear; }
  42%      { transform: translateX(6px)  scaleX(1.09); animation-timing-function: cubic-bezier(.25, .7, .35, 1); }
  48%, 80% { transform: translateX(0) scaleX(1);       animation-timing-function: cubic-bezier(.36, 0, .5, .4); }
  86%      { transform: translateX(5px)  scaleX(1.08); animation-timing-function: linear; }
  93%      { transform: translateX(17px) scaleX(1.09); animation-timing-function: cubic-bezier(.25, .7, .35, 1); }
  100%     { transform: translateX(26px) scaleX(1); }
}
.power-line b { font-weight: 700; }
.power-line { margin: 0; font-size: clamp(1rem, 2.8vw, 1.45rem); font-weight: 400; letter-spacing: .01em; line-height: 1.35; max-width: 24ch; }
/* Desktop: the intro slides' prompt sits just under the content, twice the size, in black. */
#overlay .tap.desk-tap { position: static; margin-top: 36px; font-size: 22px; color: var(--black); }

/* instruction diagrams */
.how { display: flex; gap: clamp(28px, 8vw, 72px); margin: 6px 0 4px; }
.how-item p { margin: 12px 0 0; font-size: 14px; color: var(--black); }
/* The phone is drawn held sideways, the way the walkthrough is used. */
.phone { position: relative; width: 132px; height: 78px; border: 1.5px solid var(--black); border-radius: 14px; margin: 0 auto; }
.phone::before { content: ""; position: absolute; left: 8px; top: 50%; width: 3px; height: 22px; margin-top: -11px; border-radius: 2px; background: var(--g4); }
.phone::after { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--g3); }
.thumb { position: absolute; width: 18px; height: 18px; border-radius: 50%; background: var(--black); top: 30px; }
/* walk = LEFT half of the phone, drags in the direction you travel.
   look = RIGHT half, sweeps side to side. */
.thumb.walk { left: 30px; animation: walkAnim 2.2s var(--ease) infinite; }
.thumb.look { left: 84px; animation: lookAnim 2.2s var(--ease) infinite; }
@keyframes lookAnim { 0% { transform: translateX(-9px); opacity: .2; } 15% { opacity: 1; } 50% { transform: translateX(9px); opacity: 1; } 68% { opacity: .2; } 100% { transform: translateX(-9px); opacity: .2; } }
/* Tours: a figure walks up to a doorway and goes through. Shown only on tours. */
html:not(.tour) .tour-only { display: none !important; }
.doorway { position: relative; width: 132px; height: 78px; margin: 0 auto; }
.doorway .floor { position: absolute; left: 6px; right: 6px; bottom: 8px; height: 1px; background: var(--g3); }
.doorway .frame { position: absolute; right: 24px; bottom: 8px; width: 30px; height: 58px; border: 1.5px solid var(--black); border-bottom: 0; border-radius: 3px 3px 0 0; }
.doorway .walker { position: absolute; left: 12px; bottom: 14px; width: 16px; height: 16px; border-radius: 50%; background: var(--black); animation: approach 2.6s var(--ease) infinite; }
@keyframes approach { 0% { transform: translateX(0); opacity: .2; } 15% { opacity: 1; } 60% { transform: translateX(78px); opacity: 1; } 78% { transform: translateX(84px); opacity: 0; } 100% { transform: translateX(0); opacity: 0; } }
@keyframes walkAnim { 0% { transform: translateY(12px); opacity: .2; } 18% { opacity: 1; } 62% { transform: translateY(-14px); opacity: 1; } 82% { opacity: .2; } 100% { transform: translateY(12px); opacity: .2; } }
.desk-only { display: none; color: var(--g6); font-size: 14px; margin: 4px 0 0; }
.desk-only b { color: var(--black); font-weight: 500; letter-spacing: .1em; }
@media (hover: hover) and (pointer: fine) { .desk-only { display: block; } .how, .mob-only { display: none !important; } .keys.desk-only { display: flex; } }
/* Phones: the one line that says what this is. */
.intro-line { margin: 0 0 6px; max-width: 30ch; font-size: clamp(1.15rem, 3.6vw, 1.6rem); font-weight: 400; line-height: 1.4; letter-spacing: .01em; color: var(--black); }

/* The loading screen's way in (phones, first load). Its space is kept, it only fades. */
#s-load .enter-tap { opacity: 0; animation: none; transition: opacity .6s var(--ease); }
#s-load.ready .enter-tap { opacity: 1; animation: blink 2.4s ease-in-out .6s infinite; }
#s-load .small { transition: opacity .6s var(--ease); }
#s-load.ready .small { opacity: 0; }

/* Desktop: the keys that walk, large, pressed in turn. */
.keys { align-items: center; gap: clamp(20px, 4vw, 40px); margin: 12px 0 8px; }
.key-set { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.key-row { display: flex; gap: 8px; }
.key {
  width: 60px; height: 60px; display: grid; place-items: center; font-style: normal; font-size: 21px; font-weight: 500;
  color: var(--black); background: var(--white); border: 1.5px solid var(--black); border-radius: 12px;
  box-shadow: 0 5px 0 var(--g4); animation: keypress 4.8s var(--ease) infinite;
}
.key.k2 { animation-delay: .6s; } .key.k3 { animation-delay: 1.2s; } .key.k4 { animation-delay: 1.8s; }
.key.k5 { animation-delay: 2.4s; } .key.k6 { animation-delay: 3s; } .key.k7 { animation-delay: 3.6s; } .key.k8 { animation-delay: 4.2s; }
@keyframes keypress {
  0%, 12%, 100% { transform: translateY(0); box-shadow: 0 5px 0 var(--g4); background: var(--white); color: var(--black); }
  3%, 9% { transform: translateY(5px); box-shadow: 0 0 0 var(--g4); background: var(--black); color: var(--white); }
}
.keys-or { font-size: 12px; letter-spacing: .22em; text-transform: uppercase; color: var(--g6); }
/* The door animation from the phone slide, below the keys (tours). */
.key-door.desk-only { margin: 10px 0 4px; }
.key-door p { color: var(--black); }

.rotate { width: 42px; height: 72px; border: 1.5px solid var(--black); border-radius: 8px; animation: rot 2.6s var(--ease) infinite; }
@keyframes rot { 0%, 20% { transform: rotate(0); } 55%, 80% { transform: rotate(-90deg); } 100% { transform: rotate(0); } }

/* loading bar */
.bar-wrap { width: min(70vw, 360px); height: 2px; background: var(--g3); margin-top: 18px; overflow: hidden; }
/* Once the room is in, the bar fades away; its space is kept, so nothing moves. */
.bar-wrap { transition: opacity .6s var(--ease); }
#s-load.done .bar-wrap { opacity: 0; }
#bar { width: 0%; height: 100%; background: var(--black); transition: width .5s var(--ease); }
/* Its space is always kept, so nothing moves when it appears: it only fades. */
/* Fades in, goes at once. */
#bar-label { min-height: 1.6em; opacity: 0; }
#bar-label.on { opacity: 1; transition: opacity .6s var(--ease); }
/* The slow-connection note: a plain sentence, easy to read. */
#bar-label.note { text-transform: none; letter-spacing: .01em; font-size: 14px; color: #4a4a4a; max-width: 34ch; line-height: 1.45; }

/* ---------------------------------------------------------- menu */
#menu-btn {
  position: fixed; top: max(14px, env(safe-area-inset-top)); right: max(14px, env(safe-area-inset-right)); z-index: 40;
  width: 44px; height: 44px; border-radius: 50%; border: 0; background: rgba(0,0,0,.35); backdrop-filter: blur(8px);
  display: none; flex-direction: column; align-items: center; justify-content: center; gap: 4px; opacity: 0; transition: opacity .6s var(--ease), transform .3s var(--ease);
}
#menu-btn.on { display: flex; opacity: .85; }
#menu-btn i { display: block; width: 4px; height: 4px; border-radius: 50%; background: var(--white); transition: transform .3s var(--ease), opacity .3s; }
#menu-btn.x i:nth-child(1) { transform: translateY(8px) rotate(45deg) scaleX(4.5); border-radius: 2px; }
#menu-btn.x i:nth-child(2) { opacity: 0; }
#menu-btn.x i:nth-child(3) { transform: translateY(-8px) rotate(-45deg) scaleX(4.5); border-radius: 2px; }

#menu {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 35; width: min(86vw, 340px);
  background: rgba(255,255,255,.96); backdrop-filter: blur(14px); color: var(--black);
  display: flex; flex-direction: column; gap: 4px; padding: calc(max(14px, env(safe-area-inset-top)) + 58px) 28px 28px;
  transform: translateX(100%); transition: transform .6s var(--ease);
  /* Landscape phones are too short for every item: the panel scrolls, and the
     items keep their size rather than squashing to fit. */
  overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
}
#menu > * { flex-shrink: 0; }
#menu.on { transform: none; }
@media (orientation: landscape) and (max-height: 500px) {
  #menu { padding-top: calc(max(10px, env(safe-area-inset-top)) + 50px); padding-bottom: max(18px, env(safe-area-inset-bottom)); }
}
#menu .eyebrow { margin-bottom: 14px; }
#menu button, #menu a {
  display: block; width: 100%; text-align: left; padding: 14px 0; border: 0; border-bottom: 1px solid var(--g3); background: none;
  color: var(--black); font-size: 15px; text-decoration: none;
}
#menu a { margin-top: 10px; border: 1.5px solid var(--black); border-radius: 999px; text-align: center; padding: 14px 18px; font-size: 12px; letter-spacing: .22em; text-transform: uppercase; }
#menu .attrib { position: static; margin-top: auto; text-align: left; padding-top: 18px; }
#menu .credit { margin: 6px 0 0; font-size: 11px; color: var(--g5); }

/* Enquire: top left, opposite the menu button, and only while it is showing.
   Small and see-through so it never gets in the way of the scan. */
#enquire {
  position: fixed; top: max(14px, env(safe-area-inset-top)); left: max(14px, env(safe-area-inset-left)); z-index: 40;
  display: none; align-items: center; height: 32px; padding: 0 13px; border-radius: 8px;
  background: rgba(0,0,0,.35); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  color: var(--white); font-size: 11px; font-weight: 500; letter-spacing: .16em; text-transform: uppercase; text-decoration: none;
  opacity: .85; transition: opacity .3s var(--ease);
}
#enquire:hover { opacity: 1; }
#menu-btn.on ~ #enquire { display: flex; }
html.measuring #enquire { display: none; }
/* Detail: Fast / Balanced / Quality */
#menu .m-quality { display: flex; gap: 6px; padding: 14px 0; border-bottom: 1px solid var(--g3); }
#menu .m-quality button { flex: 1; width: auto; text-align: center; padding: 9px 4px; border: 1px solid var(--g3); border-radius: 999px; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--g6); }
#menu .m-quality button.on { border-color: var(--black); background: var(--black); color: var(--white); }

#help {
  position: fixed; inset: 0; z-index: 38; display: none; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  background: rgba(255,255,255,.94); color: var(--black); text-align: center; padding: 24px;
}
#help.on { display: flex; }
#help .tap { color: #4a4a4a; }

/* Scan-build selector: bottom right, deliberately quiet. */
#variant { display: none; position: fixed; z-index: 44;
  right: max(10px, env(safe-area-inset-right)); bottom: max(10px, env(safe-area-inset-bottom)); }
#variant.on { display: block; }
#variant select {
  font-family: var(--font); font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255,255,255,.62); background: rgba(0,0,0,.42); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,.18); border-radius: 999px;
  padding: 6px 10px; appearance: none; -webkit-appearance: none; outline: none; max-width: 46vw;
}
#variant select:focus { color: #fff; border-color: rgba(255,255,255,.4); }
#variant option { color: #000; background: #fff; text-transform: none; letter-spacing: 0; font-size: 13px; }

#toast {
  position: fixed; left: 50%; bottom: 8vh; transform: translate(-50%, 8px); z-index: 45; opacity: 0; pointer-events: none;
  padding: 10px 18px; border-radius: 22px; background: rgba(0,0,0,.72); color: var(--white); font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  /* Longer notices (Low Power Mode) wrap onto a second line inside the screen. */
  width: max-content; max-width: min(86vw, 560px); text-align: center; line-height: 1.5;
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
#toast.on { opacity: 1; transform: translate(-50%, 0); }

/* Low Power Mode found: a very small line, bottom middle between the joysticks,
   no background, all through the walkthrough (the scan only: html.scene). */
#lp-bar {
  position: fixed; left: 50%; bottom: max(8px, env(safe-area-inset-bottom)); transform: translateX(-50%); z-index: 20;
  margin: 0; max-width: 46vw; text-align: center; pointer-events: none; display: none;
  font-size: 10px; line-height: 1.4; letter-spacing: .04em; color: rgba(255,255,255,.72);
  text-shadow: 0 1px 2px rgba(0,0,0,.7);
}
html.scene #lp-bar.on { display: block; }

/* ---------------------------------------------------------- joysticks (phones and tablets) */
/* Drawn only: the whole left or right half still takes the thumb. The knob
   follows the thumb, and springs back when it lifts. */
.joy {
  --size: 92px;
  position: fixed; z-index: 20; bottom: max(22px, calc(env(safe-area-inset-bottom) + 10px));
  width: var(--size); height: var(--size); border-radius: 50%;
  border: 1.5px solid rgba(255,255,255,.95); background: rgba(0,0,0,.25);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .5s var(--ease), transform .5s var(--ease), visibility 0s linear .5s;
}
#joy-l { left: max(26px, calc(env(safe-area-inset-left) + 12px)); }
#joy-r { right: max(26px, calc(env(safe-area-inset-right) + 12px)); }
/* iPad: the corners are out of a thumb's reach and leave no room to drag
   left or down, so both sit further in and higher up (v84). */
html.ipad .joy { bottom: max(160px, 14vw, calc(env(safe-area-inset-bottom) + 140px)); }   /* same gap as the sides */
html.ipad #joy-l { left: max(160px, 14vw, calc(env(safe-area-inset-left) + 140px)); }
html.ipad #joy-r { right: max(160px, 14vw, calc(env(safe-area-inset-right) + 140px)); }
.joy .knob {
  position: absolute; left: 50%; top: 50%; width: 38px; height: 38px; margin: -19px 0 0 -19px; border-radius: 50%;
  background: rgba(255,255,255,.95); transition: transform .35s var(--ease);
}
.joy.on { opacity: .25; visibility: visible; transition: opacity .5s var(--ease), transform .5s var(--ease), visibility 0s; }
.joy.on.held { opacity: .5; }
.joy.on.held .knob { transition: none; }
/* Just in: lit up for a moment so they are noticed. */
.joy.on.hi { opacity: .95; transform: scale(1.08); }
.joy-tip {
  position: absolute; bottom: calc(100% + 12px); left: 50%; transform: translate(-50%, 6px);
  padding: 8px 14px; border-radius: 999px; background: rgba(0,0,0,.72); color: var(--white);
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase; white-space: nowrap;
  opacity: 0; transition: opacity .4s var(--ease), transform .4s var(--ease);
}
/* The tip stays readable while its joystick sits at 25%. */
.joy.on.tip { opacity: .55; }
.joy.on.tip.hi { opacity: .95; }
.joy.tip .joy-tip { opacity: 1; transform: translate(-50%, 0); }
#joy-l .joy-tip { left: 0; transform: translate(0, 6px); }
#joy-l.tip .joy-tip { transform: none; }
#joy-r .joy-tip { left: auto; right: 0; transform: translate(0, 6px); }
#joy-r.tip .joy-tip { transform: none; }

/* ---------------------------------------------------------- doors (tours) */
#door {
  position: fixed; left: 50%; bottom: max(12vh, calc(env(safe-area-inset-bottom) + 28px)); transform: translate(-50%, 10px); z-index: 44;
  padding: 13px 34px; border: 1.5px solid rgba(255,255,255,.9); border-radius: 999px; background: rgba(0,0,0,.38); backdrop-filter: blur(8px);
  color: var(--white); font-size: 12px; letter-spacing: .24em; text-transform: uppercase;
  opacity: 0; pointer-events: none; transition: opacity .6s var(--ease), transform .6s var(--ease);
}
#door.on { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
/* Single joystick (tour.json phonePortrait, phones held upright): one ring in
   the bottom middle, and the door button moves up to the top middle. */
html.solo #joy-r { display: none; }
html.solo #joy-l { left: 50%; margin-left: calc(var(--size) / -2); bottom: max(44px, calc(env(safe-area-inset-bottom) + 30px)); }
html.solo #joy-l .joy-tip, html.solo #joy-l.tip .joy-tip { left: 50%; transform: translate(-50%, 0); }
html.ipad.solo #joy-l { bottom: max(120px, calc(env(safe-area-inset-bottom) + 100px)); }
html.solo #door { top: max(12vh, calc(env(safe-area-inset-top) + 28px)); bottom: auto; }
/* Held upright: in the middle of the screen (v105). */
@media (orientation: portrait) { html.solo #door { top: calc(50% - 22px); } }

/* Between rooms: the loading screen pared back to the room and the time left */
/* One plain screen: the room being loaded, a bar, the time left. The same
   screen fades in over the room being left and out onto the next, in one page,
   so nothing moves in between. */
html.hop .screen { display: none !important; }
html.hop #s-load { display: flex !important; animation: none; }
html.hop #s-load .small { display: none !important; }
html.hop #s-load .wordmark.small-mark { width: min(22vw, 110px); margin: 0 0 8px; }
html.hop #load-title { margin: 0; font-size: 20px; font-weight: 400; }
html.hop #overlay { transition: opacity .7s var(--ease); }

/* ---------------------------------------------------------- diagnostics */
#hud {
  display: none; position: fixed; top: 0; left: 0; z-index: 50; margin: 0; padding: 8px 10px; pointer-events: none;
  font: 11px/1.5 ui-monospace, Menlo, Consolas, monospace; color: #0f0; background: rgba(0,0,0,.68); white-space: pre;
}
#hud.on { display: block; } #hud.warn { color: #fc0; } #hud.bad { color: #f44; }
#dbg { display: none; position: fixed; left: 8px; bottom: max(8px, env(safe-area-inset-bottom)); z-index: 50; gap: 6px; flex-wrap: wrap; max-width: 60vw; }
#dbg.on { display: flex; }
#dbg button { padding: 8px 10px; border: 1px solid #0f0; background: rgba(0,0,0,.75); color: #0f0; font: 11px ui-monospace, monospace; border-radius: 4px; }

/* ---------------------------------------------------------- how to move (menu) */
/* The start's instructions again, drawings included. Fits a phone held sideways. */
#help { gap: 10px; overflow-y: auto; justify-content: safe center; padding: max(18px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right)) max(18px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left)); }
#help .how { margin: 4px 0 2px; }
#help .intro-line { max-width: 40ch; font-size: clamp(1rem, 2.6vw, 1.3rem); }
#help .help-line { margin: 0; max-width: 52ch; font-size: 14px; line-height: 1.5; color: var(--g6); }
#help .help-what { color: var(--black); font-size: 16px; }
#help .tap { position: static; margin-top: 8px; }
@media (hover: hover) and (pointer: fine) { #help .help-line { display: none; } }

/* ---------------------------------------------------------- measure */
#measure-ov { position: fixed; inset: 0; width: 100vw; height: 100dvh; z-index: 15; pointer-events: none; display: none; }
#measure-ov.on { display: block; }
#measure-bar {
  position: fixed; top: max(14px, env(safe-area-inset-top)); left: 50%; transform: translate(-50%, -8px); z-index: 42;
  display: flex; align-items: center; gap: 6px; padding: 5px 5px 5px 18px; border-radius: 999px;
  background: rgba(0,0,0,.62); backdrop-filter: blur(8px); color: var(--white);
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity .4s var(--ease), transform .4s var(--ease);
}
#measure-bar.on { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
#measure-msg { margin-right: 8px; }
#measure-bar button {
  border: 1px solid rgba(255,255,255,.55); background: none; color: var(--white); border-radius: 999px;
  padding: 8px 14px; font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
}
#measure-bar #measure-done { background: var(--white); color: var(--black); border-color: var(--white); }
/* Desktop: the pointer shows where the next point lands. */
html.measuring canvas#app { cursor: crosshair; }

/* ---------------------------------------------------------- slow walkthrough tip */
#perf-tip {
  position: fixed; z-index: 41;
  top: calc(max(14px, env(safe-area-inset-top)) + 56px); right: max(14px, env(safe-area-inset-right));
  max-width: min(70vw, 260px); padding: 11px 16px; border: 0; border-radius: 14px;
  background: rgba(0,0,0,.78); backdrop-filter: blur(8px); color: var(--white); text-align: left;
  font-size: 13px; line-height: 1.4; letter-spacing: .01em;
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity .5s var(--ease), transform .5s var(--ease), visibility 0s linear .5s;
}
/* The point, under the three dots. */
#perf-tip::before { content: ""; position: absolute; top: -7px; right: 16px; border: 7px solid transparent; border-top: 0; border-bottom-color: rgba(0,0,0,.78); }
#perf-tip.on { opacity: 1; visibility: visible; transform: none; transition: opacity .5s var(--ease), transform .5s var(--ease), visibility 0s; }

/* Recording a walkthrough (?debug=1): only the scan shows. */
html.film body * { visibility: hidden !important; }
html.film #app { visibility: visible !important; }

/* The notice for a device that cannot run the scan (v106): a loading bar for the film, then black, then the film. */
#video-bar { width: 0%; height: 100%; background: var(--black); transition: width .5s var(--ease); }
#video-black { position: fixed; inset: 0; z-index: 70; background: #000; opacity: 0; pointer-events: none; transition: opacity .9s var(--ease); }
#video-black.on { opacity: 1; }
/* Walkthrough only: nothing to go back to. */
#fly.only #fly-exit { display: none; }
#fly.dragging { cursor: ew-resize; }
#fly video { touch-action: none; }

/* ---------------------------------------------------------- fly-through */
/* The player: the film over everything, controls along the bottom as on YouTube. */
#fly {
  position: fixed; inset: 0; z-index: 60; background: #000; color: var(--white);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .35s var(--ease), visibility 0s linear .35s;
}
#fly.on { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity .35s var(--ease), visibility 0s; }
#fly video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
#fly-exit {
  position: absolute; top: max(14px, env(safe-area-inset-top)); left: max(14px, env(safe-area-inset-left));
  display: flex; align-items: center; gap: 6px; height: 40px; padding: 0 16px 0 10px; border: 0; border-radius: 999px;
  background: rgba(0,0,0,.5); backdrop-filter: blur(8px); color: var(--white);
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
}
#fly-exit svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
#fly-bar {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 28px max(16px, env(safe-area-inset-right)) max(10px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  background: linear-gradient(to top, rgba(0,0,0,.7), rgba(0,0,0,0));
}
#fly-exit, #fly-bar { transition: opacity .4s var(--ease); }
/* Playing and left alone: the controls fade, as YouTube's do. */
#fly.idle #fly-exit, #fly.idle #fly-bar { opacity: 0; pointer-events: none; }
#fly.idle { cursor: none; }
/* A tall strip, easy to catch with a thumb; the line itself is thin. */
#fly-track { position: relative; height: 22px; cursor: pointer; touch-action: none; }
#fly-rail { position: absolute; left: 0; right: 0; top: 50%; height: 3px; margin-top: -1.5px; background: rgba(255,255,255,.28); border-radius: 2px; overflow: hidden; transition: height .15s, margin-top .15s; }
#fly-track:hover #fly-rail, #fly.scrubbing #fly-rail { height: 5px; margin-top: -2.5px; }
#fly-buf { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: rgba(255,255,255,.35); }
#fly-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--white); }
/* Where each room's clip starts. */
#fly-marks i { position: absolute; top: 0; bottom: 0; width: 3px; margin-left: -1.5px; background: rgba(0,0,0,.75); }
#fly-knob { position: absolute; top: 50%; left: 0; width: 13px; height: 13px; margin: -6.5px 0 0 -6.5px; border-radius: 50%; background: var(--white); }
#fly-row { display: flex; align-items: center; gap: 12px; height: 40px; }
#fly-play { width: 40px; height: 40px; margin-left: -8px; border: 0; background: none; color: var(--white); display: flex; align-items: center; justify-content: center; }
#fly-play svg { width: 26px; height: 26px; fill: currentColor; }
#fly-play .i-pause, #fly.playing #fly-play .i-play { display: none; }
#fly.playing #fly-play .i-pause { display: block; }
#fly-time { font-size: 12px; font-variant-numeric: tabular-nums; letter-spacing: .04em; }
#fly-room { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; opacity: .8; }

/* ---------------------------------------------------------- zoomed in */
/* Small and quiet: the level, and how to come back out. Tapping it resets. */
#zoom-note {
  position: fixed; top: max(12px, env(safe-area-inset-top)); left: 50%; z-index: 41;
  display: flex; align-items: baseline; gap: 7px; padding: 4px 11px; border: 0; border-radius: 999px;
  background: rgba(0,0,0,.5); backdrop-filter: blur(6px); color: var(--white);
  font-size: 10px; line-height: 1.3; letter-spacing: .06em; white-space: nowrap;
  opacity: 0; visibility: hidden; pointer-events: none; transform: translate(-50%, -6px);
  transition: opacity .3s var(--ease), transform .3s var(--ease), visibility 0s linear .3s;
}
#zoom-note.on { opacity: 1; visibility: visible; pointer-events: auto; transform: translate(-50%, 0); transition: opacity .3s var(--ease), transform .3s var(--ease), visibility 0s; }
#zoom-lvl { font-weight: 500; }
#zoom-how { opacity: .7; }
/* Measuring has its bar up there: sit just under it. */
html.measuring #zoom-note { top: calc(max(14px, env(safe-area-inset-top)) + 52px); }

/* The menu gives its buttons a display, so hiding one needs saying twice. */
#menu [hidden], #measure-bar [hidden] { display: none !important; }

/* v117: a long load condenses and shows one tip (viewer.js showTip). */
#s-load .tip-ul, #s-load .tip-room, #s-load .load-tip { display: none; }
#s-load .wordmark.small-mark { transition: width .9s var(--ease), margin .9s var(--ease); }
#s-load #load-title, #s-load .small, #s-load #bar-label { transition: opacity .4s var(--ease); }
#s-load.tips-out #load-title, #s-load.tips-out .small, #s-load.tips-out #bar-label { opacity: 0; }
#s-load.tips #load-title, #s-load.tips .small, #s-load.tips #bar-label { display: none !important; }
#s-load.tips .wordmark.small-mark, html.hop #s-load.tips .wordmark.small-mark { width: min(30vw, 128px); clip-path: inset(0 0 22% 0); margin: 0 0 -6px; }
#s-load.tips .tip-ul { display: block; height: 1.5px; width: 18px; margin: 4px auto 6px; background: var(--black); animation: tip-breathe 2.4s ease-in-out infinite; }
@keyframes tip-breathe { 0%, 100% { width: 16px; } 50% { width: 44px; } }
#s-load.tips .tip-room { display: block; margin: 0; animation: tip-in .7s var(--ease) both; }
#s-load .bar-wrap { transition: opacity .6s var(--ease), width .9s var(--ease); }
#s-load.tips .bar-wrap { width: min(34vw, 130px); margin-top: 2px; }
#s-load.tips .load-tip { display: flex; flex-direction: column; align-items: center; margin-top: 30px; max-width: 300px; animation: tip-in .8s var(--ease) .1s both; }
@keyframes tip-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.tip-ic { flex: 0 0 auto; width: 52px; height: 52px; border-radius: 50%; border: 1.5px solid var(--black); display: flex; align-items: center; justify-content: center; margin-bottom: 16px; color: var(--black); }
.tip-ey { margin: 0 0 8px; font-size: 11px; letter-spacing: .24em; text-transform: uppercase; color: var(--g6); font-weight: 500; }
.tip-tx { margin: 0; font-size: 18px; line-height: 1.45; color: var(--black); font-weight: 400; }
.tip-stats { display: flex; justify-content: center; gap: 18px; }
.tip-stats div { font-size: 12px; color: var(--g6); }
.tip-stats b { display: block; font-size: 24px; font-weight: 500; color: var(--black); }
.tip-sub { margin: 10px 0 0; font-size: 13px; color: var(--g6); }
/* Held sideways: logo, room and bar on the left, the tip on the right. */
@media (orientation: landscape) and (max-height: 520px) {
  html #s-load.tips, html.hop #s-load.tips { display: grid !important; grid-template-columns: 1fr 1.3fr; align-content: center; align-items: center; justify-items: center; row-gap: 6px; padding: 6vh 6vw; }
  #s-load.tips > * { grid-column: 1; }
  #s-load.tips .load-tip { grid-column: 2; grid-row: 1 / span 5; justify-self: stretch; flex-direction: row; text-align: left; gap: 18px; margin: 0; max-width: 440px; padding-left: 5vw; border-left: 1px solid var(--g3); }
  #s-load.tips .tip-ic { margin: 0; }
  #s-load.tips .tip-stats { justify-content: flex-start; }
}
/* v117: Go to room - the rooms open under the button, the current one marked. */
#menu #m-rooms { position: relative; }
#menu #m-rooms::after { content: ""; position: absolute; right: 4px; top: 50%; width: 7px; height: 7px; margin-top: -6px; border-right: 1.5px solid var(--black); border-bottom: 1.5px solid var(--black); transform: rotate(45deg); transition: transform .3s var(--ease); }
#menu #m-rooms[aria-expanded="true"]::after { transform: rotate(-135deg); margin-top: -2px; }
#menu .m-rooms { border-bottom: 1px solid var(--g3); padding: 4px 0 8px; }
#menu .m-rooms button { padding: 10px 0 10px 14px; border: 0; font-size: 14px; color: var(--g6); }
#menu .m-rooms button.here { color: var(--black); }
#menu .m-rooms button.here::after { content: "You are here"; margin-left: 10px; font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--g5); }

/* v117: no door / stairs offer over the open menu */
html.menu-open #door { opacity: 0 !important; pointer-events: none !important; }
/* v117: How to move, held upright (one joystick): no two-joystick diagrams, nothing wider than the screen */
#help .solo-only { display: none; }
html.solo #help .duo-only { display: none; }
html.solo #help .solo-only { display: block; }
#help .how { flex-wrap: wrap; justify-content: center; max-width: 100%; row-gap: 18px; }
/* v117: a tip changes with a quick fade; names of menu items in bold */
#load-tip .tip-ic, #load-tip .tip-main { transition: opacity .35s var(--ease); }
#load-tip.swap .tip-ic, #load-tip.swap .tip-main { opacity: 0; }
.tip-tx b { font-weight: 500; }
/* v117: Go to room - a small icon before each room */
#menu .m-rooms button { display: flex; align-items: center; gap: 12px; }
#menu .m-rooms .rm-ic { flex: 0 0 20px; width: 20px; height: 20px; color: var(--g6); display: flex; }
#menu .m-rooms .rm-ic svg { width: 20px; height: 20px; }
#menu .m-rooms button.here .rm-ic { color: var(--black); }
#menu .m-rooms button.here::after { margin-left: 0; }
