/* ============================================================
   Habitar — aplicação (vanilla JS, sem dependências)
   Conceito "A Casa Contínua": landing ao entardecer → check-in →
   casa em corte (dollhouse) → cômodo com objetos em prateleiras →
   experiência de leitura como página de livro.
   Conteúdo: data/checkin.json + data/rooms/*.json
   Ilustrações: assets/icons/{room}.svg (sprites), assets/house.svg
   (corte), assets/house-front.svg (fachada) — todos com fallback.
   ============================================================ */

(function () {
  "use strict";

  /* conteúdo protegido: servido pela api/content.php só para assinantes */
  var ROOM_KEYS = [
    "rooms/01-sala-dos-alarmes",
    "rooms/02-sala-dos-espelhos",
    "rooms/03-sala-das-perdas",
    "rooms/04-sala-dos-ecos",
    "rooms/05-sala-das-escolhas",
    "rooms/06-sala-das-possibilidades",
    "rooms/07-quarto-do-descanso",
    "rooms/08-sala-dos-pesos",
    "rooms/09-biblioteca-dos-encontros"
  ];

  var FINAL_MESSAGE = "Você concluiu esta experiência. Esperamos que ela tenha oferecido acolhimento, novas perspectivas e mais compreensão sobre aquilo que vive. O autoconhecimento não acontece de uma só vez, mas cada passo dentro da sua casa interior transforma a forma como você habita a si mesmo.";
  var VISIT_INVITE = "Se desejar, compartilhe anonimamente como foi esta experiência para você. Uma palavra, uma frase ou uma descoberta podem acolher outras pessoas que também estão percorrendo sua casa interior.";

  /* casinha line-art de reserva (usada quando um asset ainda não existe) */
  var MINI_HOUSE =
    '<svg viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
      '<path d="M14 44 L48 16 L82 44" stroke="#4A3F35" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>' +
      '<path d="M22 42 V78 H74 V42" stroke="#4A3F35" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>' +
      '<rect x="42" y="56" width="12" height="22" rx="2" stroke="#4A3F35" stroke-width="4"/>' +
      '<rect x="29" y="50" width="9" height="9" rx="2" fill="#F7C873" stroke="#4A3F35" stroke-width="3"/>' +
      '<rect x="58" y="50" width="9" height="9" rx="2" stroke="#4A3F35" stroke-width="3"/>' +
      '<path d="M62 24 V14 M62 14 c 3 -2 6 1 4 4" stroke="#4A3F35" stroke-width="4" stroke-linecap="round"/>' +
    "</svg>";

  var app = document.getElementById("app");
  var DATA = { rooms: [], roomsById: {}, checkin: null, sprites: {}, houseSvg: null, frontSvg: null };
  var checkinState = { answers: [null, null, null] };
  var AUTH = { logado: false, nome: "", email: "", ativa: false, demo: false, livre: false, status: "nenhuma" };
  var CONTEUDO_OK = false;
  var PLANO = { preco_formatado: "R$ 29,90", periodo: "mês" };

  function postJson(url, corpo) {
    return fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(corpo || {}),
      credentials: "same-origin"
    }).then(function (r) {
      return r.json().then(function (d) { d._http = r.status; return d; });
    });
  }

  /* ---------- utilidades ---------- */

  function esc(s) {
    return String(s == null ? "" : s)
      .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
  }

  function store(key, value) {
    try {
      if (arguments.length === 2) { localStorage.setItem("habitar." + key, JSON.stringify(value)); return value; }
      var raw = localStorage.getItem("habitar." + key);
      return raw ? JSON.parse(raw) : null;
    } catch (e) { return null; }
  }

  function roomArticle(room) { return room.id === "quarto-do-descanso" ? "o" : "a"; }
  function roomFullName(room) { return roomArticle(room) + " " + room.name; }

  function visitedMap() { return store("visited") || {}; }
  function isVisited(roomId, objId) {
    var v = visitedMap();
    return !!(v[roomId] && v[roomId].indexOf(objId) !== -1);
  }
  function markVisited(roomId, objId) {
    var v = visitedMap();
    if (!v[roomId]) v[roomId] = [];
    if (v[roomId].indexOf(objId) === -1) v[roomId].push(objId);
    store("visited", v);
  }

  /* ícone SVG do objeto (sprite) com fallback para emoji */
  function objIcon(roomId, obj) {
    if (DATA.sprites[roomId] && DATA.sprites[roomId].indexOf('i-' + roomId + '-' + obj.id + '"') !== -1) {
      return '<svg class="obj-icon" aria-hidden="true"><use href="#i-' + esc(roomId) + "-" + esc(obj.id) + '"/></svg>';
    }
    return '<span class="obj-emoji-fallback" aria-hidden="true">' + esc(obj.emoji) + "</span>";
  }

  var REDUCED_MQ = window.matchMedia("(prefers-reduced-motion: reduce)");
  function prefersReduced() { return REDUCED_MQ.matches; }

  /* ============================================================
     A CASA VIVA — céu por hora, sons ambiente, memória e gato
     ============================================================ */
  function faixaDoDia() {
    var h = new Date().getHours();
    if (h >= 6 && h < 11) return "manha";
    if (h >= 11 && h < 17) return "tarde";
    if (h >= 17 && h < 20) return "entardecer";
    return "noite";
  }

  /* o céu da casa é sempre a noite azul estrelada (identidade da
     marca, igual à página de vendas); a hora real continua valendo
     para saudações, sons e atmosferas */
  function atualizarCeu() {
    var raiz = document.documentElement;
    ["ceu-manha", "ceu-tarde", "ceu-entardecer"].forEach(function (c) {
      raiz.classList.remove(c);
    });
    raiz.classList.add("ceu-noite");
  }

  function ehHoje(iso) {
    if (!iso) return false;
    var d = new Date(iso), n = new Date();
    return d.getFullYear() === n.getFullYear() && d.getMonth() === n.getMonth() && d.getDate() === n.getDate();
  }

  function formatarData(iso) {
    if (!iso) return "";
    try {
      return new Date(iso).toLocaleDateString("pt-BR", { day: "numeric", month: "long" });
    } catch (e) { return ""; }
  }

  /* céu 3D (js/ceu.js, three.js): monta nas telas de chegada */
  function montarCeu() {
    var tela = app.querySelector(".landing");
    if (!tela) return;
    if (window.CeuVivo) window.CeuVivo.montar(tela);
    else window.__ceuAlvo = tela;
  }

  /* diorama 3D (js/casa3d.js): casa + ilha-jardim nas telas de chegada */
  function montarCasa3D(el, opts) {
    if (!el) return;
    if (window.Casa3D) window.Casa3D.montar(el, opts || {});
    else window.__casaAlvo = { el: el, opts: opts || {} };
  }

  /* maquete 3D da casa em corte (js/casa3d.js): 9 cômodos clicáveis */
  function montarCorte3D(el, opts) {
    if (!el) return;
    if (window.Casa3D && window.Casa3D.montarCorte) window.Casa3D.montarCorte(el, opts || {});
    else window.__corteAlvo = { el: el, opts: opts || {} };
  }

  /* sala 3D (js/casa3d.js): a foto do cômodo vira um ambiente com
     profundidade e os objetos viram marcadores clicáveis */
  function montarSala3D(el, opts) {
    if (!el) return;
    if (window.Casa3D && window.Casa3D.montarSala) window.Casa3D.montarSala(el, opts || {});
    else window.__salaAlvo = { el: el, opts: opts || {} };
  }

  /* Enquanto o 3D chega, o host não mostra a ilustração antiga (evita
     a troca brusca): fica um espaço em repouso. Se o 3D não vier em
     6s — ou não vier nunca —, as reservas voltam a aparecer. */
  var TEM_WEBGL = (function () {
    try {
      var c = document.createElement("canvas");
      return !!(window.WebGLRenderingContext && (c.getContext("webgl2") || c.getContext("webgl")));
    } catch (e) { return false; }
  })();
  var TEM_MODULOS = "noModule" in HTMLScriptElement.prototype;

  function prepara3D(el) {
    if (!el) return el;
    /* sem WebGL o 3D nunca vem: as ilustrações de reserva assumem já */
    if (!TEM_WEBGL || !TEM_MODULOS || prefersReduced()) {
      el.classList.remove("aguarda3d", "oculta-antigo");
      return el;
    }
    /* seguro largo: só entra em cena se a rede estiver muito lenta —
       quem decide normalmente é o próprio módulo 3D */
    setTimeout(function () {
      if (el.classList.contains("em3d")) return;
      el.classList.add("volta-antigo");
      el.classList.remove("aguarda3d", "oculta-antigo");
    }, 12000);
    return el;
  }

  /* quarto 3D: a câmera entra no cômodo da própria maquete */
  function montarQuarto3D(el, opts) {
    if (!el) return;
    if (window.Casa3D && window.Casa3D.montarQuarto) window.Casa3D.montarQuarto(el, opts || {});
    else window.__quartoAlvo = { el: el, opts: opts || {} };
  }

  /* posição de cada cômodo na maquete (coluna 0-2, linha 0=térreo) —
     mesma ordem da casa em corte ilustrada */
  var GRADE_COMODOS = {
    "sala-dos-alarmes": [0, 2], "sala-dos-espelhos": [1, 2], "sala-das-perdas": [2, 2],
    "sala-dos-ecos": [0, 1], "sala-das-escolhas": [1, 1], "sala-das-possibilidades": [2, 1],
    "quarto-do-descanso": [0, 0], "sala-dos-pesos": [1, 0], "biblioteca-dos-encontros": [2, 0]
  };

  /* fachada ilustrada recortada só na casa (fallback do 3D) */
  function fachadaFallback() {
    return (DATA.frontSvg || MINI_HOUSE).replace('viewBox="0 0 520 440"', 'viewBox="100 30 320 400"');
  }

  /* cena fotográfica do cômodo; se o arquivo faltar, a ilustração de
     reserva volta a aparecer no lugar */
  function fotoSala(room, classe, extra) {
    return '<img class="' + classe + '" src="assets/salas/' + esc(room.id) + '.webp" alt="" ' +
      (extra || "") + ' onerror="this.parentNode.classList.add(\'sem-foto\');this.remove()">';
  }

  /* atmosfera 3D de cada ambiente: partículas próprias por cômodo */
  var ATMOS_SALA = {
    "sala-dos-alarmes": "brasas",
    "sala-dos-espelhos": "cristais",
    "sala-das-perdas": "folhas",
    "sala-dos-ecos": "ondas",
    "sala-das-escolhas": "sementes",
    "sala-das-possibilidades": "poeira-de-sol",
    "quarto-do-descanso": "poeira",
    "sala-dos-pesos": "poeira-pesada",
    "biblioteca-dos-encontros": "poeira-dourada"
  };

  function atmosDaFaixa() {
    return { manha: "polen", tarde: "brisa", entardecer: "vagalumes", noite: "vagalumes" }[faixaDoDia()];
  }

  function montarAtmos(seletor, preset) {
    var el = app.querySelector(seletor);
    if (!el || !preset) return;
    if (window.CeuVivo && window.CeuVivo.ambiente) window.CeuVivo.ambiente(el, preset);
    else window.__ceuAmb = { el: el, preset: preset };
  }

  /* tilt 3D genérico: o elemento inclina acompanhando o ponteiro
     e volta devagar quando ele sai (auto-desliga ao assentar) */
  function inclinarAoMouse(area, elemento, grausX, grausY) {
    if (prefersReduced() || !area || !elemento) return;
    var alvoX = 0, alvoY = 0, rotX = 0, rotY = 0, rafId = 0;
    function passo() {
      rotX += (alvoX - rotX) * 0.09;
      rotY += (alvoY - rotY) * 0.09;
      elemento.style.transform =
        "perspective(900px) rotateX(" + rotX.toFixed(2) + "deg) rotateY(" + rotY.toFixed(2) + "deg)";
      if (Math.abs(alvoX - rotX) + Math.abs(alvoY - rotY) > 0.02 && elemento.isConnected) {
        rafId = requestAnimationFrame(passo);
      } else { rafId = 0; }
    }
    area.addEventListener("mousemove", function (e) {
      var r = area.getBoundingClientRect();
      alvoY = ((e.clientX - r.left) / r.width - 0.5) * grausY;
      alvoX = -((e.clientY - r.top) / r.height - 0.5) * grausX;
      if (!rafId) rafId = requestAnimationFrame(passo);
    });
    area.addEventListener("mouseleave", function () {
      alvoX = 0; alvoY = 0;
      if (!rafId) rafId = requestAnimationFrame(passo);
    });
  }

  /* fachada viva da chegada: tilt 3D com o mouse, janelas com
     interruptor e a porta como convite para o check-in */
  function vivificarCasa() {
    var art = app.querySelector(".casa-viva");
    if (!art) return;

    /* clicar numa janela acende/apaga, com o clique do interruptor */
    Array.prototype.forEach.call(art.querySelectorAll(".house-window"), function (w) {
      w.addEventListener("click", function (e) {
        e.stopPropagation();
        w.classList.toggle("lit");
        tocarToque("interruptor", 0.35);
      });
    });

    /* a porta abre o check-in (com o rangido de sempre) */
    var porta = art.querySelector("#door");
    if (porta) {
      porta.addEventListener("click", function (e) {
        e.stopPropagation();
        tocarToque("porta", 0.45);
        checkinState.answers = [null, null, null];
        setTimeout(function () { go("/checkin"); }, 300);
      });
    }

    /* tilt 3D acompanhando o ponteiro, com retorno suave */
    inclinarAoMouse(art, art.querySelector("svg"), 8, 10);
  }

  /* janelas da fachada acendem uma a uma, do térreo ao sótão */
  function acenderJanelas() {
    var wins = Array.prototype.slice.call(app.querySelectorAll(".landing-art .house-window"));
    if (!wins.length) return;
    wins.sort(function (a, b) {
      return (+b.getAttribute("data-order") || 0) - (+a.getAttribute("data-order") || 0);
    });
    if (prefersReduced()) {
      wins.forEach(function (w) { w.classList.add("lit"); });
      return;
    }
    wins.forEach(function (w, i) {
      setTimeout(function () { w.classList.add("lit"); }, 380 + i * 150);
    });
  }

  /* o gato da casa dorme em um cômodo diferente a cada dia */
  var HOUSE_CAT =
    '<g class="house-cat" aria-hidden="true" transform="translate(14,96)">' +
      '<g class="cat-body">' +
        '<path d="M50 24 q9 -3 7 -12 q-1 -6 -7 -6" fill="none" stroke="#4A3F35" stroke-width="3" stroke-linecap="round"/>' +
        '<ellipse cx="30" cy="17" rx="20" ry="11" fill="#E5D6BC" stroke="#4A3F35" stroke-width="3"/>' +
        '<circle cx="13" cy="10" r="8.5" fill="#E5D6BC" stroke="#4A3F35" stroke-width="3"/>' +
        '<path d="M7.5 4 L6 -2 L12 1 Z" fill="#4A3F35"/>' +
        '<path d="M16 3 L20 -2.5 L21 4 Z" fill="#4A3F35"/>' +
        '<path d="M9 11 q2 2 4 0" fill="none" stroke="#4A3F35" stroke-width="2" stroke-linecap="round"/>' +
        '<path d="M15 12 q1.6 1.6 3.2 0" fill="none" stroke="#4A3F35" stroke-width="2" stroke-linecap="round"/>' +
      "</g>" +
    "</g>";

  function comodoDoGato() {
    if (!DATA.rooms.length) return null;
    var d = new Date();
    var seed = d.getFullYear() * 372 + (d.getMonth() + 1) * 31 + d.getDate();
    return DATA.rooms[seed % DATA.rooms.length].id;
  }

  /* ------------------------------------------------------------
     sons da casa — opcionais e discretos.
     modo "ambiente": a trilha acompanha onde você está (cômodo,
     objeto e hora do dia), com crossfade suave.
     ------------------------------------------------------------ */
  /* som ligado por padrão (modo ambiente); a escolha da pessoa fica em som2 */
  var SOM = {
    modo: store("som2") || "ambiente",
    audio: null,
    fade: null,
    contexto: null,   /* trilha sugerida pela tela atual */
    tocandoSrc: "",
    arquivos: {
      lareira:    "assets/sons/lareira.mp3",
      chuva:      "assets/sons/chuva.mp3",
      relogio:    "assets/sons/relogio.mp3",
      espelhos:   "assets/sons/espelhos.mp3",
      ecos:       "assets/sons/ecos.mp3",
      brisa:      "assets/sons/brisa.mp3",
      passaros:   "assets/sons/passaros.mp3",
      pesos:      "assets/sons/pesos.mp3",
      biblioteca: "assets/sons/biblioteca.mp3",
      grilos:     "assets/sons/grilos.mp3"
    },
    disponiveis: []
  };

  /* a ambiência de cada cômodo */
  var SOM_SALA = {
    "sala-dos-alarmes": "relogio",
    "sala-dos-espelhos": "espelhos",
    "sala-das-perdas": "chuva",
    "sala-dos-ecos": "ecos",
    "sala-das-escolhas": "brisa",
    "sala-das-possibilidades": "passaros",
    "quarto-do-descanso": "lareira",
    "sala-dos-pesos": "pesos",
    "biblioteca-dos-encontros": "biblioteca"
  };

  /* fora dos cômodos, o som segue a hora do dia */
  var SOM_FAIXA = { manha: "passaros", tarde: "brisa", entardecer: "lareira", noite: "grilos" };

  /* objetos puxam a trilha da sua natureza (por palavra no id/nome) */
  var SOM_OBJETO = [
    [/vela|lareira|brasa|chama|fogo|lampi|luminaria|abajur/, "lareira"],
    [/relog|despert|pendulo|ampulheta|metronomo/, "relogio"],
    [/chuva|nuvem|tempest|guarda-chuva|regador|lagrima/, "chuva"],
    [/janela|passar|jardim|arvore|semente|flor|folha|horizonte|varanda|ninho|ceu|sol/, "passaros"],
    [/espelho|cristal|vidro|lente|lupa|prisma|joia/, "espelhos"],
    [/livro|biblio|carta|caderno|diario|foto|album|estante|pagina|papel|pena/, "biblioteca"],
    [/pedra|peso|bau|mala|ancora|mochila|corrente|carga|caixa-forte|rocha/, "pesos"],
    [/radio|eco|sino|campainha|disco|telefone|musica|alarme|apito|sirene/, "ecos"],
    [/porta|caminho|mapa|bussola|chave|trilha|ponte|barco|bicicleta|sapato|escada/, "brisa"],
    [/cama|travesseiro|cobertor|manta|rede|poltrona|xicara|caneca|cha|sofa/, "lareira"]
  ];

  /* ------------------------------------------------------------
     toques — o som de TOCAR em cada objeto (um por objeto, curto),
     por cima da ambiência. Arquivos em assets/sons/toques/.
     ------------------------------------------------------------ */
  var TOQUE_OBJETO = {
    /* sala-dos-alarmes */
    "vela": "fosforo", "relogio": "tique", "campainha": "sino", "extintor": "vapor",
    "fio-desencapado": "faisca", "sirene": "sirene", "brasa": "fosforo", "panela-de-pressao": "vapor",
    /* sala-dos-espelhos */
    "espelho-rachado": "vidro", "mascara": "tecido", "lupa": "vidro",
    "fotografia-na-gaveta": "gaveta", "retrato-inacabado": "caneta", "janela-iluminada": "vidro",
    /* sala-das-perdas */
    "vela-apagada": "sopro", "caixa-de-lembrancas": "gaveta", "folha-seca": "vento",
    "cadeira-vazia": "madeira", "ampulheta": "areia", "broto": "terra",
    /* sala-dos-ecos */
    "papagaio": "passaro", "microfone": "radio", "caixa-de-som": "radio", "radio-antigo": "radio",
    "fones-de-ouvido": "radio", "carta-antiga": "pagina", "sino-de-vento": "carrilhao",
    /* sala-das-escolhas */
    "bussola": "metal", "encruzilhada": "passos", "chave": "chave", "balanca": "metal",
    "dado": "dado", "mochila": "tecido", "ponte": "passos", "pipa": "vento",
    /* sala-das-possibilidades */
    "janela-aberta": "janela", "escada": "passos", "mala": "gaveta", "mapa": "pagina",
    "semente": "terra", "catavento": "vento",
    /* quarto-do-descanso */
    "cama": "tecido", "poltrona": "tecido", "abajur": "interruptor",
    "janela-com-cortina": "janela", "cobertor": "tecido", "caneca": "caneca",
    /* sala-dos-pesos */
    "pedra": "pedra", "ancora": "corrente", "balao": "vento", "cesto": "madeira", "tijolo": "pedra",
    /* biblioteca-dos-encontros */
    "livro-aberto": "pagina", "estante": "madeira", "marcador-de-paginas": "pagina",
    "livro-fechado": "pagina", "caneta": "caneta"
  };

  function tocarToque(nome, volume) {
    if (SOM.modo === "silencio" || !nome) return;
    try {
      var a = new Audio("assets/sons/toques/" + nome + ".mp3");
      a.volume = volume || 0.5;
      var p = a.play();
      if (p && p.catch) p.catch(function () {});
    } catch (e) {}
  }

  function semAcentos(s) {
    try {
      return String(s || "").toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
    } catch (e) { return String(s || "").toLowerCase(); }
  }

  function trilhaDaFaixa() { return SOM_FAIXA[faixaDoDia()] || "lareira"; }

  function trilhaDoObjeto(room, obj) {
    var texto = semAcentos(obj.id + " " + obj.name);
    for (var i = 0; i < SOM_OBJETO.length; i++) {
      if (SOM_OBJETO[i][0].test(texto)) return SOM_OBJETO[i][1];
    }
    return SOM_SALA[room.id] || trilhaDaFaixa();
  }

  /* cada tela declara sua trilha; em modo ambiente ela toca na hora */
  function setContexto(trilha) {
    SOM.contexto = trilha;
    if (SOM.modo === "ambiente") aplicarSom();
  }

  function trilhaDisponivel(chave) {
    if (SOM.disponiveis.indexOf(chave) !== -1) return chave;
    if (SOM.disponiveis.indexOf("lareira") !== -1) return "lareira";
    return SOM.disponiveis[0] || null;
  }

  function checarSons() {
    Object.keys(SOM.arquivos).forEach(function (k) {
      fetch(SOM.arquivos[k], { method: "HEAD" }).then(function (r) {
        if (r.ok) {
          SOM.disponiveis.push(k);
          if (CONTEUDO_OK) montarSomToggle();
        }
      }).catch(function () {});
    });
  }

  function montarSomToggle() {
    if (!SOM.disponiveis.length || document.getElementById("somToggle")) return;
    var btn = document.createElement("button");
    btn.id = "somToggle";
    btn.className = "som-toggle";
    btn.setAttribute("aria-label", "Sons da casa");
    btn.innerHTML = '<span class="som-onda" aria-hidden="true"></span><span class="som-rotulo"></span>';
    btn.onclick = function () {
      var ordem = ["silencio", "ambiente"].concat(["lareira", "chuva"].filter(function (k) {
        return SOM.disponiveis.indexOf(k) !== -1;
      }));
      var i = ordem.indexOf(SOM.modo);
      SOM.modo = ordem[(i + 1) % ordem.length];
      store("som2", SOM.modo);
      aplicarSom();
    };
    document.body.appendChild(btn);
    pintarSomToggle();
    /* preferência lembrada: volta a tocar no primeiro toque (autoplay) */
    if (SOM.modo !== "silencio") {
      var once = function () {
        document.removeEventListener("pointerdown", once);
        aplicarSom();
      };
      document.addEventListener("pointerdown", once);
    }
  }

  function pintarSomToggle() {
    var btn = document.getElementById("somToggle");
    if (!btn) return;
    var rotulo;
    if (SOM.modo === "ambiente") {
      var nomesTrilha = {
        lareira: "lareira", chuva: "chuva", relogio: "relógio", espelhos: "cristais",
        ecos: "ecos", brisa: "brisa", passaros: "passarinhos", pesos: "madeira",
        biblioteca: "páginas", grilos: "grilos"
      };
      var atual = trilhaDisponivel(SOM.contexto || trilhaDaFaixa());
      rotulo = "ambiente · " + (nomesTrilha[atual] || "…");
    } else {
      rotulo = { silencio: "silêncio", lareira: "lareira", chuva: "chuva" }[SOM.modo] || "silêncio";
    }
    btn.querySelector(".som-rotulo").textContent = "Sons da casa · " + rotulo;
    btn.classList.toggle("tocando", SOM.modo !== "silencio");
  }

  function fadeAudio(audio, alvo, fim) {
    if (SOM.fade) clearInterval(SOM.fade);
    SOM.fade = setInterval(function () {
      var passo = 0.04;
      if (Math.abs(audio.volume - alvo) <= passo) {
        audio.volume = alvo;
        clearInterval(SOM.fade);
        SOM.fade = null;
        if (fim) fim();
      } else {
        audio.volume += (alvo > audio.volume ? passo : -passo);
      }
    }, 70);
  }

  function tocarArquivo(src) {
    if (!SOM.audio) {
      SOM.audio = new Audio();
      SOM.audio.loop = true;
      SOM.audio.volume = 0;
    }
    if (SOM.tocandoSrc === src && !SOM.audio.paused) {
      fadeAudio(SOM.audio, 0.32);
      return;
    }
    var trocar = function () {
      SOM.tocandoSrc = src;
      SOM.audio.src = src;
      SOM.audio.volume = 0;
      var p = SOM.audio.play();
      if (p && p.catch) p.catch(function () {});
      fadeAudio(SOM.audio, 0.32);
    };
    /* crossfade simples: abaixa o atual, troca, sobe o novo */
    if (!SOM.audio.paused && SOM.audio.volume > 0.02) {
      fadeAudio(SOM.audio, 0, trocar);
    } else {
      trocar();
    }
  }

  function aplicarSom() {
    pintarSomToggle();
    if (SOM.modo === "silencio") {
      if (SOM.audio) fadeAudio(SOM.audio, 0, function () { SOM.audio.pause(); SOM.tocandoSrc = ""; });
      return;
    }
    var chave = SOM.modo === "ambiente"
      ? trilhaDisponivel(SOM.contexto || trilhaDaFaixa())
      : SOM.modo;
    if (!chave) return;
    tocarArquivo(SOM.arquivos[chave]);
  }

  /* grava --i em cada filho de todo container [data-stagger] recém-renderizado */
  function applyStagger(root) {
    var groups = (root || app).querySelectorAll("[data-stagger]");
    Array.prototype.forEach.call(groups, function (group) {
      Array.prototype.forEach.call(group.children, function (child, i) {
        child.style.setProperty("--i", i);
      });
    });
  }

  var _transitioning = false;
  var _pendingSwap = null;

  function setScreen(html, opts) {
    opts = opts || {};
    document.documentElement.style.setProperty("--room", opts.roomColor || "#D8C8B4");

    /* um swap pendente nunca pode sobrescrever uma tela mais nova */
    if (_pendingSwap) { clearTimeout(_pendingSwap); _pendingSwap = null; }

    function swap() {
      _pendingSwap = null;
      _transitioning = false;
      app.innerHTML = html;
      window.scrollTo(0, 0);
      applyStagger();
      if (opts.after) opts.after();
    }

    var current = app.querySelector(".screen");
    if (!current || prefersReduced() || _transitioning) { swap(); return; }

    _transitioning = true;
    current.classList.add("is-leaving");
    if (opts.step) current.classList.add("step-mode");
    _pendingSwap = setTimeout(swap, opts.step ? 150 : 180);
  }

  function proseHTML(lines) {
    if (!lines || !lines.length) return "";
    var html = "", listOpen = false;
    lines.forEach(function (line) {
      var t = String(line);
      if (t.indexOf("• ") === 0) {
        if (!listOpen) { html += "<ul>"; listOpen = true; }
        html += "<li>" + esc(t.slice(2)) + "</li>";
        return;
      }
      if (listOpen) { html += "</ul>"; listOpen = false; }
      if (t.indexOf("## ") === 0) { html += "<h4>" + esc(t.slice(3)) + "</h4>"; return; }
      var cls = (t.charAt(0) === "—") ? ' class="dialogue"' : "";
      html += "<p" + cls + ">" + esc(t) + "</p>";
    });
    if (listOpen) html += "</ul>";
    return html;
  }

  /* ---------- roteamento ---------- */

  function go(hash) { location.hash = hash; }

  function route() {
    var h = location.hash.replace(/^#\/?/, "");
    var parts = h.split("/").filter(Boolean);

    /* ---- portão de acesso: login + assinatura ativa ---- */
    if (parts[0] === "redefinir" && parts[1]) return renderRedefinir(parts[1]);
    if (!AUTH.logado) {
      if (parts[0] === "cadastro") return renderCadastro();
      if (parts[0] === "esqueci") return renderEsqueci();
      return renderLogin();
    }
    if (parts[0] === "conta") return renderConta();
    if (!AUTH.ativa) {
      if (parts[0] === "retorno") return renderRetorno();
      return renderAssinar();
    }
    if (!CONTEUDO_OK) return renderCarregando();

    if (parts[0] === "checkin") return renderCheckin(0);
    if (parts[0] === "comodos" || parts[0] === "casa") return renderHouse();
    if (parts[0] === "caderno") return renderCaderno();
    if (parts[0] === "sala" && parts[1]) {
      var room = DATA.roomsById[parts[1]];
      if (!room) return renderHouse();
      if (parts[2]) {
        var obj = room.objects.find(function (o) { return o.id === parts[2]; });
        if (obj) return renderExperience(room, obj, 0);
      }
      return renderRoom(room);
    }
    return renderLanding();
  }

  /* ============================================================
     ACESSO — login, cadastro, assinatura e conta
     ============================================================ */
  function telaAuth(titulo, subtitulo, corpo, opts) {
    var art = fachadaFallback();
    setScreen(
      '<div class="screen landing"><div class="landing-inner auth-inner">' +
        '<div class="landing-art landing-3d auth-art aguarda3d oculta-antigo" id="authCasa" aria-hidden="true">' + art + "</div>" +
        '<h2 class="auth-title">' + esc(titulo) + "</h2>" +
        (subtitulo ? '<p class="auth-sub">' + esc(subtitulo) + "</p>" : "") +
        '<div class="auth-card">' + corpo + "</div>" +
        ((opts && opts.rodape) || "") +
      "</div></div>",
      { after: function () {
        acenderJanelas();
        montarCeu();
        montarCasa3D(prepara3D(document.getElementById("authCasa")), {});
        if (opts && opts.after) opts.after();
      } }
    );
  }

  function campo(id, rotulo, tipo, placeholder) {
    return (
      '<label class="auth-field"><span>' + esc(rotulo) + "</span>" +
      '<input id="' + id + '" type="' + tipo + '" placeholder="' + esc(placeholder || "") +
      '" autocomplete="' + (tipo === "password" ? "current-password" : tipo === "email" ? "email" : "name") + '"></label>'
    );
  }

  function erroAuth(msg) {
    var el = document.getElementById("authErro");
    if (el) { el.textContent = msg; el.style.display = "block"; }
  }

  function renderLogin() {
    telaAuth(
      "Que bom ter você de volta",
      "Entre para continuar habitando a sua casa interior.",
      campo("email", "E-mail", "email", "seu@email.com") +
        campo("senha", "Senha", "password", "Sua senha") +
        '<div id="authErro" class="auth-erro"></div>' +
        '<button class="btn btn-primary btn-block" id="entrar">Entrar</button>' +
        '<button class="btn-quiet btn-block" id="irCadastro">Ainda não tenho conta — criar agora</button>' +
        '<button class="btn-quiet btn-block" id="irEsqueci">Esqueci minha senha</button>',
      {
        rodape:
          '<p class="credit"><a href="./">Conhecer o Habitar</a></p>',
        after: function () {
          document.getElementById("irCadastro").onclick = function () { go("/cadastro"); };
          document.getElementById("irEsqueci").onclick = function () { go("/esqueci"); };
          var enviar = function () {
            var email = document.getElementById("email").value.trim();
            var senha = document.getElementById("senha").value;
            postJson("api/login.php", { email: email, senha: senha }).then(function (d) {
              if (d.ok) { iniciarSessao(); } else { erroAuth(d.erro || "Não foi possível entrar."); }
            });
          };
          document.getElementById("entrar").onclick = enviar;
          document.getElementById("senha").addEventListener("keydown", function (e) {
            if (e.key === "Enter") enviar();
          });
        }
      }
    );
  }

  function renderCadastro() {
    telaAuth(
      "Crie a sua conta",
      "Um passo antes de entrar na casa.",
      campo("nome", "Seu nome", "text", "Como podemos te chamar") +
        campo("email", "E-mail", "email", "seu@email.com") +
        campo("senha", "Senha", "password", "Mínimo de 8 caracteres") +
        '<label class="auth-aceite"><input type="checkbox" id="aceite"> ' +
          "<span>Li e aceito os <a href=\"termos\" target=\"_blank\" rel=\"noopener\">Termos de Uso</a> " +
          'e a <a href="privacidade" target="_blank" rel="noopener">Política de Privacidade</a>.</span></label>' +
        '<div id="authErro" class="auth-erro"></div>' +
        '<button class="btn btn-primary btn-block" id="criar">Criar conta</button>' +
        '<button class="btn-quiet btn-block" id="irLogin">Já tenho conta — entrar</button>',
      {
        rodape: '<p class="credit"><a href="./">Conhecer o Habitar</a></p>',
        after: function () {
          document.getElementById("irLogin").onclick = function () { go("/"); };
          document.getElementById("criar").onclick = function () {
            if (!document.getElementById("aceite").checked) {
              erroAuth("Para continuar, aceite os Termos de Uso e a Política de Privacidade.");
              return;
            }
            postJson("api/register.php", {
              nome: document.getElementById("nome").value.trim(),
              email: document.getElementById("email").value.trim(),
              senha: document.getElementById("senha").value
            }).then(function (d) {
              if (d.ok) { iniciarSessao(); } else { erroAuth(d.erro || "Não foi possível criar a conta."); }
            });
          };
        }
      }
    );
  }

  function renderEsqueci() {
    telaAuth(
      "Redefinir a senha",
      "Digite seu e-mail e enviaremos as instruções.",
      campo("email", "E-mail", "email", "seu@email.com") +
        '<div id="authErro" class="auth-erro"></div>' +
        '<div id="authOk" class="visit-saved" style="display:none;margin-top:0"></div>' +
        '<button class="btn btn-primary btn-block" id="enviar">Enviar instruções</button>' +
        '<button class="btn-quiet btn-block" id="irLogin">Voltar para o login</button>',
      {
        after: function () {
          document.getElementById("irLogin").onclick = function () { go("/"); };
          document.getElementById("enviar").onclick = function () {
            postJson("api/esqueci.php", {
              email: document.getElementById("email").value.trim()
            }).then(function (d) {
              var ok = document.getElementById("authOk");
              ok.style.display = "block";
              ok.innerHTML = esc(d.mensagem || "Verifique seu e-mail.");
              if (d.demo_link) {
                ok.innerHTML += '<br><small>Ambiente de teste — use este link: ' +
                  '<a href="' + esc(d.demo_link) + '">redefinir agora</a></small>';
              }
            });
          };
        }
      }
    );
  }

  function renderRedefinir(token) {
    telaAuth(
      "Criar uma nova senha",
      "",
      campo("senha", "Nova senha", "password", "Mínimo de 8 caracteres") +
        '<div id="authErro" class="auth-erro"></div>' +
        '<button class="btn btn-primary btn-block" id="salvar">Salvar nova senha</button>',
      {
        after: function () {
          document.getElementById("salvar").onclick = function () {
            postJson("api/redefinir.php", {
              token: token,
              senha: document.getElementById("senha").value
            }).then(function (d) {
              if (d.ok) { location.hash = "#/"; iniciarSessao(); }
              else { erroAuth(d.erro || "Não foi possível redefinir."); }
            });
          };
        }
      }
    );
  }

  function renderAssinar() {
    telaAuth(
      "Falta pouco, " + (AUTH.nome.split(" ")[0] || "") ,
      "Ative sua assinatura para abrir todas as portas da casa.",
      '<div class="plano">' +
        '<div class="plano-nome">Assinatura Habitar</div>' +
        '<div class="plano-preco">' + esc(PLANO.preco_formatado) + "<span>/" + esc(PLANO.periodo) + "</span></div>" +
        '<ul class="plano-lista">' +
          "<li>Check-in diário que indica o melhor cômodo para o seu momento</li>" +
          "<li>9 cômodos e 58 experiências guiadas de autoconhecimento</li>" +
          "<li>Diário privado — suas respostas ficam só no seu aparelho</li>" +
          "<li>Cancele quando quiser, direto no Mercado Pago</li>" +
        "</ul>" +
      "</div>" +
        '<div id="authErro" class="auth-erro"></div>' +
        '<button class="btn btn-primary btn-block" id="assinar">Assinar com Mercado Pago</button>' +
        '<button class="btn-quiet btn-block" id="sair">Sair da conta</button>',
      {
        after: function () {
          document.getElementById("sair").onclick = sairDaConta;
          document.getElementById("assinar").onclick = function () {
            var btn = document.getElementById("assinar");
            btn.disabled = true;
            btn.textContent = "Preparando o pagamento…";
            postJson("api/checkout.php").then(function (d) {
              if (d.init_point) { location.href = d.init_point; return; }
              if (d.ok) { iniciarSessao(); return; }
              btn.disabled = false;
              btn.textContent = "Assinar com Mercado Pago";
              erroAuth(d.erro || "Não foi possível iniciar o pagamento.");
            });
          };
        }
      }
    );
  }

  function renderRetorno() {
    var tentativas = 0;
    telaAuth(
      "Confirmando seu pagamento…",
      "Isso costuma levar só alguns segundos.",
      '<div class="auth-espera">A casa está acendendo as luzes para você.</div>' +
        '<button class="btn-quiet btn-block" id="tentar">Verificar de novo</button>',
      {
        after: function () {
          document.getElementById("tentar").onclick = function () { iniciarSessao(); };
          var poll = function () {
            tentativas += 1;
            fetch("api/me.php", { credentials: "same-origin" })
              .then(function (r) { return r.json(); })
              .then(function (me) {
                if (me.logado && me.assinatura && me.assinatura.ativa) { iniciarSessao(); }
                else if (tentativas < 10) { setTimeout(poll, 3000); }
              });
          };
          setTimeout(poll, 2000);
        }
      }
    );
  }

  function renderConta() {
    var statusTxt = AUTH.livre
      ? "Acesso livre, período de testes"
      : AUTH.ativa
        ? "Ativa" + (AUTH.demo ? " (modo de teste)" : "")
        : { pending: "Aguardando pagamento", paused: "Pausada", cancelled: "Cancelada" }[AUTH.status] || "Inativa";
    telaAuth(
      "Minha conta",
      "",
      '<div class="conta-linha"><span>Nome</span><strong>' + esc(AUTH.nome) + "</strong></div>" +
        '<div class="conta-linha"><span>E-mail</span><strong>' + esc(AUTH.email) + "</strong></div>" +
        '<div class="conta-linha"><span>Assinatura</span><strong>' + esc(statusTxt) + "</strong></div>" +
        (AUTH.ativa && !AUTH.demo && !AUTH.livre
          ? '<p class="auth-sub" style="margin-top:14px">Para cancelar, acesse Assinaturas na sua conta do Mercado Pago.</p>'
          : "") +
        '<button class="btn btn-primary btn-block" id="voltar" style="margin-top:16px">' +
          (AUTH.ativa ? "Voltar para a casa" : "Ativar assinatura") + "</button>" +
        '<button class="btn-quiet btn-block" id="sair">Sair da conta</button>',
      {
        after: function () {
          document.getElementById("voltar").onclick = function () { go(AUTH.ativa ? "/comodos" : "/"); };
          document.getElementById("sair").onclick = sairDaConta;
        }
      }
    );
  }

  function renderCarregando() {
    setScreen(
      '<div class="screen" style="justify-content:center;align-items:center;text-align:center">' +
        '<p class="type-lead">Abrindo a casa…</p></div>'
    );
  }

  function sairDaConta() {
    postJson("api/logout.php").then(function () { location.href = "./"; });
  }

  /* recarrega o estado de acesso e segue para o app */
  function iniciarSessao() {
    fetch("api/me.php", { credentials: "same-origin" })
      .then(function (r) { return r.json(); })
      .then(function (me) {
        AUTH.logado = !!me.logado;
        AUTH.nome = me.nome || "";
        AUTH.email = me.email || "";
        AUTH.ativa = !!(me.assinatura && me.assinatura.ativa);
        AUTH.demo = !!(me.assinatura && me.assinatura.demo);
        AUTH.livre = !!(me.assinatura && me.assinatura.livre);
        AUTH.status = (me.assinatura && me.assinatura.status) || "nenhuma";
        if (AUTH.logado && AUTH.ativa && !CONTEUDO_OK) {
          if (location.hash.indexOf("retorno") !== -1 || location.hash.indexOf("cadastro") !== -1) {
            location.hash = "#/";
          }
          carregarConteudo();
        } else {
          if (AUTH.logado && !AUTH.ativa && location.hash.indexOf("retorno") === -1) {
            location.hash = "#/";
          }
          route();
        }
      });
  }

  /* ============================================================
     LANDING — chegada ao entardecer
     ============================================================ */
  function renderLanding() {
    setContexto(trilhaDaFaixa());
    var art = fachadaFallback();
    var faixa = faixaDoDia();
    var saudacao = { manha: "Bom dia", tarde: "Boa tarde", entardecer: "Boa tarde", noite: "Boa noite" }[faixa];
    var frase = {
      manha: "A casa amanheceu com você.",
      tarde: "A casa está em silêncio, esperando sua visita.",
      entardecer: "A casa está ao entardecer — a hora dela.",
      noite: "A casa acendeu as luzes para você."
    }[faixa];
    var primeiro = (AUTH.nome || "").split(" ")[0];

    /* continuidade: se o check-in de hoje já foi feito, a casa lembra */
    var last = store("lastCheckin");
    var salaHoje = (last && ehHoje(last.at) && DATA.roomsById[last.roomId]) || null;

    var intro = salaHoje
      ? "Hoje a casa acendeu " + esc(roomFullName(salaHoje)) +
        " para você. Volte quando quiser — ou caminhe livremente pelos cômodos."
      : "Como você chega hoje? Faça o check-in e deixe a casa " +
        "indicar o melhor cômodo para este momento — ou caminhe livremente.";

    var acaoPrincipal = salaHoje
      ? "Voltar para " + esc(roomFullName(salaHoje))
      : "Fazer o check-in de hoje";

    setScreen(
      /* a casa fica parada no alto; só o texto rola, na área de baixo */
      '<div class="screen landing chegada">' +
        '<div class="landing-inner">' +
          '<div class="landing-art landing-3d aguarda3d oculta-antigo" id="landingCasa" aria-hidden="true">' + art + "</div>" +
          '<div class="landing-texto" data-stagger>' +
            "<h1>" + esc(saudacao) + (primeiro ? ", " + esc(primeiro) : "") + "</h1>" +
            '<p class="tagline">' + esc(frase) + "</p>" +
            '<p class="intro">' + intro + "</p>" +
            '<div class="landing-acoes">' +
              '<button class="btn btn-primary" id="enter">' + acaoPrincipal + "</button>" +
              '<div class="landing-links">' +
                (salaHoje ? '<button class="btn-quiet" id="redo">Refazer o check-in</button>' : "") +
                '<button class="btn-quiet" id="caderno">Meu caderno</button>' +
                '<button class="btn-quiet" id="conta">Minha conta</button>' +
              "</div>" +
            "</div>" +
          "</div>" +
        "</div>" +
      "</div>",
      { after: function () {
        acenderJanelas();
        /* na chegada o céu vem em 3D, dentro da própria cena da casa: o
           campo de estrelas do ceu.js só entra se o 3D não vier */
        if (!TEM_WEBGL || !TEM_MODULOS || prefersReduced()) montarCeu();
        /* a casa 3D é o convite: tocar nela aproxima até ela se abrir
           nos cômodos (o check-in continua no botão principal) */
        if (window.Casa3D && window.Casa3D.aquecerCorte) setTimeout(window.Casa3D.aquecerCorte, 2200);
        montarCasa3D(prepara3D(document.getElementById("landingCasa")), {
          interativo: true,
          /* o canvas cobre a tela, mas o enquadramento fica no quadro da
             casa: é o que dá espaço para ela crescer na aproximação */
          solto: true,
          /* e, cobrindo a tela, ele pode trazer o céu junto */
          ceu: true,
          aoClicarCasa: function () {
            tocarToque("porta", 0.45);
            var perto = window.Casa3D && window.Casa3D.aproximarCasa && window.Casa3D.aproximarCasa();
            if (perto) {
              /* o texto sai de cena e a casa cresce na tela inteira; a luz
                 cobre só a troca de tela, no fim (a maquete a apaga) */
              var tela = document.querySelector(".landing.chegada");
              if (tela) tela.classList.add("aproximando");
              setTimeout(function () { window.Casa3D.luz(true); }, 1020);
            }
            setTimeout(function () { go("/comodos"); }, perto ? 1400 : 300);
          }
        });
        document.getElementById("enter").onclick = function () {
          if (salaHoje) { go("/sala/" + salaHoje.id); return; }
          checkinState.answers = [null, null, null];
          go("/checkin");
        };
        var redo = document.getElementById("redo");
        if (redo) redo.onclick = function () { checkinState.answers = [null, null, null]; go("/checkin"); };
        document.getElementById("caderno").onclick = function () { go("/caderno"); };
        document.getElementById("conta").onclick = function () { go("/conta"); };
      } }
    );
  }

  /* ============================================================
     CHECK-IN
     ============================================================ */
  function renderCheckin(qIndex) {
    var q = DATA.checkin.questions[qIndex];
    var progress = DATA.checkin.questions.map(function (_, i) {
      return "<i" + (i <= qIndex ? ' class="on"' : "") + "></i>";
    }).join("");

    var longLabels = q.options.some(function (o) { return o.label.length > 18; });
    var optionsHtml = q.options.map(function (o, i) {
      var icon = o.icon ? '<span class="icon" aria-hidden="true">' + esc(o.icon) + "</span>" : "";
      return '<button class="option" data-i="' + i + '">' + icon + "<span>" + esc(o.label) + "</span></button>";
    }).join("");

    setScreen(
      '<div class="screen checkin">' +
        '<div class="topbar">' +
          '<button class="back" id="back" aria-label="Voltar">←</button>' +
        "</div>" +
        '<div class="checkin-progress">' + progress + "</div>" +
        '<span class="eyebrow">Check-in · ' + (qIndex + 1) + " de " + DATA.checkin.questions.length + "</span>" +
        "<h2>" + esc(q.title) + "</h2>" +
        '<div class="options' + (longLabels ? " long" : "") + '" data-stagger>' + optionsHtml + "</div>" +
      "</div>",
      { after: function () {
        document.getElementById("back").onclick = function () {
          if (qIndex === 0) go("/"); else renderCheckin(qIndex - 1);
        };
        Array.prototype.forEach.call(document.querySelectorAll(".option"), function (btn) {
          btn.onclick = function () {
            btn.classList.add("selected");
            checkinState.answers[qIndex] = parseInt(btn.getAttribute("data-i"), 10);
            setTimeout(function () {
              if (qIndex + 1 < DATA.checkin.questions.length) renderCheckin(qIndex + 1);
              else renderResult();
            }, 280);
          };
        });
      } }
    );
  }

  /* soma ponderada; desempate: cômodo indicado pela pergunta de maior peso */
  function computeResult() {
    var scores = {};
    DATA.checkin.questions.forEach(function (q, qi) {
      var ai = checkinState.answers[qi];
      if (ai == null) return;
      (q.options[ai].rooms || []).forEach(function (roomId) {
        scores[roomId] = (scores[roomId] || 0) + q.weight;
      });
    });

    var bestScore = -1, tied = [];
    Object.keys(scores).forEach(function (id) {
      if (scores[id] > bestScore) { bestScore = scores[id]; tied = [id]; }
      else if (scores[id] === bestScore) { tied.push(id); }
    });

    if (tied.length > 1) {
      var q3 = DATA.checkin.questions[2];
      var a3 = checkinState.answers[2];
      var q3rooms = (a3 != null && q3.options[a3].rooms) || [];
      var byQ3 = tied.filter(function (id) { return q3rooms.indexOf(id) !== -1; });
      if (byQ3.length) tied = byQ3;
    }
    var best = tied.sort(function (a, b) {
      return DATA.roomsById[a].order - DATA.roomsById[b].order;
    })[0] || DATA.rooms[0].id;

    return DATA.roomsById[best];
  }

  /* fallback: casa viva com 9 janelas, fumaça e estrelas */
  function fallbackHouseSVG(litOrder) {
    var windows = "";
    for (var i = 1; i <= 9; i++) {
      var col = (i - 1) % 3, row = Math.floor((i - 1) / 3);
      var x = 62 + col * 62, y = 96 + row * 54;
      windows += '<rect class="house-window"' + (i === litOrder ? " data-lit" : "") +
        ' x="' + x + '" y="' + y + '" width="36" height="34" rx="6" fill="#E9E2D2"/>';
    }
    return (
      '<svg viewBox="0 -46 280 346" role="img" aria-label="Casa com uma janela acesa">' +
        '<g fill="#C9AE94">' +
          '<circle class="star" cx="34"  cy="-8"  r="2.4"/>' +
          '<circle class="star" cx="70"  cy="-30" r="1.8"/>' +
          '<circle class="star" cx="132" cy="-38" r="1.8"/>' +
          '<circle class="star" cx="214" cy="-34" r="1.6"/>' +
          '<circle class="star" cx="246" cy="-14" r="2.2"/>' +
          '<circle class="star" cx="18"  cy="30"  r="1.6"/>' +
          '<circle class="star" cx="262" cy="40"  r="1.7"/>' +
        "</g>" +
        '<g id="smoke" fill="#CFC6B8">' +
          '<circle cx="206" cy="12" r="7"/>' +
          '<circle cx="206" cy="12" r="5.5"/>' +
          '<circle cx="206" cy="12" r="4.5"/>' +
        "</g>" +
        '<rect x="196" y="26" width="20" height="28" rx="3" fill="#B99B7E"/>' +
        '<rect x="192" y="21" width="28" height="8"  rx="3" fill="#A98467"/>' +
        '<path d="M140 8 L266 84 L14 84 Z" fill="#C9AE94"/>' +
        '<rect x="30" y="84" width="220" height="196" rx="10" fill="#EFE6D4" stroke="#DDD0B8" stroke-width="2"/>' +
        windows +
        '<rect x="120" y="236" width="40" height="44" rx="6" fill="#B99B7E"/>' +
      "</svg>"
    );
  }

  /* casa em corte com o cômodo indicado marcado (acende depois, via JS) */
  function dollhouseWithLight(roomId) {
    if (!DATA.houseSvg) return null;
    var host = document.createElement("div");
    host.innerHTML = DATA.houseSvg;
    var g = host.querySelector('.house-room[data-room="' + roomId + '"]');
    if (g) g.setAttribute("data-lit", "");
    return host.innerHTML;
  }

  /* fachada com a janela do cômodo marcada (acende depois, via JS) */
  function frontWithLight(order) {
    if (!DATA.frontSvg) return null;
    var host = document.createElement("div");
    host.innerHTML = DATA.frontSvg;
    var w = host.querySelector('[data-order="' + order + '"]');
    if (w) w.setAttribute("data-lit", "");
    return host.innerHTML;
  }

  /* momento-assinatura: a luz acende ~0,75s depois da casa aparecer */
  function lightUp() {
    var el = document.querySelector("[data-lit]");
    if (!el) return;
    setTimeout(function () {
      el.classList.add(el.classList.contains("house-room") ? "suggested" : "lit");
    }, prefersReduced() ? 0 : 750);
  }

  function renderResult() {
    var room = computeResult();
    store("lastCheckin", { at: new Date().toISOString(), roomId: room.id, answers: checkinState.answers });
    /* o som do cômodo indicado já começa junto com a luz que acende */
    setContexto(SOM_SALA[room.id] || trilhaDaFaixa());

    var reveal = DATA.checkin.resultReveal.replace("{sala}", roomFullName(room));
    var message = DATA.checkin.roomMessages[room.id] || "";
    /* o cômodo revelado aparece em foto, acendendo; a casa em corte
       fica de reserva caso a foto falte */
    var houseArt =
      '<img class="result-foto" src="assets/salas/' + esc(room.id) + '.webp" alt="" data-lit ' +
        'onerror="this.parentNode.classList.remove(\'com-foto\'); this.remove()">' +
      (dollhouseWithLight(room.id) || frontWithLight(room.order) || fallbackHouseSVG(room.order));

    setScreen(
      '<div class="screen result">' +
        '<p class="intro-line">' + esc(DATA.checkin.resultIntro) + "</p>" +
        '<div class="house-figure com-foto">' + houseArt + "</div>" +
        '<p class="reveal">' + esc(reveal) + "</p>" +
        '<span class="room-chip">' + esc(room.name) + "</span>" +
        '<div class="room-message">' + esc(message) + "</div>" +
        '<p class="confirm-q">' + esc(DATA.checkin.confirmQuestion) + "</p>" +
        '<div class="btn-stack">' +
          '<button class="btn btn-primary btn-block" id="yes">' + esc(DATA.checkin.confirmYes) + " n" + esc(roomArticle(room)) + " " + esc(room.name) + "</button>" +
          '<button class="btn btn-ghost btn-block" id="no">' + esc(DATA.checkin.confirmNo) + "</button>" +
        "</div>" +
      "</div>",
      { roomColor: room.color, after: function () {
        lightUp();
        montarAtmos(".result", ATMOS_SALA[room.id]);
        document.getElementById("yes").onclick = function () { renderAccept(room); };
        document.getElementById("no").onclick = function () { renderDecline(); };
      } }
    );
  }

  function renderAccept(room) {
    setScreen(
      '<div class="screen result">' +
        '<p class="intro-line">' + esc(DATA.checkin.acceptMessage[0]) + "</p>" +
        '<p class="reveal" style="margin-top:14px">' + esc(DATA.checkin.acceptMessage[1]) + "</p>" +
        '<div class="btn-stack" style="max-width:420px;width:100%">' +
          '<button class="btn btn-primary btn-block" id="enter">Entrar n' + esc(roomArticle(room)) + " " + esc(room.name) + "</button>" +
        "</div>" +
      "</div>",
      { roomColor: room.color, after: function () {
        document.getElementById("enter").onclick = function () { go("/sala/" + room.id); };
      } }
    );
  }

  function renderDecline() {
    setScreen(
      '<div class="screen result">' +
        '<p class="intro-line">' + esc(DATA.checkin.declineMessage[0]) + "</p>" +
        '<p class="reveal" style="margin-top:14px">' + esc(DATA.checkin.declineMessage[1]) + "</p>" +
        '<div class="btn-stack" style="max-width:420px;width:100%">' +
          '<button class="btn btn-primary btn-block" id="explore">' + esc(DATA.checkin.declineButton) + "</button>" +
        "</div>" +
      "</div>",
      { after: function () {
        document.getElementById("explore").onclick = function () { go("/comodos"); };
      } }
    );
  }

  /* ============================================================
     CASA — corte transversal clicável + lista
     ============================================================ */
  function renderHouse() {
    setContexto(trilhaDaFaixa());
    var v = visitedMap();
    var last = store("lastCheckin");
    var suggestedId = last && last.roomId;

    var cards = DATA.rooms.map(function (room) {
      var done = (v[room.id] || []).length;
      var total = room.objects.length;
      var progress = done > 0
        ? done + " de " + total + " visitados"
        : total + " objetos";
      return (
        '<button class="room-card' + (room.id === suggestedId ? " aceso" : "") +
          '" data-room="' + esc(room.id) + '" ' +
          'style="--card-accent:' + esc(room.color) + ";--card-tint:color-mix(in srgb, " + esc(room.color) + ' 12%, var(--surface))">' +
          '<span class="room-mini" aria-hidden="true">' +
            '<svg class="vignette" viewBox="0 0 720 320" preserveAspectRatio="xMidYMax slice" focusable="false"><use href="#vg-room"/></svg>' +
            fotoSala(room, "room-mini-foto", 'loading="lazy"') +
          "</span>" +
          "<h3>" + esc(room.name) + "</h3>" +
          "<p>" + esc(room.description) + "</p>" +
          '<div class="room-progress">' + progress + "</div>" +
        "</button>"
      );
    }).join("");

    var dollhouse = DATA.houseSvg
      /* a legenda mora DENTRO da cena: a maquete ocupa a janela inteira e
         o texto descansa no rodapé dela, sem criar uma borda de quadro */
      ? '<div class="dollhouse casa-corte aguarda3d oculta-antigo" id="dollhouse">' + DATA.houseSvg +
          '<p class="dollhouse-caption" id="dollCaption">Toque em um cômodo para entrar.</p>' +
        "</div>"
      : "";

    setScreen(
      /* a casa vem primeiro: sem cabeçalho por cima dela, e o voltar
         descansa no canto de baixo */
      '<div class="screen house-screen">' +
        '<div class="topbar topbar-canto">' +
          '<button class="back" id="back" aria-label="Início">←</button>' +
          '<span class="topbar-title">Habitar</span>' +
        "</div>" +
        dollhouse +
        '<div class="rooms-grid" data-stagger>' + cards + "</div>" +
        '<div style="text-align:center;margin-top:26px">' +
          '<button class="btn-quiet" id="redo">Refazer o check-in</button>' +
          '<button class="btn-quiet" id="conta">Minha conta</button>' +
        "</div>" +
      "</div>",
      { after: function () {
        montarAtmos(".house-screen", atmosDaFaixa());
        document.getElementById("back").onclick = function () { go("/"); };
        document.getElementById("conta").onclick = function () { go("/conta"); };
        document.getElementById("redo").onclick = function () { checkinState.answers = [null, null, null]; go("/checkin"); };
        Array.prototype.forEach.call(document.querySelectorAll(".room-card"), function (card) {
          card.onclick = function () { go("/sala/" + card.getAttribute("data-room")); };
        });

        var doll = document.getElementById("dollhouse");
        if (doll) {
          inclinarAoMouse(doll, doll.querySelector("svg"), 4, 5);
          var caption = document.getElementById("dollCaption");
          var catId = comodoDoGato();
          var algumaVisita = DATA.rooms.some(function (r) { return (v[r.id] || []).length > 0; });
          var padrao = "Toque em um cômodo para entrar." +
            (algumaVisita ? " As luzes baixas guardam suas visitas." : "");
          caption.textContent = padrao;

          /* maquete 3D por cima da ilustração (que fica de fallback) */
          function legenda(id) {
            var room = id && DATA.roomsById[id];
            if (!room) { caption.textContent = padrao; return; }
            caption.innerHTML = "<strong>" + esc(room.name) + "</strong> · " + esc(room.description) +
              (room.id === catId ? " · <em>o gato da casa dorme aqui hoje.</em>" : "");
          }
          montarCorte3D(prepara3D(doll), {
            comodos: DATA.rooms.map(function (r) {
              var pos = GRADE_COMODOS[r.id] || [0, 0];
              return { id: r.id, cor: r.color, coluna: pos[0], linha: pos[1],
                visitado: (v[r.id] || []).length > 0, sugerido: r.id === suggestedId };
            }),
            aoPassar: legenda,
            /* veio da chegada com a luz acesa: a maquete no ar apaga */
            aoPronto: function () {
              if (window.Casa3D && window.Casa3D.luz) window.Casa3D.luz(false);
            },
            /* o voo até o cômodo começa aqui, no quadro grande da casa;
               a tela da sala abre com a câmera já a caminho */
            aoEntrar: function (id) {
              tocarToque("porta", 0.3);
              var p = GRADE_COMODOS[id] || [0, 0];
              /* a sala já começa a carregar aqui, antes da troca de tela */
              var sala = DATA.roomsById[id];
              var reserva = sala ? {
                roomId: id, foto: "assets/salas/" + id + ".webp",
                objetos: sala.objects.map(function (o) {
                  return { id: o.id, nome: o.name, visitado: isVisited(id, o.id) };
                })
              } : null;
              var indo = window.Casa3D && window.Casa3D.aproximar && window.Casa3D.aproximar(p[0], p[1], reserva);
              if (!indo) { go("/sala/" + id); return; }
              /* a aproximação termina AQUI, na casa. A luz sobe e a troca
                 de tela acontece escondida: do outro lado já é o cômodo,
                 sem passar pela maquete de novo. */
              setTimeout(function () { if (window.Casa3D.luz) window.Casa3D.luz(true); }, 900);
              setTimeout(function () { go("/sala/" + id); }, 1280);
            }
          });

          Array.prototype.forEach.call(doll.querySelectorAll(".house-room"), function (g) {
            var room = DATA.roomsById[g.getAttribute("data-room")];
            if (!room) return;
            g.setAttribute("tabindex", "0");
            g.setAttribute("role", "button");
            g.setAttribute("aria-label", room.name);
            if ((v[room.id] || []).length > 0) g.classList.add("visited-lit");
            if (room.id === catId) g.insertAdjacentHTML("beforeend", HOUSE_CAT);
            g.addEventListener("click", function () { go("/sala/" + room.id); });
            g.addEventListener("keydown", function (e) {
              if (e.key === "Enter" || e.key === " ") { e.preventDefault(); go("/sala/" + room.id); }
            });
            g.addEventListener("mouseenter", function () {
              caption.innerHTML = "<strong>" + esc(room.name) + "</strong> · " + esc(room.description) +
                (room.id === catId ? " · <em>o gato da casa dorme aqui hoje.</em>" : "");
            });
            g.addEventListener("mouseleave", function () {
              caption.textContent = padrao;
            });
          });
        }
      } }
    );
  }

  /* ============================================================
     CÔMODO — objetos em prateleiras na parede
     ============================================================ */
  function renderRoom(room) {
    setContexto(SOM_SALA[room.id] || trilhaDaFaixa());
    tocarToque("porta", 0.3); /* ritual de entrada: a porta se abre */
    renderExperience._key = null;

    var objects = room.objects.map(function (obj) {
      var mark = isVisited(room.id, obj.id)
        ? '<span class="visited-seal" title="Objeto visitado" aria-label="Objeto visitado">✦</span>'
        : "";
      return (
        '<button class="object-card" data-obj="' + esc(obj.id) + '">' + mark +
          '<span class="obj-frame">' + objIcon(room.id, obj) + "</span>" +
          '<span class="obj-shelf" aria-hidden="true"></span>' +
          '<span class="obj-name">' + esc(obj.name) + "</span>" +
        "</button>"
      );
    }).join("");

    setScreen(
      '<div class="screen room-screen">' +
        '<div class="topbar">' +
          '<button class="back" id="back" aria-label="Voltar à casa">←</button>' +
          '<span class="topbar-title">Sua casa interior</span>' +
        "</div>" +
        '<figure class="room-scene room-3d aguarda3d oculta-antigo" id="salaCena" aria-hidden="true">' +
          '<svg class="vignette" viewBox="0 0 720 320" preserveAspectRatio="xMidYMax slice" focusable="false"><use href="#vg-room"/></svg>' +
          fotoSala(room, "room-foto") +
        "</figure>" +
        '<header class="room-title">' +
          "<h1>" + esc(room.name) + "</h1>" +
          "<p>" + esc(room.description) + "</p>" +
        "</header>" +
        '<p class="room-hint">Toque em um objeto para viver a experiência.</p>' +
        '<div class="room-wall">' +
          '<div class="objects-grid" data-stagger>' + objects + "</div>" +
        "</div>" +
      "</div>",
      { roomColor: room.color, after: function () {
        montarAtmos(".room-screen", ATMOS_SALA[room.id]);
        document.getElementById("back").onclick = function () { go("/comodos"); };
        Array.prototype.forEach.call(document.querySelectorAll(".object-card"), function (card) {
          card.onclick = function () { go("/sala/" + room.id + "/" + card.getAttribute("data-obj")); };
        });
        /* o cômodo em 3D: a câmera entra no cômodo da maquete; os objetos
           estão na cena — passar o ponteiro neles realça o card
           correspondente, clicar abre a experiência */
        var objetos3d = room.objects.map(function (o) {
          return { id: o.id, nome: o.name, visitado: isVisited(room.id, o.id) };
        });
        var realcar = function (objId) {
          Array.prototype.forEach.call(document.querySelectorAll(".object-card"), function (card) {
            card.classList.toggle("realce", card.getAttribute("data-obj") === objId);
          });
        };
        var abrir = function (objId) { go("/sala/" + room.id + "/" + objId); };
        montarSala3D(prepara3D(document.getElementById("salaCena")), {
          roomId: room.id,
          foto: "assets/salas/" + room.id + ".webp",
          objetos: objetos3d,
          aoPassar: realcar,
          aoClicar: abrir,
          /* pronta (ou impossível): a luz da entrada pode se apagar */
          aoPronto: function () { if (window.Casa3D && window.Casa3D.luz) window.Casa3D.luz(false); }
        });
      } }
    );
  }

  /* ============================================================
     EXPERIÊNCIA DO OBJETO — página de livro
     ============================================================ */
  function buildSteps(obj) {
    var steps = [
      { key: "metaphor" },
      { key: "psycho" },
      { key: "diary" },
      { key: "feedback" },
      { key: "care" }
    ];
    if (obj.echo) steps.push({ key: "echo" });
    steps.push({ key: "visit" });
    steps.push({ key: "final" });
    return steps;
  }

  function diaryKey(roomId, objId) { return "diary." + roomId + "." + objId; }

  function renderExperience(room, obj, stepIndex) {
    setContexto(trilhaDoObjeto(room, obj));
    var steps = buildSteps(obj);
    if (stepIndex >= steps.length) stepIndex = steps.length - 1;
    var step = steps[stepIndex];

    /* navegação dentro do mesmo objeto = "virar página" (só o miolo anima) */
    var expKey = room.id + "/" + obj.id;
    var isStep = renderExperience._key === expKey;
    renderExperience._key = expKey;

    /* ao tocar no objeto pela primeira vez, ele responde com o seu som;
       nas etapas seguintes, a página vira baixinho */
    if (!isStep) tocarToque(TOQUE_OBJETO[obj.id], 0.7);
    else tocarToque("pagina", 0.22);

    var bars = steps.map(function (_, i) {
      return "<i" + (i <= stepIndex ? ' class="on"' : "") + "></i>";
    }).join("");

    var body = "", navHtml = "";

    function defaultNav(nextLabel) {
      return '<button class="btn btn-primary btn-block" id="next">' + esc(nextLabel || "Continuar") + "</button>";
    }

    if (step.key === "metaphor") {
      body =
        '<div class="exp-title">' +
          '<span class="obj-badge">' + objIcon(room.id, obj) + "</span>" +
          "<h1>" + esc(obj.name) + "</h1>" +
        "</div>" +
        '<span class="step-label">Metáfora</span>' +
        '<div class="prose prose-cap">' + proseHTML(obj.metaphor) + "</div>";
      navHtml = defaultNav();
    }

    if (step.key === "psycho") {
      body =
        '<span class="step-label">Compreendendo</span>' +
        '<div class="prose">' + proseHTML(obj.psychoeducation) + "</div>";
      navHtml = defaultNav();
    }

    if (step.key === "diary") {
      var saved = store(diaryKey(room.id, obj.id)) || [];
      var qs = (obj.diaryQuestions || []).map(function (q, i) {
        return (
          '<div class="diary-q">' +
            '<label for="dq' + i + '">' + esc(q) + "</label>" +
            '<textarea id="dq' + i + '" placeholder="Escreva se desejar…">' + esc(saved[i] || "") + "</textarea>" +
          "</div>"
        );
      }).join("");
      body =
        '<span class="step-label">Diário</span>' +
        '<p class="diary-note">Responder é opcional. Este espaço é só seu — suas respostas ficam guardadas apenas neste dispositivo.</p>' +
        qs;
      navHtml =
        '<button class="btn btn-primary btn-block" id="next">Continuar</button>' +
        '<button class="btn-quiet" id="skip">Prefiro não responder agora</button>';
    }

    if (step.key === "feedback") {
      var answered = !!renderExperience._answered;
      var text = answered ? obj.feedbackAnswered : obj.feedbackSkipped;
      body =
        '<span class="step-label">Diário</span>' +
        '<div class="exp-card"><div class="prose">' + proseHTML(text) + "</div></div>";
      navHtml = defaultNav();
    }

    if (step.key === "care") {
      var title = (obj.care && obj.care.title) || "Um instante de cuidado";
      var careSteps = (obj.care && obj.care.steps) || [];
      body =
        '<span class="step-label">Um instante de cuidado</span>' +
        '<div class="care-card">' +
          '<div class="care-flame" aria-hidden="true"></div>' +
          "<h3>" + esc(title) + "</h3>" +
          '<div class="prose">' + proseHTML(careSteps) + "</div>" +
        "</div>";
      navHtml = defaultNav();
    }

    if (step.key === "echo") {
      body =
        '<div class="exp-card echo-card">' +
          '<div class="echo-mark">❝</div>' +
          "<blockquote>" + esc(obj.echo) + "</blockquote>" +
          '<div class="echo-source">Ecos da Casa</div>' +
        "</div>";
      navHtml = defaultNav();
    }

    if (step.key === "visit") {
      body =
        '<span class="step-label">Registro de Visita</span>' +
        '<p class="visit-note">' + esc(VISIT_INVITE) + "</p>" +
        '<textarea id="visitmsg" class="visit-textarea" placeholder="✍️ Escrever uma mensagem anônima…"></textarea>' +
        '<div id="savedNote"></div>';
      navHtml =
        '<button class="btn btn-primary btn-block" id="next">Deixar mensagem e concluir</button>' +
        '<button class="btn-quiet" id="skip">Concluir sem mensagem</button>';
    }

    if (step.key === "final") {
      markVisited(room.id, obj.id);
      body =
        '<div class="final-card">' +
          '<div class="final-house" aria-hidden="true">' + MINI_HOUSE + "</div>" +
          "<h2>Você concluiu esta experiência</h2>" +
          "<p>" + esc(FINAL_MESSAGE) + "</p>" +
        "</div>";
      navHtml =
        '<button class="btn btn-primary btn-block" id="backRoom">Voltar para ' + esc(roomArticle(room)) + " " + esc(room.name) + "</button>" +
        '<button class="btn btn-ghost btn-block" id="backHouse">Voltar à casa</button>';
    }

    setScreen(
      '<div class="screen exp-screen' + (isStep ? " step-mode" : "") + '">' +
        '<div class="topbar">' +
          '<button class="back" id="close" aria-label="Voltar ao cômodo">←</button>' +
          '<span class="topbar-title">' + esc(room.name) + " · " + esc(obj.name) + "</span>" +
        "</div>" +
        '<div class="exp-steps">' + bars + "</div>" +
        '<div class="step-anim">' +
          body +
          '<div class="exp-nav">' + navHtml + "</div>" +
        "</div>" +
      "</div>",
      { roomColor: room.color, step: isStep, after: function () {
        document.getElementById("close").onclick = function () { go("/sala/" + room.id); };

        var nextBtn = document.getElementById("next");
        var skipBtn = document.getElementById("skip");

        if (step.key === "diary") {
          nextBtn.onclick = function () {
            var answers = (obj.diaryQuestions || []).map(function (_, i) {
              return document.getElementById("dq" + i).value.trim();
            });
            store(diaryKey(room.id, obj.id), answers);
            var escreveu = answers.some(function (a) { return a.length > 0; });
            if (escreveu) {
              var meta = store("diaryMeta") || {};
              meta[room.id + "." + obj.id] = new Date().toISOString();
              store("diaryMeta", meta);
            }
            renderExperience._answered = escreveu;
            renderExperience(room, obj, stepIndex + 1);
          };
          if (skipBtn) skipBtn.onclick = function () {
            renderExperience._answered = false;
            renderExperience(room, obj, stepIndex + 1);
          };
        } else if (step.key === "visit") {
          nextBtn.onclick = function () {
            var msg = document.getElementById("visitmsg").value.trim();
            if (msg) {
              var visits = store("visits") || [];
              visits.push({ at: new Date().toISOString(), room: room.id, object: obj.id, message: msg });
              store("visits", visits);
              document.getElementById("savedNote").innerHTML =
                '<div class="visit-saved">Sua mensagem foi guardada com carinho.</div>';
              setTimeout(function () { renderExperience(room, obj, stepIndex + 1); }, 900);
            } else {
              renderExperience(room, obj, stepIndex + 1);
            }
          };
          if (skipBtn) skipBtn.onclick = function () { renderExperience(room, obj, stepIndex + 1); };
        } else if (step.key === "final") {
          document.getElementById("backRoom").onclick = function () { go("/sala/" + room.id); };
          document.getElementById("backHouse").onclick = function () { go("/comodos"); };
        } else if (nextBtn) {
          nextBtn.onclick = function () { renderExperience(room, obj, stepIndex + 1); };
        }
      } }
    );
  }

  /* ============================================================
     O CADERNO DA CASA — reler o que foi escrito nas visitas
     ============================================================ */
  function renderCaderno() {
    setContexto("biblioteca");
    var meta = store("diaryMeta") || {};
    var entradas = [];
    DATA.rooms.forEach(function (room) {
      room.objects.forEach(function (obj) {
        var saved = store(diaryKey(room.id, obj.id));
        if (!saved || !saved.some(function (a) { return a && a.length; })) return;
        entradas.push({
          room: room, obj: obj, respostas: saved,
          at: meta[room.id + "." + obj.id] || null
        });
      });
    });
    entradas.sort(function (a, b) { return (b.at || "").localeCompare(a.at || ""); });

    var visitas = (store("visits") || []).slice().reverse();

    var corpo;
    if (!entradas.length && !visitas.length) {
      corpo =
        '<div class="cad-vazio">' +
          '<p class="type-lead">As páginas ainda estão em branco.</p>' +
          "<p>Quando você escrever no diário de um objeto, suas palavras ficam guardadas " +
          "aqui — para reler quando quiser voltar ao que descobriu.</p>" +
          '<div class="btn-stack" style="max-width:320px;margin:22px auto 0">' +
            '<button class="btn btn-primary btn-block" id="irCheckin">Fazer o check-in de hoje</button>' +
          "</div>" +
        "</div>";
    } else {
      corpo = entradas.map(function (e) {
        var qa = (e.obj.diaryQuestions || []).map(function (q, i) {
          var r = e.respostas[i];
          if (!r) return "";
          return '<div class="cad-q"><div class="pergunta">' + esc(q) +
            '</div><div class="resposta">' + esc(r) + "</div></div>";
        }).join("");
        return (
          '<article class="cad-entrada" style="--cad-cor:' + esc(e.room.color) + '">' +
            '<div class="cad-topo">' +
              objIcon(e.room.id, e.obj) +
              '<div class="cad-nome"><strong>' + esc(e.obj.name) + "</strong>" +
                "<span>" + esc(e.room.name) + "</span></div>" +
              (e.at ? '<span class="cad-data">' + esc(formatarData(e.at)) + "</span>" : "") +
            "</div>" +
            qa +
            '<button class="btn-quiet cad-revisitar" data-r="' + esc(e.room.id) +
              '" data-o="' + esc(e.obj.id) + '">Revisitar este objeto</button>' +
          "</article>"
        );
      }).join("");

      if (visitas.length) {
        corpo += '<div class="cad-secao">Mensagens deixadas na casa</div>' +
          visitas.map(function (m) {
            var room = DATA.roomsById[m.room];
            return '<div class="cad-msg">' + esc(m.message) +
              "<small>" + esc(room ? room.name : m.room) +
              (m.at ? " · " + esc(formatarData(m.at)) : "") + "</small></div>";
          }).join("");
      }
    }

    setScreen(
      '<div class="screen caderno-screen">' +
        '<div class="topbar">' +
          '<button class="back" id="back" aria-label="Voltar">←</button>' +
          '<span class="topbar-title">Habitar</span>' +
        "</div>" +
        '<div class="caderno-abre">' +
          "<h1>O caderno da casa</h1>" +
          "<p>Tudo o que você escreveu enquanto visitava os cômodos, na ordem das suas descobertas.</p>" +
          '<p class="caderno-priv">Estas páginas existem apenas neste aparelho. Nem nós temos acesso.</p>' +
        "</div>" +
        '<div data-stagger>' + corpo + "</div>" +
      "</div>",
      { after: function () {
        document.getElementById("back").onclick = function () { go("/"); };
        var irc = document.getElementById("irCheckin");
        if (irc) irc.onclick = function () { checkinState.answers = [null, null, null]; go("/checkin"); };
        Array.prototype.forEach.call(document.querySelectorAll(".cad-revisitar"), function (b) {
          b.onclick = function () {
            go("/sala/" + b.getAttribute("data-r") + "/" + b.getAttribute("data-o"));
          };
        });
      } }
    );
  }

  /* ============================================================
     BOOT — conteúdo + ilustrações (todas opcionais menos o conteúdo)
     ============================================================ */
  function fetchText(url) {
    return fetch(url).then(function (r) { return r.ok ? r.text() : null; }).catch(function () { return null; });
  }

  /* conteúdo protegido: baixa da API autenticada e monta o app */
  function carregarConteudo() {
    var pedir = function (chave) {
      return fetch("api/content.php?f=" + encodeURIComponent(chave), {
        credentials: "same-origin"
      }).then(function (r) {
        if (r.status === 401 || r.status === 403) { throw new Error("sem_acesso"); }
        if (!r.ok) throw new Error(chave);
        return r.json();
      });
    };

    Promise.all([pedir("checkin")].concat(ROOM_KEYS.map(pedir)))
      .then(function (results) {
        DATA.checkin = results[0];
        DATA.rooms = results.slice(1).sort(function (a, b) { return a.order - b.order; });
        DATA.roomsById = {};
        DATA.rooms.forEach(function (room) { DATA.roomsById[room.id] = room; });

        /* sprites: namespace por cômodo (ids repetem entre cômodos) */
        var artFetches = DATA.rooms.map(function (room) {
          return fetchText("assets/icons/" + room.id + ".svg").then(function (text) {
            if (text) DATA.sprites[room.id] = text.replace(/id="obj-/g, 'id="i-' + room.id + "-");
          });
        });
        return Promise.all(artFetches);
      })
      .then(function () {
        if (!document.getElementById("spriteHost")) {
          var spriteHost = document.createElement("div");
          spriteHost.id = "spriteHost";
          spriteHost.setAttribute("aria-hidden", "true");
          spriteHost.style.display = "none";
          spriteHost.innerHTML = Object.keys(DATA.sprites)
            .map(function (k) { return DATA.sprites[k]; })
            .join("");
          document.body.appendChild(spriteHost);
        }
        CONTEUDO_OK = true;
        montarSomToggle();
        route();
      })
      .catch(function (err) {
        if (err && err.message === "sem_acesso") {
          AUTH.ativa = false;
          route();
          return;
        }
        app.innerHTML =
          '<div class="screen" style="justify-content:center;text-align:center">' +
          "<h2>Não foi possível carregar a casa</h2>" +
          '<p style="color:var(--ink-soft);margin-top:10px">Tente recarregar a página. Se persistir, fale com a gente.</p>' +
          '<p style="color:var(--ink-faint);font-size:13px;margin-top:10px">' + esc(err && err.message) + "</p>" +
          "</div>";
      });
  }

  function boot() {
    atualizarCeu();
    setInterval(atualizarCeu, 5 * 60 * 1000);
    checarSons();
    /* quando a aba fica de lado, a casa avisa que espera */
    document.addEventListener("visibilitychange", function () {
      document.title = document.hidden
        ? "A casa deixou a luz acesa — Habitar"
        : "Habitar — Uma casa para habitar a si mesmo";
    });
    /* arte pública (fachada/corte) e plano carregam junto com a sessão */
    var art = Promise.all([
      fetchText("assets/house.svg").then(function (t) { DATA.houseSvg = t; }),
      fetchText("assets/house-front.svg").then(function (t) { DATA.frontSvg = t; }),
      fetch("api/plano.php").then(function (r) { return r.json(); })
        .then(function (p) { if (p.preco_formatado) PLANO = p; })
        .catch(function () {})
    ]);
    window.addEventListener("hashchange", route);
    art.then(iniciarSessao);
  }

  boot();
})();
