/* Duo Design — teste 02: a cidade começa.
   Base comum a todo o prédio (vem do teste 01). Sem JavaScript na página.

   O isométrico é CSS 3D. Cada .b é uma caixa: a face de cima é o próprio
   elemento e as duas faces da frente são o ::before (y = d, frente-direita)
   e o ::after (x = 0, frente-esquerda). Cada .py e .px é um plano de pé e
   cada .pz um plano deitado. As coordenadas são unidades do piso (sala de
   300 × 300, laje de 16, paredes de 200), escaladas por --u.

   Este ficheiro é da estrutura e não se mexe a partir dos pisos. Cada piso
   tem o seu pisos/ID.css, com todas as regras dentro de .f-ID, e o
   index.html sai de molde.html pelo montar.py. */

:root {
  --u: clamp(0.6px, 0.085vw, 1.25px);
  --paper: #F3EEE6;
  --ink: #1F1C2C;
  --ink-2: #5B5767;
  --line: rgb(31 28 44 / 0.24);
  --coral: #F48466;
  --cobalt: #3D6FB6;
  --sun: #F2C14E;
  --mint: #93BFA2;
  --wood: #D2AE86;
  --slab: #CEC3B2;
  --floor: #E9E1D3;
  --page-1: #F8F3EA;
  --page-2: #E4DCCD;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --sans: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif;
  --dots: "Doto", var(--mono);
  color-scheme: light;
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* ---------- cabeçalho e rodapé ---------- */
/* presos aos elementos: «top» e «foot» também são nomes de peças nos pisos (o pé de um monitor) */

header.top, footer.foot {
  max-width: 1180px; margin: 0 auto; padding: 22px 16px;
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  font: 500 12px/1.3 var(--mono); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-2);
}
header.top b { color: var(--ink); font-weight: 700; }
footer.foot { padding-top: 56px; padding-bottom: 40px; justify-content: flex-start; gap: 6px 28px; }
/* o email, montado pelo CSS (ver o molde): os robôs que recolhem endereços lêem o HTML e não o encontram */
footer.foot .email { color: var(--ink); text-transform: none; }
footer.foot .email::before { content: attr(data-u) "\0040" attr(data-d); }

/* ---------- o prédio ---------- */

.building { max-width: 1180px; margin: 0 auto; padding: 0 16px; overflow-x: clip; }

.floor {
  --wallc: #E8E1D5;
  --accent: var(--ink);
  --numc: color-mix(in oklab, var(--wallc), var(--ink) 34%);
  display: grid;
  grid-template-columns: minmax(0, 11fr) minmax(0, 9fr);
  gap: 0 56px;
  align-items: center;
  position: relative;
}

/* --cy é onde fica o centro do chão e --sh a altura da cena, em unidades; num ecrã largo a cena estica até à altura do
   ecrã e o desenho fica ao meio (mais abaixo, em «um piso por ecrã») */
.scene {
  --cy: 333; --sh: 480;
  position: relative;
  isolation: isolate;
  height: calc(var(--sh) * var(--u));
}
.scene > .iso { top: calc(var(--cy) * var(--u) + (100% - var(--sh) * var(--u)) / 2); }

/* linhas de construção que ligam os pisos explodidos */
.scene::before, .scene::after {
  content: ""; position: absolute; top: 0; bottom: 0; z-index: -1; pointer-events: none;
}
.scene::before {
  left: calc(50% - 212 * var(--u)); width: calc(424 * var(--u));
  border-inline: 1px dashed var(--line);
}
.scene::after { left: 50%; border-left: 1px dashed var(--line); }

.iso {
  position: absolute; left: 50%; top: calc(var(--cy) * var(--u));
  width: calc(300 * var(--u)); height: calc(300 * var(--u));
  margin: calc(-150 * var(--u)) 0 0 calc(-150 * var(--u));
  transform: rotateX(60deg) rotateZ(-45deg);
  transform-style: preserve-3d;
  transition: translate .5s cubic-bezier(.2, .8, .2, 1);
}

.b, .py, .px, .pz { position: absolute; --x: 0; --y: 0; --z: 0; --w: 10; --d: 10; --h: 10; }

.b {
  --c: #DDD; --top: initial; --c1: initial; --c2: initial;
  left: calc(var(--x) * var(--u)); top: calc(var(--y) * var(--u));
  width: calc(var(--w) * var(--u)); height: calc(var(--d) * var(--u));
  transform: translateZ(calc((var(--z) + var(--h)) * var(--u)));
  transform-style: preserve-3d;
  background: var(--top, var(--c));
}
.b::before, .b::after { content: ""; position: absolute; }
.b::before { /* face da frente-direita (y = d) */
  left: 0; top: 100%; width: 100%; height: calc(var(--h) * var(--u));
  transform-origin: 50% 0; transform: rotateX(-90deg);
  background: var(--c2, color-mix(in oklab, var(--c), var(--ink) 24%));
}
.b::after { /* face da frente-esquerda (x = 0) */
  left: 0; top: 0; width: calc(var(--h) * var(--u)); height: 100%;
  transform-origin: 0 50%; transform: rotateY(90deg);
  background: var(--c1, color-mix(in oklab, var(--c), var(--ink) 11%));
}

.py, .px {
  left: calc(var(--x) * var(--u)); top: calc((var(--y) - var(--h)) * var(--u));
  width: calc(var(--w) * var(--u)); height: calc(var(--h) * var(--u));
}
.py { transform-origin: 50% 100%; transform: translateZ(calc(var(--z) * var(--u))) rotateX(-90deg); }
.px { transform-origin: 0 100%; transform: translateZ(calc(var(--z) * var(--u))) rotateZ(90deg) rotateX(-90deg); }
.pz {
  left: calc(var(--x) * var(--u)); top: calc(var(--y) * var(--u));
  width: calc(var(--w) * var(--u)); height: calc(var(--d) * var(--u));
  transform: translateZ(calc(var(--z) * var(--u)));
}

/* ---------- a sala de cada piso: laje, paredes, número, nome ---------- */

.slab {
  --w: 300; --d: 300; --h: 16; --c: var(--slab);
  --top: repeating-linear-gradient(90deg, transparent 0 calc(29 * var(--u)), rgb(31 28 44 / .07) 0 calc(30 * var(--u))) var(--floor);
}
.wall-l {
  --z: 16; --w: 300; --d: 8; --h: 200;
  --c: color-mix(in oklab, var(--wallc), white 40%);
  --c2: color-mix(in oklab, var(--wallc), var(--ink) 7%);
}
.wall-r {
  --x: 292; --y: 8; --z: 16; --w: 8; --d: 292; --h: 200;
  --c: color-mix(in oklab, var(--wallc), white 40%);
  --c1: var(--wallc);
}

/* a cave: a sala do Lab fica enterrada; a terra corta-se rente aos lados abertos,
   e o cimo da terra (z 216) é o nível da rua */
.cave .soil {
  --c: #A9B78F;
  --c1: repeating-linear-gradient(135deg, #A27D55 0 2px, #BC966C 2px 9px);
  --c2: repeating-linear-gradient(45deg, #94714B 0 2px, #AF895F 2px 9px);
}
.cave .soil-base { --x: 0; --y: -30; --z: -40; --w: 330; --d: 330; --h: 40; }
.cave .soil-back { --x: 0; --y: -30; --z: 0; --w: 330; --d: 30; --h: 216; }
.cave .soil-side { --x: 300; --y: 0; --z: 0; --w: 30; --d: 300; --h: 216; }

/* número do piso, pintado em contorno no alto da parede da esquerda
   (filho da .wall-l: z = 0 é o topo da parede, abs. 216) */
.wl-num {
  --x: 14; --y: 11; --w: 170; --z: -118; --h: 104;
  display: grid; place-items: end start;
  color: transparent; -webkit-text-stroke: calc(1.5 * var(--u)) var(--numc);
  font: 800 calc(112 * var(--u))/.78 var(--sans); letter-spacing: -.04em;
  font-variation-settings: "opsz" 96;
}
/* nome do piso, na parede da direita (filho da .wall-r: x = 0 é a face de dentro) */
.wr-name {
  --x: -3; --y: 20; --w: 236; --z: -56; --h: 40;
  display: grid; place-items: end start;
  color: var(--numc);
  font: 600 calc(14 * var(--u))/1.2 var(--mono); letter-spacing: .14em; text-transform: uppercase;
}

/* o tubo do correio, que atravessa o prédio todo, até à cave −4, onde acaba tapado; a cápsula desce
   um piso a cada 0,7 s (piso 8 com --i:0 … rés-do-chão com --i:8, o Lab com 9, o Fabel na −2 com 10, a −3 com 11
   e a −4 com 12; ciclo de 9,1 s). É de vidro: azulado, com dois reflexos brancos e o rebordo marcado, para se ver
   no claro, na projecção do Lab e no escuro das caves */
.tube {
  --x: 272; --y: 268; --w: 14; --d: 14; --h: 216;
  --c: rgb(61 111 182 / .3);
  /* na face -x (o ::after), a largura do elemento é a altura do tubo: os reflexos verticais vão a 180deg */
  --c1: linear-gradient(180deg, rgb(255 255 255 / .75) 0 9%, rgb(61 111 182 / .26) 9% 64%, rgb(255 255 255 / .38) 64% 72%, rgb(61 111 182 / .34) 72%);
  --c2: linear-gradient(90deg, rgb(61 111 182 / .36) 0 18%, rgb(255 255 255 / .5) 18% 26%, rgb(61 111 182 / .3) 26% 88%, rgb(255 255 255 / .6) 88%);
}
.tube, .tube::before, .tube::after { box-shadow: inset 0 0 0 calc(.6 * var(--u)) rgb(40 80 140 / .55); }
.tube-cap { --x: 269; --y: 265; --w: 20; --d: 20; --h: 6; --c: var(--ink); }
.capsule {
  --x: 274.5; --y: 270.5; --w: 9; --d: 9; --h: 20; --z: 196; --c: var(--coral);
  scale: 0;
  animation: mail 9.1s linear infinite;
  animation-delay: calc(var(--i, 0) * .7s - 9.1s);
}
@keyframes mail {
  0%        { scale: 1; translate: 0 0 0; }
  7.69%     { scale: 1; translate: 0 0 calc(-196 * var(--u)); }
  7.73%, to { scale: 0; translate: 0 0 calc(-196 * var(--u)); }
}
/* na cave −4 a linha acaba: o tubo está tapado rente ao chão (z 22). Quando ninguém está na −4, a cápsula desce
   até à tampa e desaparece, como nos outros pisos */
#cave-4 .capsule { animation-name: mail-cave; }
@keyframes mail-cave {
  0%        { scale: 1; translate: 0 0 0; }
  7.69%     { scale: 1; translate: 0 0 calc(-174 * var(--u)); }
  7.73%, to { scale: 0; translate: 0 0 calc(-174 * var(--u)); }
}
/* quem está na −4 (o mostrador do elevador no −4) vê a cápsula chegar pelo cimo do tubo e ficar pousada na tampa;
   enquanto lá estiver, o correio pára no prédio todo, porque uma cápsula que voltasse a subir tinha de se ver a
   subir na −3. Ao sair da −4, o ciclo recomeça no piso 8 (o José, a 4 de Novembro: «mais vale ficar parada no fim e
   se sairmos da -3 voltam ao movimento normal de descida... O que interessa é a ilusão»). O «estar na −4» é o
   --na-cave3, que a linha do tempo do −4 (era a −3 até o Fabel entrar na −2), a mesma do mostrador, põe a 1: o scroll só muda o estado, não leva a
   cápsula. A descida na −4 tem a velocidade das outras (174 unidades em 0,62 s) */
@property --na-cave3 { syntax: "<integer>"; inherits: true; initial-value: 0; }
@keyframes na-cave3 { from, to { --na-cave3: 1; } }
@keyframes mail-pousa {
  from { scale: 1; translate: 0 0 0; }
  to   { scale: 1; translate: 0 0 calc(-174 * var(--u)); }
}
@supports (timeline-scope: none) and (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .building { animation: na-cave3 linear; animation-timeline: --cv4; }
    @container style(--na-cave3: 1) {
      .capsule { animation: none; }
      #cave-4 .capsule { animation: mail-pousa .62s linear both; }
    }
  }
}

/* ---------- terraço ---------- */

.roof { padding-top: clamp(8px, 4vh, 48px); }
.roof .scene { --cy: 300; --sh: 430; }
.roof .scene::before { top: calc(var(--cy) * var(--u) + (100% - var(--sh) * var(--u)) / 2); }
.roof .scene::after { top: calc((var(--cy) + 106) * var(--u) + (100% - var(--sh) * var(--u)) / 2); }
.roof .slab {
  --c: #C9C1B4;
  --top: radial-gradient(circle, rgb(31 28 44 / .13) 0 calc(1.1 * var(--u)), transparent calc(1.4 * var(--u))) 0 0 / calc(9 * var(--u)) calc(9 * var(--u)) #D8D1C5;
}
.parapet { --c: #E4DDD1; }

/* ---------- rés-do-chão ---------- */

.ground { --wallc: #E6DFD3; }
.ground .scene::before, .ground .scene::after { bottom: auto; height: calc(var(--cy) * var(--u) + (100% - var(--sh) * var(--u)) / 2); }
.lift-door {
  display: grid; grid-template-columns: 1fr 1fr; gap: calc(1.5 * var(--u));
  padding: calc(6 * var(--u)) calc(6 * var(--u)) 0;
  background: #BDB4A6;
}
.lift-door i { background: linear-gradient(90deg, #DCD6CD, #C9C2B7); }
.lift-sign {
  display: grid; place-items: center;
  background: var(--ink); color: var(--coral);
  font: 700 calc(12 * var(--u))/1 var(--dots); letter-spacing: .1em;
}

/* ---------- coluna de texto ---------- */

.info { padding: 28px 0; }
.kicker {
  display: flex; align-items: center; gap: 10px; margin: 0 0 12px;
  font: 500 12px/1.3 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-2);
}
.kicker::before { content: ""; flex: none; width: 10px; height: 10px; border-radius: 2px; background: var(--accent); }
.info h1 {
  margin: 0 0 20px;
  font-size: clamp(44px, 6vw, 88px); font-weight: 800; line-height: .92; letter-spacing: -.035em;
  font-variation-settings: "opsz" 96;
  text-wrap: balance;
}
.info h2 {
  margin: 0 0 14px;
  font-size: clamp(34px, 3.6vw, 56px); font-weight: 750; line-height: .98; letter-spacing: -.025em;
  font-variation-settings: "opsz" 96;
  text-wrap: balance;
}
.info p { margin: 0 0 18px; max-width: 38ch; color: var(--ink-2); text-wrap: pretty; }
.info .kicker { max-width: none; }
/* os diálogos com os Claudes, nos textos dos pisos: quem fala em letra mono, pequena, à cabeça da fala, com um filete
   à esquerda na cor do piso */
.info .dialogo { margin: 0 0 18px; padding-left: 12px; border-left: 2px solid var(--accent); max-width: 38ch; }
.info .dialogo p { margin: 0 0 10px; font-size: .94em; }
.info .dialogo b { margin-right: .6em; font: 500 10px/1.6 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--ink); }   /* o nome à cabeça da fala, na mesma linha, para os diálogos caberem no andar */   /* a linha de cima não parte antes do fim da coluna (o 38ch da letra mono não conta o espaçamento) */
/* os sites dos projectos: sublinhados na cor do piso, e abrem num separador novo */
.info p a { color: inherit; text-decoration-line: underline; text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.info p a:hover { color: var(--ink); text-decoration-thickness: 3px; }
.info p a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.info .lead { font-size: 19px; color: var(--ink); max-width: 34ch; }
.info .todo { font: 500 12px/1.45 var(--mono); letter-spacing: .04em; color: var(--ink-2); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 22px; padding: 0; list-style: none; }
.chips li { padding: 6px 10px; border: 1px solid var(--line); border-radius: 99px; font: 500 12px/1 var(--mono); color: var(--ink-2); }
/* para quem usa o teclado: os objectos da sala que abrem alguma coisa, em botões que só aparecem
   quando recebem o foco (o montar.py fá-los a partir dos da sala) */
.teclado { position: absolute; z-index: 4; left: 12px; bottom: 12px; display: flex; flex-wrap: wrap; gap: 6px; }
.teclado button {
  position: absolute; width: 1px; height: 1px; padding: 0; border: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* o leitor de som, copiado da sala, igual: escondido até ter o foco */
.teclado audio { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.teclado audio:focus-within { position: static; width: 280px; height: 40px; clip-path: none; outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 99px; }
.teclado button:focus-visible {
  position: static; width: auto; height: auto; clip-path: none; padding: 9px 14px; border-radius: 99px;
  background: var(--ink); color: var(--paper); font: 600 13px/1 var(--sans); cursor: pointer;
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* ---------- o mostrador do elevador ---------- */

.lift, .lift-sair { display: none; }
/* cada andar do elevador acende enquanto o seu piso passa pelo meio do ecrã. A animação fica sempre
   aplicada (both) e apaga-se ela própria nas pontas: com «none», o Safari deixava acesos os andares
   por onde se passou a subir. Anima a visibility e não a opacity: com a opacity, no Safari do José,
   a subir e a descer ficavam dois andares acesos ao mesmo tempo (a 3 de Novembro), o que o WebKit sem
   janela não repete; a hipótese é que o Safari passa a opacity para a composição, fora da página, e
   lá alguma fica presa; a visibility não se compõe e é recalculada a cada imagem */
@keyframes lift-on { 0%, 100% { visibility: hidden; } 0.5%, 99.5% { visibility: visible; } }

/* ---------- lightbox: as demos a funcionar ---------- */

.lb {
  --accent: var(--ink);
  width: min(1040px, calc(100vw - 32px)); max-height: calc(100svh - 32px);
  margin: auto; padding: 0; border: 0; border-radius: 24px; overflow: auto;
  background: var(--paper); color: var(--ink);
  box-shadow: 0 30px 80px -20px rgb(31 28 44 / .5);
}
.lb:popover-open { display: grid; grid-template-columns: minmax(0, 1fr); }
.lb-stage {
  display: grid; place-items: center; align-content: center; gap: 20px; min-width: 0; padding: 40px 28px;
  background:
    radial-gradient(circle at 50% 45%, color-mix(in oklab, var(--accent), var(--paper) 55%), transparent 70%),
    color-mix(in oklab, var(--accent), var(--paper) 82%);
}
/* ---------- animações ao scroll (só onde o browser as tem) ---------- */

@keyframes drop { from { scale: 1 1 .02; } to { scale: 1 1 1; } }
@keyframes rise { from { scale: 1 1 .02; } to { scale: 1 1 1; } }
@keyframes show { from { opacity: 0; translate: 0 28px; } to { opacity: 1; translate: 0 0; } }

@supports (animation-timeline: view()) {
  .floor { view-timeline-inset: auto, 50%; }
  @media (prefers-reduced-motion: no-preference) {
    .drop {
      animation: drop cubic-bezier(.3, 1.45, .5, 1) both;
      animation-timeline: --floor;
      animation-range: entry calc(15% + var(--s, 0) * 12%) cover calc(32% + var(--s, 0) * 3%);
    }
    .rise {
      animation: rise cubic-bezier(.3, .6, .3, 1) both;
      animation-timeline: --floor;
      animation-range: entry 0% entry 75%;
    }
    .floor:not(.roof) .info {
      animation: show ease-out both;
      animation-timeline: --floor;
      animation-range: entry 20% cover 40%;
    }
  }
}
/* a sala que nasce: o piso 8, logo debaixo do terraço, onde se chega no mergulho, não espera pelo scroll.
   Mal a página abre, as paredes sobem, os móveis caem no sítio, uns a seguir aos outros (pelo --s), e o
   texto aparece; quando se chega pelo mergulho, fica tudo à espera que o mergulho acabe. Sem estas
   animações (menos movimento, ou as fotografias da cidade), a sala está inteira */
@media (prefers-reduced-motion: no-preference) {
  .nasce .rise { animation: rise .75s cubic-bezier(.3, .6, .3, 1) .15s both; animation-timeline: auto; animation-range: normal; }
  .nasce .drop {
    animation: drop .6s cubic-bezier(.3, 1.45, .5, 1) calc(.55s + var(--s, 0) * .22s) both;
    animation-timeline: auto; animation-range: normal;
  }
  .floor.nasce .info { animation: show .6s ease-out 1.25s both; animation-timeline: auto; animation-range: normal; }
  :root:active-view-transition .nasce :is(.rise, .drop, .info) { animation-play-state: paused; }
}

@supports (timeline-scope: none) and (animation-timeline: view()) {
  body { timeline-scope: --t, --p8, --p7, --p6, --p5, --p4, --p3, --p2, --p1, --rc, --cv, --cv2, --cv3, --cv4; }
  .lift {
    position: fixed; right: 16px; bottom: 16px; z-index: 5;
    display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 0 12px;
    width: 250px; padding: 10px 14px 10px 12px; border-radius: 14px;
    background: var(--ink); color: var(--paper);
    box-shadow: 0 14px 30px -12px rgb(31 28 44 / .55);
  }
  .lift-arrow { grid-row: span 2; color: var(--coral); font: 700 14px/1 var(--mono); animation: blink 1.2s steps(2) infinite; }
  @keyframes blink { 50% { opacity: .25; } }
  .lift-stack { position: relative; height: 44px; }
  .lift-l {
    position: absolute; inset: 0; display: flex; align-items: center; gap: 12px;
    visibility: hidden; animation: lift-on linear both;
  }
  .lift-l b { min-width: 2.2ch; font: 700 30px/1 var(--dots); color: var(--coral); }
  /* o STOP, ao lado do mostrador e com o mesmo painel: escuro, arredondado, «STOP» nos mesmos pontos
     cor de coral e «sair» por baixo, na letra pequena do mostrador; leva de volta à Boavista */
  .lift-sair {
    position: fixed; right: 278px; bottom: 16px; z-index: 5;
    display: grid; justify-items: center; align-content: center; gap: 5px; height: 64px; padding: 0 16px;
    border-radius: 14px; background: var(--ink); text-decoration: none;
    box-shadow: 0 14px 30px -12px rgb(31 28 44 / .55);
  }
  .lift-sair span { font: 700 26px/1 var(--dots); color: var(--coral); letter-spacing: .04em; text-transform: uppercase; }
  .lift-sair::after { content: "exit"; font: 500 11px/1 var(--mono); letter-spacing: .04em; text-transform: uppercase; color: rgb(243 238 230 / .75); }
  .lift-sair:hover span, .lift-sair:focus-visible span { color: #FFB09B; text-shadow: 0 0 6px rgb(244 132 102 / .6); }
  .lift-sair:focus-visible { outline: 2px solid var(--coral); outline-offset: 3px; }
  .lift-l small { font: 500 11px/1.25 var(--mono); letter-spacing: .04em; text-transform: uppercase; color: rgb(243 238 230 / .75); }
}

/* ---------- o mergulho: da Boavista para dentro do prédio ----------
   Uma transição entre páginas, sem JavaScript: o cidade.html e o index.html têm as duas
   @view-transition, e o prédio da cidade (o link .cd-entrar) e o terraço daqui têm o mesmo nome. O
   browser leva um ao outro, a crescer, enquanto a cidade se aproxima e se apaga; ao voltar para trás,
   faz o caminho ao contrário. */
@view-transition { navigation: auto; }
#terraco .scene { view-transition-name: predio; }
::view-transition-group(predio) { animation-duration: 1.1s; animation-timing-function: cubic-bezier(.7, 0, .25, 1); }
::view-transition-old(predio), ::view-transition-new(predio) { animation-duration: 1.1s; }
::view-transition-old(root) { animation: mergulho-sai 1.1s cubic-bezier(.7, 0, .25, 1) both; }
::view-transition-new(root) { animation: mergulho-entra 1.1s cubic-bezier(.7, 0, .25, 1) both; }
@keyframes mergulho-sai { to { opacity: 0; scale: 1.6; } }
@keyframes mergulho-entra { from { opacity: 0; scale: .92; } }

/* ---------- menos movimento ---------- */

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  html { scroll-behavior: auto; }
  *:not(.lift-l), *::before, *::after { animation: none !important; transition: none !important; }
  .f-memory .capsule { scale: 1; --z: 120; } /* parada no Memory, onde está o posto de envio do correio */
}

/* ---------- um piso por ecrã ---------- */
/* num ecrã largo (o MacBook Pro do José), a página começa a 20 px do topo e cada piso tem a altura do ecrã menos esses
   20 px, com o desenho ao meio: cabe inteiro e o seguinte começa onde o ecrã acaba. E o scroll tem íman: passado meio
   caminho, o piso seguinte puxa-se para o ecrã e pára sempre no mesmo sítio, a 20 px do topo. Pedidos do José a 3 de
   Novembro: «Cada andar deve caber num MacBook Pro... Sem o seguinte aparecer» e «depois de passar um ponto de não
   retorno, o próximo andar força-se no ecrã e cai exactamente no mesmo sítio». No telemóvel, os pisos são mais altos
   do que o ecrã, e o íman prendia o texto: fica como estava */
@media (min-width: 861px) {
  html:has(.floor) { scroll-snap-type: y mandatory; }   /* só na página do prédio: a cidade e o catálogo usam esta folha */
  .building { padding-top: 20px; }
  .roof { padding-top: 0; }
  .floor { scroll-snap-align: start; scroll-margin-top: 20px; }
  .scene { height: max(calc(var(--sh) * var(--u)), calc(100svh - 20px)); }
  body:has(.floor) footer.foot { scroll-snap-align: end; }
}
/* e num ecrã alto (um monitor grande), onde cabem dois pisos inteiros, cada piso fica com meio ecrã: vêem-se dois de
   cada vez e o íman pára em cada um. O desenho não passa de 600 px de alto (--u vai até 1,25 px), por isso dois pisos
   cabem a partir de 1220 px de altura (2 × 600 + os 20 do topo); num ecrã mais estreito o desenho é mais pequeno, e
   fica um piso por ecrã, como no MacBook */
@media (min-width: 861px) and (min-height: 1220px) {
  .scene { height: max(calc(var(--sh) * var(--u)), calc((100svh - 20px) / 2)); }
}

/* ---------- ecrãs estreitos ---------- */

@media (max-width: 860px) {
  .floor { grid-template-columns: 1fr; }
  .scene::before, .scene::after { display: none; }
  .info { padding: 0 0 48px; }
  .roof .info { order: -1; padding-top: 24px; }
  .lb-stage { padding: 28px 16px; }
  .lift { width: auto; right: 10px; bottom: 10px; padding: 6px 10px 6px 8px; gap: 0 8px; border-radius: 10px; }
  .lift-l small { display: none; }
  .lift-stack { width: 3.4ch; height: 30px; font: 700 22px/1 var(--dots); }
  .lift-l b { font-size: 22px; }
  .lift-sair { right: 98px; bottom: 10px; height: 42px; padding: 0 10px; border-radius: 10px; }
  .lift-sair span { font-size: 18px; }
  .lift-sair::after { display: none; }
  footer.foot { padding-bottom: 88px; }
}

/* ---------- telemóvel deitado ---------- */
/* com o telemóvel deitado, o ecrã é largo e baixo: o desenho fica à esquerda, preso ao ecrã enquanto o texto passa à
   direita, mais pequeno; o desenho mede-se pela altura do ecrã (cabe inteiro nos 100svh) e não pela largura. Sem íman,
   porque cada piso é mais alto do que o ecrã. Vem depois dos ecrãs estreitos, que também apanham alguns telemóveis
   deitados (até 860 px), e passa-lhes por cima */
@media (orientation: landscape) and (max-height: 500px) {
  :root { --u: min(calc(100svh / 540), calc(46vw / 440), 1.25px); }
  html:has(.floor) { scroll-snap-type: none; }
  .building { padding: 0 max(16px, env(safe-area-inset-right)) 0 max(16px, env(safe-area-inset-left)); }
  .floor { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 28px; align-items: start; scroll-snap-align: none; }
  .scene { position: sticky; top: 0; height: 100svh; }
  .scene::before, .scene::after { display: none; }
  .info { padding: 24px 0 40px; font-size: 15px; }
  .roof .info { order: 0; }
  .info h1 { font-size: 36px; }
  .info h2 { font-size: 28px; }
  .info p, .info .dialogo { max-width: 46ch; }
  .lift { width: auto; right: 10px; bottom: 10px; padding: 6px 10px 6px 8px; gap: 0 8px; border-radius: 10px; }
  .lift-l small { display: none; }
  .lift-stack { width: 3.4ch; height: 30px; font: 700 22px/1 var(--dots); }
  .lift-l b { font-size: 22px; }
  .lift-sair { right: 98px; bottom: 10px; height: 42px; padding: 0 10px; border-radius: 10px; }
  .lift-sair span { font-size: 18px; }
  .lift-sair::after { display: none; }
  footer.foot { padding-bottom: 72px; }
}

/* ---------- ecrãs clicáveis e protótipos a funcionar ---------- */

/* a sala deixa passar o clique para o piso; só o ecrã o apanha */
.iso:has(.ecra-btn) { z-index: 3; pointer-events: none; }
.ecra-btn {
  position: absolute; inset: 0; z-index: 1; pointer-events: auto;
  border: 0; padding: 0; background: none;
}

/* ovo da Páscoa: uma imagem pequena (480 px no lado maior), sem a moldura da lightbox */
.ovo {
  width: min-content; padding: 10px 10px 9px; border: 0; border-radius: 4px; background: #FFFDF8; overflow: visible;
  box-shadow: 0 18px 50px rgb(31 28 44 / .35);
  font: 500 11px/1.35 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-2);
}
.ovo img { display: block; width: auto; height: auto; max-width: min(480px, calc(100vw - 52px)); max-height: min(480px, calc(100svh - 96px)); }
.ovo span { display: block; margin-top: 8px; white-space: nowrap; text-align: center; }
/* o botão de fechar, redondo, no canto de cima, por fora da fotografia */
.ovo-fechar {
  position: absolute; top: -14px; right: -14px; width: 32px; height: 32px; padding: 0 0 2px;
  display: grid; place-items: center; border: 2px solid #FFFDF8; border-radius: 50%;
  background: var(--ink); color: #FFFDF8; font: 400 20px/1 var(--sans); cursor: pointer;
  box-shadow: 0 4px 14px rgb(31 28 44 / .35);
  z-index: 5;   /* sempre por cima do que a janela mostra (uma imagem ou um iframe com posição própria tapava-o no Safari) */
}
.ovo-fechar:hover { background: var(--coral); }
.ovo-fechar:focus-visible { outline: 2px solid var(--cobalt); outline-offset: 3px; }
/* as capturas de apps e de sites, também com 480 px, como os outros ovos: desistimos das janelas grandes a mostrar
   trabalho (o José Rui, a 11 de Novembro); dantes iam até 760 */
.ovo-larga img { max-width: min(480px, calc(100vw - 52px)); max-height: min(480px, calc(100svh - 110px)); }
/* várias imagens, a deslizar de lado */
.ovo-rail {
  display: flex; gap: 12px; width: min(480px, calc(100vw - 52px)); overflow-x: auto;
  scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
}
.ovo .ovo-rail img { flex: none; width: 100%; max-width: none; max-height: min(480px, calc(100svh - 110px)); object-fit: contain; scroll-snap-align: center; }
/* uma imagem alta (a captura de um terminal), com a largura das outras capturas, a correr para baixo */
.ovo-rolo {
  width: min(480px, calc(100vw - 52px)); max-height: min(480px, calc(100svh - 110px));
  overflow-y: auto; overscroll-behavior-y: contain;
}
.ovo .ovo-rolo img { width: 100%; max-width: none; max-height: none; height: auto; }
/* duas imagens, uma por cima da outra (as duas versões de um logótipo) */
.ovo-par { display: grid; gap: 18px; width: min(480px, calc(100vw - 52px)); padding: 14px 6px; }
.ovo .ovo-par img { width: 100%; max-width: none; height: auto; }
/* nas demos, o botão de fechar fica dentro do canto */
.lb > .ovo-fechar { top: 12px; right: 12px; z-index: 2; }

/* abrir e fechar com calma, nas lightboxes e nos ovos: a caixa aparece a crescer um pouco
   e a subir, e o fundo escurece e desfoca devagar; ao fechar, o mesmo ao contrário, mais
   depressa. Sem movimento reduzido, nada disto anima (ver «menos movimento») */
.lb, .ovo {
  opacity: 0; scale: .94; translate: 0 14px;
  transition: opacity .3s ease, scale .32s cubic-bezier(.4, 0, 1, 1), translate .32s cubic-bezier(.4, 0, 1, 1),
              overlay .32s allow-discrete, display .32s allow-discrete;
}
.lb:popover-open, .ovo:popover-open {
  opacity: 1; scale: 1; translate: 0 0;
  transition: opacity .4s ease, scale .55s cubic-bezier(.16, 1, .3, 1), translate .55s cubic-bezier(.16, 1, .3, 1),
              overlay .55s allow-discrete, display .55s allow-discrete;
}
@starting-style { .lb:popover-open, .ovo:popover-open { opacity: 0; scale: .94; translate: 0 14px; } }
.lb::backdrop, .ovo::backdrop {
  background: rgb(31 28 44 / 0); -webkit-backdrop-filter: blur(0); backdrop-filter: blur(0);
  transition: background .32s ease, -webkit-backdrop-filter .32s ease, backdrop-filter .32s ease,
              overlay .32s allow-discrete, display .32s allow-discrete;
}
.lb:popover-open::backdrop { background: rgb(31 28 44 / .5); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.ovo:popover-open::backdrop { background: rgb(31 28 44 / .3); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.lb:popover-open::backdrop, .ovo:popover-open::backdrop {
  transition: background .5s ease, -webkit-backdrop-filter .5s ease, backdrop-filter .5s ease,
              overlay .55s allow-discrete, display .55s allow-discrete;
}
@starting-style {
  .lb:popover-open::backdrop, .ovo:popover-open::backdrop { background: rgb(31 28 44 / 0); -webkit-backdrop-filter: blur(0); backdrop-filter: blur(0); }
}

/* lightbox com o protótipo verdadeiro dentro, a correr */
.lb-vivo { width: min(1320px, calc(100vw - 32px)); }
.lb-vivo .lb-stage { padding: 24px; gap: 14px; }
.lb-vivo iframe {
  display: block; width: 100%; aspect-ratio: 16 / 10; border: 0; border-radius: 12px;
  background: #15131C; box-shadow: 0 20px 50px -20px rgb(0 0 0 / .5);
}
.vivo-nav { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.vivo-nav a {
  font: 500 12px/1 var(--mono); letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink); text-decoration: none; padding: 9px 12px; border-radius: 999px;
  background: rgb(255 255 255 / .6);
}
.vivo-nav a:hover, .vivo-nav a:focus-visible { background: #fff; }
