/* ALTIS — the residence control surface (src/panel.js).

   The photograph is the product. Everything here is drawn on top of it and is
   therefore held to one rule: AT REST THE ROOM MUST LOOK LIKE A PHOTOGRAPH
   WITH A FEW SMALL LIGHTS IN IT. Three ~5px pips of warm light, no labels, no
   rings, no scrims. Everything else is summoned by a press and folds away.

   Colour comes from the site tokens on :root in styles/main.css; every var()
   carries a literal fallback so the component also renders standalone.

   SCROLL CONTRACT (tests/t10-scroll-block.mjs): nothing here touches the y axis
   of html / body / #stage, there is no overscroll-behavior, and there is no
   `touch-action` declaration anywhere in this file — nothing in the control
   surface is dragged, so nothing needs to claim a gesture from the page. */

.ap-root {
  --ap-ink:      var(--altis-mineral, #DCE3E8);
  --ap-dim:      var(--altis-text-dim, #8B99A4);
  --ap-hi:       var(--altis-white, #F7F8F5);
  --ap-amber:    var(--altis-amber, #FF9D2E);
  --ap-focus:    #7FC0FF;
  --ap-hair:     rgba(247, 248, 245, 0.09);
  --ap-hair-2:   rgba(247, 248, 245, 0.16);
  --ap-ease:     cubic-bezier(0.32, 0.72, 0.24, 1);
  --ap-ease-out: cubic-bezier(0.16, 0.84, 0.28, 1);

  position: absolute;
  inset: 0;
  /* Above the spot switcher and the move hint (z-index 70 in main.css): on a
     narrow viewport the panel becomes a bottom sheet and would otherwise be
     printed through by the switcher. */
  z-index: 80;
  pointer-events: none;
  font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
  font-feature-settings: "tnum" 1, "cv05" 1;
  color: var(--ap-ink);
}

/* The layer exists only while the residence is the spot you are standing in. */
.ap-root:not(.roomlive) .ap-markers,
.ap-root:not(.roomlive) .ap-keys,
.ap-root:not(.roomlive) .ap-panel {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}

.ap-markers {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition: opacity 0.7s ease, visibility 0s;
}

.ap-sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.ap-root :focus-visible {
  outline: 1.5px solid var(--ap-focus);
  outline-offset: 3px;
}
.ap-root .ap-g { display: block; }

/* ============================================================ the dot of light
   A recessed status LED, not a UI pin. One warm core, one tight bloom, and a
   1px dark bezel so it survives both the travertine and the sea. The 44px
   target around it is completely invisible. */

.ap-mark {
  position: absolute;
  width: 44px; height: 44px;
  margin: 0; padding: 0;
  transform: translate(-50%, -50%);
  background: none;
  border: 0;
  cursor: pointer;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}

/* A ring around the dot, so a fixture reads as something you can press rather
   than as a light that happens to be on. The dot alone was too quiet to find:
   the room is the one spot that is operated rather than looked at. */
.ap-mark::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 214, 160, 0.5);
  box-shadow: 0 0 0 1px rgba(6, 11, 16, 0.35), inset 0 0 12px rgba(255, 190, 124, 0.14);
  pointer-events: none;
  transition: border-color 0.3s ease, transform 0.3s var(--ap-ease);
}
.ap-mark:hover::after,
.ap-mark:focus-visible::after {
  border-color: rgba(255, 226, 184, 0.9);
  transform: scale(1.12);
}

.ap-pip {
  position: absolute;
  left: 50%; top: 50%;
  width: 8px; height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 38%, #FFFFFF 0%, #FFEBD2 40%, #FFC581 100%);
  box-shadow:
    0 0 0 1px rgba(6, 11, 16, 0.55),
    0 0 4px 1px rgba(255, 210, 150, 0.95),
    0 0 13px 3px rgba(255, 168, 84, 0.48);
  transition: box-shadow 0.5s var(--ap-ease), background 0.5s ease,
              width 0.35s var(--ap-ease), height 0.35s var(--ap-ease),
              margin 0.35s var(--ap-ease);
}

/* The only thing that moves at rest: the bloom breathes, very slowly, at a
   very low amplitude. It should read as a light being on, not as an animation. */
.ap-pip::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 26px; height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 186, 118, 0.40) 0%, rgba(255, 176, 100, 0.14) 45%, rgba(255, 170, 90, 0) 72%);
  animation: ap-breathe 5.2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes ap-breathe {
  0%, 100% { opacity: 0.55; transform: scale(0.88); }
  50%      { opacity: 1;    transform: scale(1.12); }
}

.ap-mark:hover .ap-pip,
.ap-mark:focus-visible .ap-pip,
.ap-mark.ap-live .ap-pip {
  width: 6px; height: 6px;
  margin: -3px 0 0 -3px;
  box-shadow:
    0 0 0 1px rgba(6, 11, 16, 0.5),
    0 0 4px 1px rgba(255, 226, 184, 1),
    0 0 14px 4px rgba(255, 172, 92, 0.55);
}
.ap-mark.ap-live .ap-pip::after { animation-duration: 2.6s; }

/* Privacy carries its state in the light itself: warm = clear, cool = private.
   Nothing else changes, because the glass in the photograph does the talking. */
.ap-mark.ap-state-on .ap-pip {
  background: radial-gradient(circle at 50% 38%, #FFFFFF 0%, #E4F0FF 40%, #9FC7F2 100%);
  box-shadow:
    0 0 0 1px rgba(6, 11, 16, 0.55),
    0 0 3px 0.5px rgba(198, 226, 255, 0.95),
    0 0 10px 2px rgba(126, 178, 236, 0.45);
}
.ap-mark.ap-state-on .ap-pip::after {
  background: radial-gradient(circle, rgba(150, 196, 245, 0.30) 0%, rgba(140, 188, 240, 0.10) 45%, rgba(140, 188, 240, 0) 72%);
}

/* The label is summoned, never resident. */
.ap-tag {
  position: absolute;
  left: 26px; top: 50%;
  padding: 5px 9px 5px 10px;
  white-space: nowrap;
  font-size: 9px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(255, 246, 236, 0.94);
  background: rgba(7, 12, 17, 0.5);
  border-radius: 2px;
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  backdrop-filter: blur(14px) saturate(1.1);
  opacity: 0;
  transform: translateY(-50%) translateX(-4px);
  pointer-events: none;
  transition: opacity 0.32s ease, transform 0.32s var(--ap-ease-out);
}
.ap-mark.ap-flip .ap-tag {
  left: auto; right: 26px;
  padding-left: 9px; padding-right: 10px;
  transform: translateY(-50%) translateX(4px);
}
.ap-mark:hover .ap-tag,
.ap-mark:focus-visible .ap-tag,
.ap-mark.ap-live .ap-tag,
.ap-mark.ap-say .ap-tag {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}
.ap-mark.ap-live .ap-tag { opacity: 0; }   /* the control itself is now the label */

/* ================================================================ the keypad
   A motorised-blind keypad: a small machined plate with three keys. Solid, not
   translucent — hardware should read as hardware sitting in the room. */

.ap-keys {
  position: absolute;
  width: 52px;
  padding: 5px 5px 10px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  border-radius: 11px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0) 34%),
    linear-gradient(176deg, #1D242C 0%, #12181E 52%, #0A0E13 100%);
  border: 1px solid rgba(247, 248, 245, 0.10);
  box-shadow:
    0 22px 44px -18px rgba(0, 0, 0, 0.9),
    0 2px 6px -2px rgba(0, 0, 0, 0.7),
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    inset 0 -1px 0 rgba(0, 0, 0, 0.6);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px) scale(0.94);
  transform-origin: 50% 50%;
  transition: opacity 0.34s ease, transform 0.44s var(--ap-ease-out), visibility 0s linear 0.34s;
}
.ap-keys.ap-on {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition-delay: 0s;
}

.ap-key {
  position: relative;
  display: grid;
  place-items: center;
  width: 42px; height: 44px;
  margin: 0; padding: 0;
  color: rgba(228, 236, 242, 0.74);
  background: none;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
  transition: color 0.22s ease, background 0.22s ease;
}
.ap-key + .ap-key::before {
  content: "";
  position: absolute;
  left: 8px; right: 8px; top: -1px;
  height: 1px;
  background: rgba(255, 255, 255, 0.06);
}
.ap-key:hover { color: var(--ap-hi); background: rgba(255, 255, 255, 0.05); }
.ap-key:active { background: rgba(255, 255, 255, 0.09); }
.ap-key-stop { color: rgba(228, 236, 242, 0.58); }

/* A press lights the key for a beat — the acknowledgement a real keypad gives. */
.ap-key.ap-hit,
.ap-bkey.ap-hit { animation: ap-ack 0.62s var(--ap-ease) 1; }
@keyframes ap-ack {
  0%   { box-shadow: inset 0 0 0 1px rgba(255, 176, 92, 0.85), 0 0 12px 0 rgba(255, 157, 46, 0.5); color: #FFE6C8; }
  100% { box-shadow: inset 0 0 0 1px rgba(255, 176, 92, 0), 0 0 12px 0 rgba(255, 157, 46, 0); }
}

/* Travel indicator: a hairline showing where the blind actually is, filled from
   the head rail down. It moves at motor speed, so "stop" is something you see. */
.ap-travel {
  position: absolute;
  left: 12px; right: 12px; bottom: 4px;
  height: 1px;
  background: rgba(247, 248, 245, 0.13);
  border-radius: 1px;
  overflow: hidden;
}
.ap-travel-f {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: var(--ap-trav, 100%);
  background: linear-gradient(90deg, rgba(255, 190, 124, 0.55), rgba(255, 214, 168, 0.95));
  box-shadow: 0 0 5px 0 rgba(255, 168, 84, 0.6);
}

/* =============================================================== the panel
   The in-wall keypad: dark, glassy, precise. Edge-lit at the top like a piece
   of anodised hardware, never like a settings dialog. */

.ap-panel {
  position: absolute;
  right: clamp(14px, 2.6vw, 40px);
  top: 50%;
  width: min(348px, calc(100vw - 28px));
  max-height: min(86vh, 720px);
  overflow-y: auto;
  padding: 0 0 4px;
  border-radius: 15px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0) 18%),
    linear-gradient(166deg, rgba(24, 34, 44, 0.70) 0%, rgba(12, 18, 25, 0.78) 46%, rgba(7, 11, 16, 0.85) 100%);
  border: 1px solid rgba(247, 248, 245, 0.10);
  box-shadow:
    0 52px 96px -40px rgba(0, 0, 0, 0.92),
    0 6px 22px -12px rgba(0, 0, 0, 0.7),
    inset 0 1px 0 rgba(255, 255, 255, 0.09),
    inset 0 -1px 0 rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(30px) saturate(1.3);
  backdrop-filter: blur(30px) saturate(1.3);
  opacity: 0;
  transform: translateY(-50%) translateX(14px) scale(0.985);
  transform-origin: 100% 50%;
  pointer-events: none;
  transition: opacity 0.34s ease, transform 0.5s var(--ap-ease-out);
  scrollbar-width: thin;
  scrollbar-color: rgba(247, 248, 245, 0.18) transparent;
}
.ap-panel.ap-open {
  opacity: 1;
  transform: translateY(-50%) translateX(0) scale(1);
  pointer-events: auto;
}
.ap-panel[hidden] { display: none; }

/* A single warm filament along the top edge — the one lit detail on the case. */
.ap-edge {
  position: absolute;
  left: 22%; right: 22%; top: 0;
  height: 1px;
  background: linear-gradient(90deg, rgba(255, 157, 46, 0), rgba(255, 190, 120, 0.75), rgba(255, 157, 46, 0));
  opacity: 0;
  transition: opacity 0.9s ease 0.18s;
}
.ap-panel.ap-open .ap-edge { opacity: 1; }

.ap-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 15px 14px 13px 18px;
  border-bottom: 1px solid var(--ap-hair);
  background: linear-gradient(180deg, rgba(16, 23, 30, 0.72), rgba(16, 23, 30, 0.3));
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}
.ap-idt { display: flex; align-items: baseline; gap: 11px; min-width: 0; }
.ap-brand {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: rgba(255, 190, 124, 0.9);
}
.ap-place {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.30em;
  text-transform: uppercase;
  color: rgba(247, 248, 245, 0.62);
}
.ap-x {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  margin: -6px -8px -6px 0;
  color: rgba(226, 234, 240, 0.5);
  background: none;
  border: 0;
  border-radius: 9px;
  cursor: pointer;
  transition: color 0.22s ease, background 0.22s ease;
}
.ap-x:hover { color: var(--ap-hi); background: rgba(255, 255, 255, 0.05); }

.ap-body { padding: 0 18px; }

.ap-row { padding: 12px 0 13px; }
.ap-row + .ap-row { border-top: 1px solid rgba(247, 248, 245, 0.055); }
.ap-row-split {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}

/* Sections arrive in sequence — the surface waking up, not a dialog appearing. */
.ap-panel .ap-row {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.44s ease, transform 0.55s var(--ap-ease-out);
}
.ap-panel.ap-open .ap-row { opacity: 1; transform: none; }
.ap-panel.ap-open .ap-row:nth-child(1) { transition-delay: 0.05s; }
.ap-panel.ap-open .ap-row:nth-child(2) { transition-delay: 0.09s; }
.ap-panel.ap-open .ap-row:nth-child(3) { transition-delay: 0.13s; }
.ap-panel.ap-open .ap-row:nth-child(4) { transition-delay: 0.17s; }
.ap-panel.ap-open .ap-row:nth-child(5) { transition-delay: 0.21s; }
.ap-panel.ap-open .ap-row:nth-child(6) { transition-delay: 0.25s; }
.ap-panel.ap-open .ap-row:nth-child(7) { transition-delay: 0.29s; }

.ap-h {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
.ap-h-inline { margin: 0; }
.ap-cap {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.30em;
  text-transform: uppercase;
  color: rgba(190, 203, 213, 0.62);
  white-space: nowrap;
}
.ap-val {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 10.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.12em;
  color: rgba(247, 248, 245, 0.9);
}
.ap-cust {
  font-style: normal;
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ap-amber);
  opacity: 0;
  transition: opacity 0.3s ease;
}
.ap-panel.ap-is-custom .ap-cust { opacity: 0.9; }
.ap-swatch {
  width: 9px; height: 9px;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 0 7px 0 currentColor;
  transition: background 0.3s ease;
}

/* --- scene keys ------------------------------------------------------------
   Five engraved keys with a status LED above each name. The lit one is the
   scene the room is in. */

.ap-scenes {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 5px;
}
.ap-scene {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 50px;
  min-width: 0;
  padding: 9px 2px 9px;
  overflow: hidden;
  font-size: 8px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  text-align: center;
  color: rgba(190, 203, 213, 0.55);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.012));
  border: 1px solid rgba(247, 248, 245, 0.07);
  border-radius: 8px;
  cursor: pointer;
  transition: color 0.26s ease, border-color 0.26s ease, background 0.26s ease, box-shadow 0.26s ease;
}
.ap-scene:hover { color: rgba(247, 248, 245, 0.88); border-color: rgba(247, 248, 245, 0.16); }
.ap-led {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: rgba(247, 248, 245, 0.20);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.4);
  transition: background 0.32s ease, box-shadow 0.32s ease;
}
.ap-scene[aria-pressed="true"] {
  color: var(--ap-hi);
  border-color: rgba(255, 180, 105, 0.34);
  background: linear-gradient(180deg, rgba(255, 168, 74, 0.11), rgba(255, 168, 74, 0.02));
  box-shadow: 0 6px 18px -12px rgba(255, 157, 46, 0.9), inset 0 1px 0 rgba(255, 255, 255, 0.07);
}
.ap-scene[aria-pressed="true"] .ap-led {
  background: #FFD9A6;
  box-shadow: 0 0 5px 1px rgba(255, 173, 86, 0.95), 0 0 13px 4px rgba(255, 157, 46, 0.35);
}

/* --- precision sliders -----------------------------------------------------
   A hairline track and a small machined thumb. Native <input type="range">, so
   they are keyboard- and AT-operable and can never fight the page. */

.ap-sl {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 22px;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
}
.ap-sl + .ap-sl { margin-top: 4px; }

.ap-sl::-webkit-slider-runnable-track { height: 3px; border-radius: 2px; }
.ap-sl::-moz-range-track { height: 3px; border-radius: 2px; }

.ap-sl-fill::-webkit-slider-runnable-track {
  background: linear-gradient(90deg,
    rgba(255, 209, 156, 0.95) 0 var(--ap-pct, 50%),
    rgba(247, 248, 245, 0.12) var(--ap-pct, 50%) 100%);
}
.ap-sl-fill::-moz-range-track {
  background: linear-gradient(90deg,
    rgba(255, 209, 156, 0.95) 0 var(--ap-pct, 50%),
    rgba(247, 248, 245, 0.12) var(--ap-pct, 50%) 100%);
}
.ap-sl-cct::-webkit-slider-runnable-track {
  background: linear-gradient(90deg, #FFB169 0%, #FFCE99 26%, #FFF0DE 52%, #E4EFFF 78%, #C2DBFF 100%);
}
.ap-sl-cct::-moz-range-track {
  background: linear-gradient(90deg, #FFB169 0%, #FFCE99 26%, #FFF0DE 52%, #E4EFFF 78%, #C2DBFF 100%);
}
.ap-sl-hue::-webkit-slider-runnable-track {
  background: linear-gradient(90deg,
    hsl(0 70% 58%), hsl(60 70% 58%), hsl(120 70% 58%), hsl(180 70% 58%),
    hsl(240 70% 58%), hsl(300 70% 58%), hsl(360 70% 58%));
}
.ap-sl-hue::-moz-range-track {
  background: linear-gradient(90deg,
    hsl(0 70% 58%), hsl(60 70% 58%), hsl(120 70% 58%), hsl(180 70% 58%),
    hsl(240 70% 58%), hsl(300 70% 58%), hsl(360 70% 58%));
}
.ap-sl-sat::-webkit-slider-runnable-track {
  background: linear-gradient(90deg,
    rgba(247, 248, 245, 0.13), hsl(var(--ap-hue, 40) 78% 58%));
}
.ap-sl-sat::-moz-range-track {
  background: linear-gradient(90deg,
    rgba(247, 248, 245, 0.13), hsl(var(--ap-hue, 40) 78% 58%));
}

.ap-sl::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 13px; height: 13px;
  margin-top: -5px;
  border: 0;
  border-radius: 50%;
  background: linear-gradient(180deg, #FFFFFF, #D6DDE3);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.6);
  transition: transform 0.18s var(--ap-ease);
}
.ap-sl::-moz-range-thumb {
  width: 13px; height: 13px;
  border: 0;
  border-radius: 50%;
  background: linear-gradient(180deg, #FFFFFF, #D6DDE3);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.6);
}
.ap-sl:hover::-webkit-slider-thumb,
.ap-sl:active::-webkit-slider-thumb { transform: scale(1.14); }

/* --- climate setpoint ------------------------------------------------------ */

.ap-stepper {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: 10px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.01));
  border: 1px solid rgba(247, 248, 245, 0.07);
}
.ap-step {
  display: grid;
  place-items: center;
  width: 40px; height: 44px;
  padding: 0;
  color: rgba(226, 234, 240, 0.6);
  background: none;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease;
}
.ap-step:hover { color: var(--ap-hi); background: rgba(255, 255, 255, 0.06); }
.ap-temp {
  display: flex;
  align-items: baseline;
  gap: 2px;
  min-width: 62px;
  justify-content: center;
  font-variant-numeric: tabular-nums;
}
.ap-temp b {
  font-size: 18px;
  font-weight: 400;
  letter-spacing: 0.01em;
  color: var(--ap-hi);
}
.ap-temp span { font-size: 9.5px; color: rgba(190, 203, 213, 0.62); }

/* --- privacy switch -------------------------------------------------------- */

.ap-switch {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 44px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}
.ap-switch-txt {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(247, 248, 245, 0.86);
  transition: color 0.28s ease;
}
.ap-switch-track {
  position: relative;
  display: block;
  width: 42px; height: 24px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.34);
  border: 1px solid rgba(247, 248, 245, 0.12);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.6);
  transition: background 0.3s ease, border-color 0.3s ease;
}
.ap-switch-knob {
  position: absolute;
  left: 2px; top: 2px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: linear-gradient(180deg, #E9EEF2, #AEBAC4);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
  transition: transform 0.36s var(--ap-ease), background 0.3s ease, box-shadow 0.3s ease;
}
.ap-switch[aria-checked="true"] .ap-switch-track {
  background: rgba(120, 170, 225, 0.22);
  border-color: rgba(150, 195, 240, 0.45);
}
.ap-switch[aria-checked="true"] .ap-switch-knob {
  transform: translateX(18px);
  background: linear-gradient(180deg, #FFFFFF, #BFD9F2);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6), 0 0 10px 1px rgba(150, 195, 240, 0.5);
}

/* --- shading keypad, panel form -------------------------------------------- */

.ap-bar {
  position: relative;
  display: flex;
  gap: 2px;
  padding: 3px 3px 6px;
  border-radius: 10px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.01));
  border: 1px solid rgba(247, 248, 245, 0.07);
}
.ap-bkey {
  position: relative;
  flex: 1 1 0;
  display: grid;
  place-items: center;
  min-height: 44px;
  padding: 0;
  color: rgba(226, 234, 240, 0.6);
  background: none;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease;
}
.ap-bkey:hover { color: var(--ap-hi); background: rgba(255, 255, 255, 0.06); }
.ap-bkey-stop { color: rgba(226, 234, 240, 0.48); }

/* In the panel the gauge is integrated into the keypad plate itself: a hairline
   along its inside edge, the way a real blind keypad carries its position. */
.ap-travel-h {
  position: absolute;
  left: 14px; right: 14px; bottom: 3px;
  width: auto;
  transform: none;
  background: rgba(247, 248, 245, 0.08);
}
.ap-travel-h .ap-travel-f {
  background: linear-gradient(90deg, rgba(255, 190, 124, 0.32), rgba(255, 206, 152, 0.62));
  box-shadow: none;
}

/* ============================================================== responsive
   Narrow viewports: the panel becomes a bottom sheet. Every target stays 44px
   and the dots stay dots. */

@media (max-width: 720px) {
  .ap-panel {
    right: 10px; left: 10px;
    top: auto; bottom: 10px;
    width: auto;
    max-width: 420px;
    margin-left: auto;
    margin-right: auto;
    /* 78vh was 627px of an 812px phone: the panel blanketed the room it
       exists to operate, and because its content is taller than that, its own
       overflow-y:auto swallowed the first flick of every attempt to scroll
       the page onward — with only ~48px of internal travel to give back.
       62vh leaves the top third of the residence visible above it, which is
       both the picture and a place to put a thumb that scrolls the page. */
    max-height: 62vh;
    /* And while a thumb IS on the panel, the panel owns the gesture. Half a
       flick scrolling the controls and the other half lurching the page on to
       the next spot is worse than either one alone. */
    overscroll-behavior: contain;
    border-radius: 16px;
    transform: translateY(16px) scale(0.99);
    transform-origin: 50% 100%;
  }
  .ap-panel.ap-open { transform: translateY(0) scale(1); }
  .ap-body { padding: 0 16px; }
  .ap-head { padding-left: 16px; }
  .ap-scenes { gap: 4px; }
  /* 44px is still a full touch target; the 6px each buys back is 6px less
     that has to be found by scrolling inside the panel. */
  .ap-scene { min-height: 44px; font-size: 7.5px; letter-spacing: 0.04em; }
}

/* ========================================================== reduced motion
   The room still answers; it just answers immediately. */

@media (prefers-reduced-motion: reduce) {
  .ap-root *,
  .ap-root *::before,
  .ap-root *::after {
    animation: none !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
  .ap-pip::after { opacity: 0.8; transform: scale(1); }
}

/* ============================================================
   PROMINENCE PASS
   The pips must invite a press without competing with the
   photograph. Larger core, wider bloom, and a slow attention
   pulse that sweeps the markers in sequence every 10s so the
   eye is drawn round the room rather than flashed at.
   ============================================================ */
.ap-core { width: 7px; height: 7px; }
.ap-halo { width: 26px; height: 26px; }

.ap-pip::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 26px; height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle,
    rgba(255, 214, 160, 0.55) 0%,
    rgba(255, 190, 124, 0.22) 42%,
    rgba(255, 176, 92, 0) 72%);
  opacity: 0;
  animation: ap-attn 10s ease-in-out infinite;
}
/* sweep: shading, then privacy, then the control panel last —
   the panel is the one we most want pressed, so it lands last
   and holds the eye. */
.ap-mark[data-id="window"]    .ap-pip::after { animation-delay: 0s; }
.ap-mark[data-id="partition"] .ap-pip::after { animation-delay: 0.45s; }
.ap-mark[data-id="wallpanel"] .ap-pip::after { animation-delay: 0.9s; }

@keyframes ap-attn {
  0%,  100% { opacity: 0; transform: scale(0.6); }
  3%        { opacity: 1; transform: scale(1.5); }
  11%       { opacity: 0; transform: scale(2.1); }
}

/* The control-panel pip is the primary call to action: hold it
   a touch brighter at rest than the two device pips. */
.ap-mark[data-id="wallpanel"] .ap-core {
  width: 8px; height: 8px;
  box-shadow:
    0 0 5px 1px rgba(255, 226, 184, 1),
    0 0 15px 4px rgba(255, 186, 110, 0.8),
    0 0 34px 11px rgba(255, 168, 84, 0.34);
}

@media (prefers-reduced-motion: reduce) {
  .ap-pip::after { animation: none; opacity: 0; }
}

/* Brand mark in the panel header. Drop a file at
   assets/brand/altis-logo.svg (or .png) and it replaces the
   wordmark automatically; absent, the wordmark stands. */
.ap-logo { display: block; height: 15px; width: auto; opacity: 0.95; }
.ap-idt.haslogo .ap-brand { display: none; }
.ap-idt.haslogo .ap-place {
  padding-left: 9px;
  margin-left: 1px;
  border-left: 1px solid rgba(255, 214, 160, 0.28);
}
.ap-idt.haslogo { align-items: center; flex-direction: row; gap: 9px; }

/* The Altis mark as the panel's maker badge — the way a real piece of
   building hardware is quietly branded. Mark only, never the wordmark. */
.ap-logo { height: 17px; opacity: 0.92; }
.ap-idt.haslogo .ap-place { padding-left: 10px; }


/* ============================================================
   PROMINENCE, corrected. The pip is a single dot drawn by
   box-shadow (there is no .ap-core / .ap-halo any more), and it
   is centred by left/top 50% with a negative half-size margin —
   so any size change has to move that margin with it.
   ============================================================ */
.ap-pip {
  width: 11px; height: 11px;
  margin: -5.5px 0 0 -5.5px;
  box-shadow:
    0 0 0 1.5px rgba(6, 11, 16, 0.6),
    0 0 6px 1px rgba(255, 216, 166, 1),
    0 0 18px 5px rgba(255, 176, 96, 0.7),
    0 0 40px 14px rgba(255, 160, 74, 0.34);
}

/* A permanent ring, so the dot reads as a control and not as a
   light in the photograph. The base rule has content:none. */
.ap-mark::after {
  content: "";
  width: 40px; height: 40px;
  margin: -20px 0 0 -20px;
  border: 1.5px solid rgba(255, 224, 184, 0.62);
  box-shadow:
    0 0 0 1px rgba(6, 12, 17, 0.4),
    inset 0 0 14px rgba(255, 190, 124, 0.18);
  transform: scale(1);
}
.ap-mark:hover::after,
.ap-mark:focus-visible::after {
  border-color: rgba(255, 240, 214, 1);
  transform: scale(1.15);
}
.ap-mark:hover .ap-pip,
.ap-mark:focus-visible .ap-pip { width: 14px; height: 14px; margin: -7px 0 0 -7px; }

/* The attention sweep, sized to the bigger ring. */
.ap-pip::after { width: 48px; height: 48px; margin: -24px 0 0 -24px; }

/* The control panel is the one we most want pressed: brightest of the three. */
.ap-mark[data-id="wallpanel"] .ap-pip {
  width: 13px; height: 13px;
  margin: -6.5px 0 0 -6.5px;
  box-shadow:
    0 0 0 1.5px rgba(6, 11, 16, 0.6),
    0 0 8px 2px rgba(255, 236, 202, 1),
    0 0 24px 7px rgba(255, 186, 110, 0.9),
    0 0 52px 18px rgba(255, 168, 84, 0.42);
}
.ap-mark[data-id="wallpanel"]::after { border-color: rgba(255, 240, 214, 0.85); }
