/* [ENTRADA 05/09] FONTES LOCAIS (Montserrat e Inter VARIAVEIS, latin + latin-ext, os mesmos arquivos que o Google
 * Fonts servia). font-display:block: o texto espera a fonte em vez de nascer na fonte de sistema e trocar
 * (era o 'TeclaTreino aparece em outra fonte por uns instantes'). Precache no SW + preload no <head>. */
@font-face{font-family:'Montserrat';font-style:normal;font-weight:100 900;font-display:block;src:url(./fonts/montserrat-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:100 900;font-display:block;src:url(./fonts/montserrat-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Inter';font-style:normal;font-weight:100 900;font-display:block;src:url(./fonts/inter-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Inter';font-style:normal;font-weight:100 900;font-display:block;src:url(./fonts/inter-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
/* [22/07 · Luis] COSTURA DE TESTE PRA SAFE AREA HORIZONTAL.
   env(safe-area-inset-*) vale 0 em headless — foi o que deixou passar correções que
   quebravam no iPhone. Com a costura, o probe injeta os 44pt do notch e mede a barra
   que o device tem de verdade.
   ⚠️ As vars NASCEM 0 e o env() é lido em CADA USO, via max(var, env) — de propósito.
   Guardar `env()` DENTRO de uma custom property depende de substituição que nem todo
   WebKit faz; se falhasse, `var(--sal)` viraria inválido e a declaração inteira (o
   `padding` da barra, por exemplo) seria DESCARTADA no device — e passaria verde aqui.
   Com max(var, env): produção usa o env real, o probe usa a var, e nenhum dos dois
   depende do outro. */
:root{
  --sal: 0px;
  --sar: 0px;
}

:root{
  --primary:#E8690A;--primary-2:#FF7B1A;--primary-dark:#B5500A;
  --bg:#0A0A0A;--card:#141414;--card2:#1A1A1A;
  --border:#222;--border2:#2E2E2E;
  --text:#FFFFFF;--muted:#999;--dim:#666;
  --font-h:'Montserrat',sans-serif;--font-b:'Inter',sans-serif;
  --sw:240px;
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{color-scheme:dark;font-size:14px;margin:0;padding:0}
/* [v2.27] body sem margin/padding default — garante que o piano da practice
   page cole nas bordas físicas reais do dispositivo, mesmo com notch/dynamic
   island (viewport-fit=cover já permite extender atrás de cutouts).
   [v2.31] overscroll-behavior:none previne o "salto pra cima" no abrir
   (iOS Safari mostra/esconde URL bar baseado em scroll, gerando reflow do
   layout — bloquear overscroll trava o viewport). */
html, body{
  overscroll-behavior:none;
}
body{margin:0;padding:0}
/* iOS press-and-hold abre menu de seleção/cópia/lookup. Desabilita globalmente
   pra que tocar e segurar em qualquer tecla ou botão NUNCA dispare callout. */
html, body{
  -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none; user-select:none;
  -webkit-touch-callout:none;
  -webkit-tap-highlight-color:transparent;
}
/* Inputs / textareas / contenteditable preservam seleção (acessibilidade) */
input, textarea, [contenteditable="true"]{
  -webkit-user-select:text; user-select:text;
  -webkit-touch-callout:default;
}
/* [fix menu iOS] REFORÇO na PRÁTICA (piano horizontal): o menu de edição iOS (copiar/recortar/colar/setas)
   aparecia em alguns toques. Trava seleção/callout em TODA a practice-page + descendentes (inclui o teclado
   SVG e a barra) com !important — sobrepõe qualquer re-habilitação. Os inputs reais (OTP/login) NÃO vivem
   dentro da practice-page, então não são afetados. */
.practice-page, .practice-page *{
  -webkit-user-select:none!important; -moz-user-select:none!important; user-select:none!important;
  -webkit-touch-callout:none!important;
}
body{background:var(--bg);color:var(--text);font-family:var(--font-b);line-height:1.55}
/* 🔴 [16/08 · Luis: "a fonte principal de texto do app foi trocada"] OS
 * CONTROLES DE FORMULÁRIO NÃO HERDAM `font-family` — é regra do UA, não do
 * app: `<button>`, `<input>`, `<select>` e `<textarea>` caem na fonte do
 * SISTEMA (Arial aqui) a menos que alguém diga `inherit`. Metade do app dizia
 * (`.fr-chip`, `.mp-item`…), metade não — e a metade calada incluía a TABBAR
 * ("Técnica", "Músicas"…), os cards de jogos, os CTAs de praticar e os botões
 * de conta. Medido nas duas versões: o defeito é ANTIGO e discreto em 12–13px;
 * ficou gritante quando a tela nova de fórmula de compasso nasceu com 17px, e
 * foi assim que ele viu.
 * Uma regra devolve a identidade ao app inteiro. Onde já havia `font-family`
 * própria (`var(--font-h)` nos títulos, por exemplo), a especificidade da
 * classe continua vencendo — esta regra só alcança quem estava calado. */
button, input, select, textarea{ font-family:inherit; }
/* [INTEGRAÇÃO] fundo estelar global: html = base escura atrás de tudo; body transparente
   pra a constelação (#c, fixed atrás do conteúdo) aparecer em todas as telas. */
html{background:#070707}
/* [v760] !important: o v729 (fix flash branco) pôs style="background:#070707" INLINE no <body> — e fundo
   de body pinta POR CIMA de filho com z-index NEGATIVO (#c) → constelação do app-pai invisível (barra da
   prática PRETA; nas outras telas o iframe tem canvas próprio, por isso ninguém viu). O 1º paint escuro
   NÃO depende do body: html inline #070707 + boot-veil cobrem; quando este CSS aplica, já há fundo escuro. */
body{background:transparent!important}
#c{position:fixed;inset:0;width:100%;height:100%;display:block;z-index:-1;pointer-events:none}
/* [INTEGRAÇÃO] modo intro (1ª vez): esconde TODO o chrome do app desde o 1º paint — zero flash
   de imagens do app (splash/logo/banner de install) antes da experiência cobrir a tela. */
html.intro-mode{background:#070707}
html.intro-mode #app,html.intro-mode #splash,html.intro-mode #pwa-banner,html.intro-mode #menu-btn,html.intro-mode #nextBtn,html.intro-mode #prevBtn{display:none!important}
/* [FIX] durante a intro, a constelação que aparece é a do IFRAME → esconde a do app-pai pra não
   ter o "movimento inicial" duplicado (duas constelações formando). Volta após a intro. */
html.intro-mode #c{display:none!important}
/* [18/09 · FLUIDEZ A7] durante a fita (blocos/pauta) o ceu esta TAPADO e congelado, mas
   continua a ser uma camada de 2,4 Mpx que o compositor produz a cada commit. Escondido,
   o browser com canvas por software passa de 20 para 39 fps (CPU 6x). A classe e posta
   por __frLiga e tirada por __frTeardown. */
html.tt-fita-sem-ceu #c{display:none!important}
/* [2b] cintilar (clique-aqui) nos controles REAIS durante o walkthrough narrado */
.tt-beckon{position:relative;animation:ttBeckon 1.5s ease-in-out infinite;z-index:6}
.tt-beckon::after{content:"";position:absolute;inset:-3px;border-radius:inherit;pointer-events:none;
  box-shadow:0 0 0 2px rgba(255,179,71,.95),0 0 16px 4px rgba(232,105,10,.6);animation:ttBeckonRing 1.5s ease-in-out infinite}
@keyframes ttBeckon{0%,100%{transform:scale(1)}50%{transform:scale(1.05)}}
@keyframes ttBeckonRing{0%,100%{opacity:.5}50%{opacity:1}}
/* ===== [Ajuste1] P1 NATIVO (boas-vindas do returning user): wordmark + 4 pilares + piano "toque qualquer
   tecla". Roda SOBRE a constelação global (#c), SEM intro-mode (substitui o antigo iframe ?welcome=1,
   aposentado). Design portado do experiencia.html P1 (namespaced wp1- p/ não colidir). A SAÍDA p/ a P2
   (mode-select) fica a cargo da transição global da showPage (page-leaving) — background #c intocável. */
body.welcome-on #menu-btn,body.welcome-on #nextBtn,body.welcome-on #prevBtn,body.welcome-on #pwa-banner{display:none!important}
.page.welcome-p1{padding:0;min-height:100vh;position:relative;overflow:hidden}
.wp1-mark{position:absolute;left:0;right:0;top:15%;transform:translateY(-50%);text-align:center;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .8s ease}
.wp1-mark.in{opacity:1;visibility:visible}
.wp1-mark .row{font-family:var(--font-h);font-weight:900;font-size:clamp(45px,10.8vw,104px);line-height:.96;letter-spacing:.06em;color:#fff;
  transform:scale(.85) translateY(18px);opacity:0;transform-origin:center;
  transition:transform 1.05s cubic-bezier(.18,.9,.24,1), opacity .8s ease, letter-spacing 1.2s ease}
.wp1-mark .row .t{color:var(--primary)}
.wp1-mark.in .row{transform:scale(1) translateY(0);opacity:1;letter-spacing:.02em}
.wp1-pillars{position:absolute;left:0;right:0;top:37%;transform:translateY(-50%);
  display:flex;flex-direction:column;align-items:center;gap:clamp(46px,9.5vh,84px);pointer-events:none}
.wp1-pillars span{font-family:var(--font-h);font-weight:700;text-transform:uppercase;letter-spacing:.36em;
  font-size:clamp(16px,2.76vw,22px);color:var(--primary-2);
  opacity:0;visibility:hidden;transform:translateY(12px);
  transition:opacity .55s ease, transform .55s cubic-bezier(.2,.8,.2,1)}
.wp1-pillars span.show{opacity:.95;transform:none;visibility:visible}
/* [22/07 · Luis] SAÍDA cinematográfica do P1 nativo — portada 1:1 do #p1.exiting
   do experiencia.html: a marca sobe com blur+escala, os pilares voam pra cima
   escalonados, o CTA some. Antes o showPage só dava o fade-out plano da página
   (ttPageExit) e a marca sumia SECA — faltava a coreografia da experiência.
   A classe .wp1-exiting entra JÁ no toque (em _welcomeAdvance), ANTES do showPage,
   então a saída roda sobre o #c persistente e a P2 (mode-select) entra depois. */
.welcome-p1.wp1-exiting .wp1-mark .row{opacity:0;transform:scale(1.18) translateY(-12px);filter:blur(7px);
  transition:opacity .6s ease, transform .78s cubic-bezier(.4,0,.6,1), filter .6s ease}
.welcome-p1.wp1-exiting .wp1-pillars span{opacity:0;transform:translateY(-16px) scale(1.12);filter:blur(6px);
  transition:opacity .5s ease, transform .62s ease, filter .5s ease}
.welcome-p1.wp1-exiting .wp1-pillars span:nth-child(2){transition-delay:.05s}
.welcome-p1.wp1-exiting .wp1-pillars span:nth-child(3){transition-delay:.10s}
.welcome-p1.wp1-exiting .wp1-pillars span:nth-child(4){transition-delay:.15s}
.welcome-p1.wp1-exiting .wp1-cta{opacity:0;transition:opacity .4s ease}
/* [21/07 · Luis] Medindo o desktop achei o MESMO defeito no celular estreito:
 * em 390×844 e 360×780 a marca e o 1º pilar se sobrepunham (6px e 11px). A
 * coluna de pilares cresce com o gap (até 84px × 3), e em tela estreita ela
 * sobe demais. Encolher o gap baixa o topo da coluna e resolve sem mexer em
 * porcentagem — o que evita reajustar os 3 blocos de uma vez. */
@media (max-width:440px){
  .wp1-pillars{ gap:clamp(30px,6.6vh,58px); }
}
.wp1-cta{position:absolute;left:0;right:0;top:78%;transform:translateY(-50%);text-align:center;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .8s ease}
.wp1-cta.in{opacity:1;visibility:visible}
.wp1-cta .lbl{font-family:var(--font-h);font-weight:700;font-size:clamp(9px,1.3vw,11px);letter-spacing:.18em;text-transform:uppercase;color:var(--primary-2);margin-bottom:14px}
.wp1-cta .lbl b{color:#fff}
.wp1-cta .lbl.pulse{animation:wp1Pulse 2.4s ease-in-out infinite}
@keyframes wp1Pulse{0%,100%{opacity:.55}50%{opacity:1}}
.wp1-piano{position:relative;width:clamp(230px,54vw,380px);height:clamp(72px,12vh,98px);margin:0 auto;pointer-events:auto}

/* ============================================================
 * [21/07 · Luis] CAPA DE RETORNO NO DESKTOP — fim da sobreposição
 *
 * Relato: "a palavra Modos sobrepondo o título Tecla Treino".
 * MEDIDO em 6 janelas (1024×640 a 1920×1080): a marca e os pilares se
 * sobrepunham em TODAS, de 7px a 69px.
 *
 * Causa: os 3 blocos são absolutos por porcentagem (15% · 37% · 78%) com
 * translateY(-50%) — ou seja, é o CENTRO deles que fica na porcentagem. A
 * coluna de pilares tem ~390px de altura (gap de até 84px entre 4 palavras),
 * então metade dela sobe 195px acima de 37% e invade a marca. Em tela de
 * celular a conta fecha; em desktop, não.
 *
 * Conserto: no desktop os 3 blocos saem do absoluto e entram em FLUXO, numa
 * coluna com gap real — não dá pra sobrepor o que está em fluxo. E os pilares
 * viram LINHA: numa tela larga eles cabem lado a lado, o que devolve altura
 * pro resto respirar. As animações continuam nos FILHOS (.row, span.show),
 * intocadas — aqui só o contêiner muda.
 * ========================================================= */
@media (min-width:600px){
  /* `html.dt body` (2 elementos + 3 classes) vence o
   * `body.mode-interactive .page.visible{display:block}` da linha ~3462, que
   * tem o MESMO peso da forma curta e vem depois no arquivo. */
  /* 🔴🔑🔑 [18/09 · LOTE 4i · Luis] *"versoes para tablet e pc, a tela inicial ao
   * abrir o app esta completamente vazia, apenas o fundo estrelado e visivel, a
   * tela inicial verdadeira aparece ao rollar o scroll todo para baixo"* — e
   * depois: *"isso nao fica restrito a tela inicial, tudo no tablet e pc esta com
   * espaco vazio no scroll"*.
   *
   * O `.visible` QUE FALTAVA. O comentário acima diz, com todas as letras, que
   * `html.dt body` foi escolhido para VENCER o `.page.visible{display:block}` — e
   * venceu. Só que venceu também o `body.mode-interactive .page{display:none}`
   * que esconde as telas INACTIVAS: um seletor que ganha por especificidade ganha
   * aos DOIS, e a `welcome` passou a estar montada para sempre, por baixo de
   * qualquer tela, em toda a caixa com `dt`.
   *
   * MEDIDO no ar (staging-330), tela a tela, sem preâmbulo nenhum:
   *   B  810x1080  as 20 telas com `welcome chegada` montadas por baixo
   *                (`toc`: 10220 px de documento para 1080 de janela)
   *   M1 1440x900  idem, as 20
   *   P1 390x844   ZERO — o telemóvel não tem `dt`, e por isso ele nunca viu isto
   * O primeiro ecrã ficava com a `welcome` (que sem `body.welcome-on` não pinta
   * nada senão o fundo) e a tela verdadeira começava exactamente um ecrã abaixo.
   *
   * 🔑 A CURA É O `.visible`, não baixar a especificidade. Com ele o seletor
   * continua a ganhar ao `.page.visible{display:block}` (é o que este bloco
   * precisa: pôr a tela ACTIVA em coluna) e deixa de ter opinião sobre as
   * inactivas, que voltam ao `display:none` de toda a casa.
   * ⚠️ A BANCADA NÃO VIA ISTO: o preâmbulo do `novaAba` navega com `showPage` a
   * partir de um estado já limpo, e a sonda tela-a-tela COM preâmbulo deu 0
   * falhas nas mesmas caixas onde a sonda CRUA deu 20/20. Como o `reducedMotion`
   * do lote 4h, é um defeito que só existe no estado em que o aluno abre o app. */
  html.dt body .page.welcome-p1.visible{
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:clamp(28px,5vh,64px);
    padding:clamp(28px,6vh,72px) clamp(24px,4vw,64px);
    min-height:100vh; box-sizing:border-box;
  }
  html.dt body .welcome-p1 .wp1-mark,
  html.dt body .welcome-p1 .wp1-pillars,
  html.dt body .welcome-p1 .wp1-cta{
    position:static; top:auto; left:auto; right:auto; transform:none;
    width:100%; flex:0 0 auto;
  }
  /* pilares lado a lado; wrap é a rede pra janela estreita ou idioma comprido */
  html.dt body .welcome-p1 .wp1-pillars{
    flex-direction:row; flex-wrap:wrap; justify-content:center;
    gap:clamp(18px,2.4vw,44px);
  }
  html.dt body .welcome-p1 .wp1-pillars span{ white-space:nowrap; }
  /* a marca não precisa mais de 104px de altura pra caber: limita e centraliza */
  html.dt body .welcome-p1 .wp1-mark .row{ font-size:clamp(44px,7.2vw,92px); }
}
.wp1-wkrow{position:absolute;inset:0;display:flex;gap:2px}
.wp1-wk{flex:1;border-radius:0 0 7px 7px;
  background:linear-gradient(180deg,rgba(255,245,235,.17),rgba(255,238,224,.045));
  border:1px solid rgba(255,225,200,.16);border-top:0;
  box-shadow:inset 0 -11px 15px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.10);transition:background .16s}
.wp1-bkrow{position:absolute;inset:0;pointer-events:none}
.wp1-bk{position:absolute;top:0;height:62%;width:9%;border-radius:0 0 5px 5px;pointer-events:auto;
  background:linear-gradient(180deg,#1a1510,#050505);border:1px solid rgba(255,170,60,.18);border-top:0;
  box-shadow:0 5px 9px rgba(0,0,0,.6), inset 0 -4px 7px rgba(0,0,0,.6);transition:background .16s}
.wp1-wk.lit{background:linear-gradient(180deg,var(--primary-2),var(--primary));border-color:var(--primary);
  box-shadow:0 0 22px rgba(255,170,60,.55);transform:translateY(2px)}
.wp1-bk.lit{background:linear-gradient(180deg,#FFC979,var(--primary));box-shadow:0 0 18px rgba(255,170,60,.75)}
/* ===== [30/08 · LOTE 2] A CHEGADA (tela 0, mini piano) =====================
 * So a CAIXA e a SAIDA sao novas: marca, CTA e piano reusam as classes .wp1-*
 * acima, que nunca foram escopadas em .welcome-p1. A page antiga fica intocada.
 * A marca sobe de 15% para 34% porque a chegada nao tem os 4 pilares entre ela
 * e o piano — no lugar deles havia um buraco. */
.page.tt-chegada{padding:0;min-height:100vh;position:relative;overflow:hidden}
.tt-chegada .wp1-mark{top:34%}
/* [11/09 · Luis] "VER O APP" — o gesto inicial com tamanho de alvo.
 * Nasce invisivel como a marca e o CTA (o __ttChegadaReveal e quem o acende),
 * e e o unico elemento CLICAVEL da metade de cima: a .wp1-mark tem
 * pointer-events:none e a caixa da page nao intercepta nada. */
.ch-verapp{position:absolute;left:50%;top:47%;transform:translate(-50%,-50%);
  opacity:0;visibility:hidden;transition:opacity .8s ease;pointer-events:auto;
  border:0;cursor:pointer;border-radius:999px;
  padding:14px clamp(26px,7vw,40px);
  font-family:var(--font-h);font-weight:800;font-size:clamp(15px,4vw,18px);
  letter-spacing:.01em;color:#fff;white-space:nowrap;
  background:linear-gradient(160deg,var(--primary),var(--primary-dark));
  box-shadow:0 10px 30px rgba(232,105,10,.34),0 0 0 1px rgba(255,255,255,.08) inset;
  -webkit-tap-highlight-color:transparent}
.ch-verapp.in{opacity:1;visibility:visible}
.ch-verapp:active{transform:translate(-50%,-50%) scale(.97)}
.tt-chegada.ch-exiting .ch-verapp{opacity:0}
/* [12/09 · Luis] "INSTALAR O APP" — o SEGUNDO botao, logo abaixo do primeiro.
 * Secundario de proposito: o convite principal da chegada continua a ser entrar
 * (quem instala sem ter visto nada instala menos), mas a porta de instalar deixa
 * de estar escondida atras de tres toques — ela e o passo que o funil quer.
 * Os dois sao `top` ABSOLUTOS a partir do mesmo 47% (envolve-los num wrapper
 * partiria o override do desktop la em baixo), entao o `calc` mede de CENTRO a
 * centro: 60px = metade do irmao (~47px de altura: 2x14 de padding + ~19 de
 * linha) + metade deste (~40px) + respiro. MEDIDO em tres larguras: 17px de vao
 * em 390x844 e em 320x568, 15px em 412x915 — e 46px de folga ate ao "toque
 * qualquer tecla" no pior caso (o 320x568). */
.ch-instalar{position:absolute;left:50%;top:calc(47% + 60px);transform:translate(-50%,-50%);
  opacity:0;visibility:hidden;transition:opacity .8s ease;pointer-events:auto;
  cursor:pointer;border-radius:999px;
  border:1.8px solid rgba(232,105,10,.62);
  padding:11px clamp(22px,6vw,34px);
  font-family:var(--font-h);font-weight:800;font-size:clamp(13.5px,3.5vw,16px);
  letter-spacing:.01em;color:var(--primary-2);white-space:nowrap;
  background:rgba(232,105,10,.09);
  -webkit-tap-highlight-color:transparent}
.ch-instalar.in{opacity:1;visibility:visible}
.ch-instalar:active{transform:translate(-50%,-50%) scale(.97)}
.tt-chegada.ch-exiting .ch-instalar{opacity:0}
/* 🔑 [12/09 · Luis] DENTRO DO APP INSTALADO OS DOIS BOTOES SOMEM. *"o botao ver o
 * app deve sumir com o aplicativo instalado, assim como botao instalar."* — e e a
 * mesma razao de dois lados: "Instalar o App" oferece ali um passo que nao ha, e
 * "Ver o app" nomeia uma coisa onde a pessoa JA esta. Sobra o piano com "toque
 * qualquer tecla", que e o desenho anterior a 11/09 e continua a ser o gesto que
 * destrava o AudioContext do iOS — nao se perdeu porta nenhuma, so o rotulo.
 *
 * A classe `tt-instalado` e escrita no <head>, no parse, a partir do MESMO latch
 * que alimenta `__ttEstaInstalado()`: nao ha segunda leitura a divergir do
 * handler. O handler ainda faria a coisa certa (fail-open, entra no app);
 * esconder e so nao prometer um passo que nao ha.
 *
 * 🔴 CADA BOTAO NA SUA REGRA, e nao uma lista de dois seletores: e assim que um
 * mutante consegue derrubar so um deles. Juntos numa linha, qualquer mutante que
 * mexesse nela mexia nos dois ao mesmo tempo — e um mutante que mata tudo nao
 * aponta nada. O do INSTALAR mora no bloco abaixo, com o resto das condicoes
 * dele (`:not(.tt-instalado)` dentro da regra de mostrar). */
html.tt-instalado .ch-verapp{display:none}
/* 🔑 [12/09 · Luis] O CONVITE APARECE POR OMISSAO; SO O ANUNCIO NAO O VE.
 * *"WhatsApp, Telegram, link colado, e-mail. tudo isso deve ter botao instalar.
 * menos as campanhas de anuncios que nao."*
 *
 * 🔴 NAO SE TENTA MAIS RECONHECER O ANUNCIO PELO `fbclid`: o Instagram carimba
 * `fbclid` em todo clique de saida, o da bio incluido, e a 1a versao desta regra
 * escondeu o botao de quem devia ve-lo (foi ao ar no 294 e o Luis apanhou-o). A
 * marca `tt-ads` e escrita no <head> quando a URL traz `?src=ads` — posta pelo
 * Luis no painel de anuncios, que e o unico sitio onde os dois se separam.
 *
 * As TRES regras de esconder tem especificidade >= a base e nao competem entre
 * si (nenhuma regra de MOSTRAR existe para elas vencerem): `tt-ads` (o anuncio),
 * `tt-instalado` (quem ja tem o app nao tem passo a dar) e `(pointer: fine)`, o
 * par CSS do `__ttPortaInstalarDispensada()` — no computador nao ha instalacao
 * de PWA. Em qualquer delas o handler ainda faria a coisa certa (fail-open,
 * entra no app); esconder e so nao prometer um passo que nao ha. */
html.tt-ads .ch-instalar{display:none}
html.tt-instalado .ch-instalar{display:none}
@media (pointer:fine){ .ch-instalar{display:none} }
/* ===== 🔑 [14/09 · Luis] O CARIMBO DO COMPRADOR (`html.tt-compra`) ==========
 * Ordem: *"coloque um carimbo no link para instalar o app que chega no e-mail do
 * comprador (...) ele nao consegue a opcao ver o app, ve o install gate, instala,
 * e a primeira tela do app tem botao de ativar acesso."*
 *
 * A classe vem do <head>, no parse, de `?src=compra` — e cala-se sozinha depois
 * de a pessoa ter ativado (ver o comentario la, que e onde a decisao mora).
 *
 * 🔴 CADA REGRA SOZINHA, como as tres do INSTALAR logo acima e pela mesma razao:
 * juntas num seletor so, um mutante que mexesse nelas mataria as tres de uma vez
 * — e um mutante que mata tudo nao aponta nada. */
/* 1. o "Ver o app" SAI. E a metade "nao consegue a opcao ver o app" da ordem, e
 *    vale em TODO lado — inclusive onde o install-gate nao pode pintar (webview,
 *    computador), que e justamente onde o comprador o veria. */
html.tt-compra .ch-verapp{display:none}
/* 2. o "Ativar acesso" ENTRA, e herda o lugar e o desenho que o "Ver o app"
 *    acabou de deixar vago (mesmo `top:47%`, mesma pilula cheia): a acao primaria
 *    da tela passa a ser a dele. Nasce invisivel como os irmaos — quem o acende e
 *    o __ttChegadaReveal.
 * 🔴 `white-space:normal` e NAO o `nowrap` dos irmaos: o rotulo e uma frase
 *    ("Ja comprei — ativar acesso") e em it/ko/de ela e mais comprida que a tela
 *    de 320 px. Duas linhas centradas cabem; uma linha so transbordava.
 * 🔴 E SEM `text-wrap:balance`, que eu pus e tive de tirar: ele iguala o
 *    COMPRIMENTO das linhas, e num ecra de 320 px o italiano
 *    ("Ho gia acquistato — attiva l'accesso") virava um bloco QUADRADO de 4
 *    linhas curtas a ~160 px de largura, encostado a marca — em vez de 2 linhas
 *    a encher os 275 px que a caixa tinha para dar. Apanhado no PRINT, nao na
 *    regua: ela media transbordo e vizinhos, e o bloco nao transbordava nada. */
.ch-ativar{position:absolute;left:50%;top:47%;transform:translate(-50%,-50%);
  opacity:0;visibility:hidden;transition:opacity .8s ease;pointer-events:auto;
  border:0;cursor:pointer;border-radius:22px;
  /* 🔴 `width:max-content` NAO E ENFEITE. Um bloco ABSOLUTO com `left` e sem
   *    `right` faz shrink-to-fit contra o espaco que sobra — que aqui e
   *    `100% - left` = METADE da tela (160 px num ecra de 320). O `max-width`
   *    abaixo nunca chegava a mandar: en/it quebravam em 4 linhas dentro de uma
   *    caixa de 160 px, com 275 px disponiveis. Os irmaos escapam a isto por
   *    acidente (o `nowrap` faz a linha transbordar a caixa e o translate
   *    centra-a); um botao que QUEBRA nao tem essa sorte. `max-content` pede a
   *    largura de uma linha so e o `max-width` corta-a onde deve. */
  padding:13px clamp(22px,6vw,34px);width:max-content;max-width:min(86vw,420px);
  font-family:var(--font-h);font-weight:800;font-size:clamp(14px,3.7vw,17px);
  line-height:1.25;letter-spacing:.01em;color:#fff;text-align:center;
  white-space:normal;
  background:linear-gradient(160deg,var(--primary),var(--primary-dark));
  box-shadow:0 10px 30px rgba(232,105,10,.34),0 0 0 1px rgba(255,255,255,.08) inset;
  -webkit-tap-highlight-color:transparent}
.ch-ativar.in{opacity:1;visibility:visible}
.ch-ativar:active{transform:translate(-50%,-50%) scale(.97)}
.tt-chegada.ch-exiting .ch-ativar{opacity:0}
/* 3. o botao so existe COM o carimbo. Sem ele a chegada e byte a byte a de
 *    ontem — o trafego que nao vem do e-mail nao ve nada de novo. */
.ch-ativar{display:none}
html.tt-compra .ch-ativar{display:block}
/* 4. e o INSTALAR desce 18 px: ele mede de centro a centro a partir do mesmo
 *    47%, e o botao de cima agora pode ter DUAS linhas (+19 px). Com os 60 px de
 *    ontem sobravam ~7 px de vao no pior idioma; com 78 sobram ~25, e a folga ate
 *    ao "toque qualquer tecla" (78 %) cai de 46 para 28 px no pior ecra (320x568).
 *    So sob o carimbo: para todo o resto do trafego o 60 px fica intocado. */
html.tt-compra .ch-instalar{top:calc(47% + 78px)}
.tt-chegada.ch-exiting{opacity:0;transition:opacity .32s ease}
/* [ENTRADA 05/09] rede: a chegada que ja esmaeceu NUNCA e re-animada pelo ttPageExit (que comeca
 * em opacity:1 e a fazia reaparecer por um instante antes do mode-select). */
.tt-chegada.ch-exiting.page-leaving{animation:none!important;opacity:0!important}
/* [ENTRADA 05/09] O DISSOLVE DO VEU DE BOOT: fundo esmaece em .7s; a logo cresce 14 % e some. O veu e
 * o img sao estaticos no index (pintam no 1o byte); aqui mora so a saida. */
#boot-veil{transition:opacity .7s cubic-bezier(.4,0,.2,1)}
#boot-veil.bv-out{opacity:0}
#boot-logo{transition:transform .8s cubic-bezier(.2,.7,.2,1),opacity .55s ease .05s;will-change:transform,opacity}
#boot-veil.bv-out #boot-logo{transform:scale(1.14);opacity:0}
@media (prefers-reduced-motion:reduce){
  .tt-chegada.ch-exiting{transition:none}
  #boot-veil{transition-duration:.2s} #boot-logo{transition:opacity .2s ease} #boot-veil.bv-out #boot-logo{transform:none}
  .tt-chegada .wp1-cta .lbl.pulse{animation:none}
}
@media (min-width:600px){
  /* mesmo tratamento que o P1 ganhou no desktop: a page vira coluna centrada e
   * os filhos saem do posicionamento absoluto (ver o bloco html.dt acima). */
  /* [18/09 · LOTE 4i] o mesmo `.visible` que falta ao bloco da `welcome-p1`,
   * pela mesma razão e com a mesma prova — ver a lápide lá. Esta regra é a
   * irmã gémea: foi escrita a copiar aquela ("mesmo tratamento que o P1
   * ganhou no desktop") e herdou o defeito junto com o desenho. */
  html.dt body .page.tt-chegada.visible{
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:clamp(28px,5vh,64px);
    padding:clamp(28px,6vh,72px) clamp(24px,4vw,64px);
    min-height:100vh; box-sizing:border-box;
  }
  html.dt body .tt-chegada .wp1-mark,
  html.dt body .tt-chegada .ch-verapp,
  html.dt body .tt-chegada .ch-instalar,
  html.dt body .tt-chegada .ch-ativar,
  html.dt body .tt-chegada .wp1-cta{
    position:static; top:auto; left:auto; right:auto; transform:none;
    width:100%; flex:0 0 auto;
  }
  /* no computador o botao nao ocupa a largura toda: encolhe e centra-se */
  html.dt body .tt-chegada .ch-verapp,
  html.dt body .tt-chegada .ch-instalar,
  html.dt body .tt-chegada .ch-ativar{ width:auto; align-self:center; }
  html.dt body .tt-chegada .ch-verapp:active,
  html.dt body .tt-chegada .ch-instalar:active,
  html.dt body .tt-chegada .ch-ativar:active{ transform:scale(.97); }
  /* [14/09] no computador a coluna e de flex: o `top` do carimbo nao se aplica
   * (os filhos ja sao `position:static` acima) e nao ha nada a empurrar. */
  html.dt body .tt-chegada .wp1-mark .row{ font-size:clamp(44px,7.2vw,92px); }
}
/* [2b] no tutorial narrado, o guiado NÃO pode ser pulado → esconde o botão Pular */
html.tt-tutorial .nav-tag.is-pb-nav.is-guided-skip{display:none!important}
/* [2b] opções do menu de inversões SEM os números ①②③ (pedido anterior do Luis) */
#inversion-menu .iv-opt .t-opt-dot{display:none}
/* [2b] cintilar do item de menu = anel validado do preview (.wt-ca == .tt-beckon). NÃO recriar
   com bg/borda; NÃO usar chip "clique aqui" (no preview a 1ª pisca só com o anel, "sem chip"). */
body.mode-interactive{display:flex}

/* ============ SIDEBAR (drawer — hidden by default) ============ */
.sidebar{width:var(--sw);height:100vh;background:var(--card);border-right:1px solid var(--border);position:fixed;top:0;left:0;bottom:0;overflow-y:auto;padding:20px 0;z-index:300;transform:translateX(-100%);transition:transform .28s cubic-bezier(.2,.8,.2,1);box-shadow:0 0 40px rgba(0,0,0,.6);-webkit-overflow-scrolling:touch}
body.sidebar-open .sidebar{transform:translateX(0)}
.sb-logo{padding:0 20px 16px;border-bottom:1px solid var(--border);margin-bottom:12px}
.sb-logo strong{color:var(--primary);font-family:var(--font-h);font-weight:900;font-size:13px;letter-spacing:1.5px;text-transform:uppercase}
.sb-logo p{color:var(--muted);font-size:11px;margin-top:2px}
.sidebar nav a{display:block;padding:8px 20px;color:var(--muted);text-decoration:none;font-size:12.5px;transition:color .15s,background .15s}
.sidebar nav a:hover{color:var(--text);background:var(--card2)}
.sidebar nav a.active{color:var(--primary);border-left:3px solid var(--primary);padding-left:17px;background:var(--card2)}
/* "Como usar" sempre acessível e levemente destacado em laranja */
.sidebar nav a[data-jump="practice-intro"]{
  color:var(--primary);font-weight:700;
  position:relative;
}
.sidebar nav a[data-jump="practice-intro"]::before{
  content:"";display:inline-block;
  width:6px;height:6px;border-radius:50%;
  background:var(--primary);box-shadow:0 0 8px rgba(232,105,10,.6);
  margin-right:8px;vertical-align:middle;
}
/* [P9] Pulso só enquanto o usuário não terminou o tutorial inicial */
body.howto-pending .sidebar nav a[data-jump="practice-intro"]::before{
  animation: sbHowtoDot 2.4s ease-in-out infinite;
}
@keyframes sbHowtoDot{
  0%,100%{opacity:.55;transform:scale(1)}
  50%    {opacity:1;  transform:scale(1.25)}
}
/* [v196] Cabeçalhos de tier (Iniciante / Intermediário / Avançado) na sidebar
   — pílulas coloridas com a cor do tier (verde/laranja/vermelho), centralizadas,
   espelhando a paleta dos tier-tags do TOC. */
.nav-sec{
  margin:14px 12px 8px;
  padding:9px 12px;
  background:linear-gradient(180deg,
    color-mix(in srgb, var(--accent, #E8690A) 92%, white) 0%,
    var(--accent, #E8690A) 100%);
  border:1px solid rgba(255,255,255,.12);
  border-radius:10px;
  box-shadow:0 4px 14px color-mix(in srgb, var(--accent, #E8690A) 38%, transparent),
             0 0 0 1px color-mix(in srgb, var(--accent, #E8690A) 25%, transparent) inset;
  font-family:var(--font-h);font-weight:800;
  font-size:13px;letter-spacing:2px;text-transform:uppercase;
  color:#fff;text-align:center;
  text-shadow:0 1px 2px rgba(0,0,0,.25);
}
/* [v213] Item da sidebar em UMA linha só: "Acorde Maior  C, G, F, D".
 * label + sub lado a lado, com gap; o conjunto trunca com ellipsis se exceder. */
.sidebar nav a.nav-item{
  display:flex;flex-direction:row;align-items:baseline;gap:8px;
  padding:8px 20px;
  white-space:nowrap;overflow:hidden;
}
.nav-item-label{
  font-size:12.5px;line-height:1.25;
  flex:0 0 auto;
}
.nav-item-sub{
  font-size:11px;color:var(--muted);
  font-family:var(--font-h);font-weight:600;letter-spacing:.04em;
  flex:1 1 auto;min-width:0;
  overflow:hidden;text-overflow:ellipsis;
}

/* Sidebar overlay */
/* [08/09 · ANDROID_BLUR] o blur MORA NA REGRA DE ABERTO. Na regra base ele ficava
   declarado num elemento que e `opacity:0` sempre que o menu esta fechado — ou seja,
   quase sempre — e mesmo assim o Blink mantinha uma camada de TELA INTEIRA com razao
   de composicao `BackdropFilter` viva o tempo todo, INCLUSIVE DENTRO DA PRATICA (sai
   no censo de camadas da fita, 800x360, ao lado do canvas). Aqui nao ha decisao de
   arte nenhuma: com o menu fechado o elemento e invisivel, entao o pixel e o MESMO. */
.sidebar-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:250;opacity:0;pointer-events:none;transition:opacity .28s}
body.sidebar-open .sidebar-overlay{opacity:1;pointer-events:auto;backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}

/* Hamburger menu button */
/* [08/09 · ANDROID_BLUR lote 2] a familia blur(8px) SAI dos fixos. Estes cinco
   nao sao cartoes de lista: sao elementos FIXOS, vivos em TODA tela (os tres de
   cima) ou durante a PRATICA inteira (os dois da barra), e cada um mantinha a sua
   render surface com readback do fundo. Medido com o ceu CONGELADO, diferenca por
   pixel ao tirar cada um: .menu-btn 0,000 (max 0) · .account-btn 0,004 · .nav-tag
   0,000 (max 0) · a familia toda na tela de PRATICA 0,004 de media com p99 = 0.
   Nao ha compensacao nenhuma a fazer: os fundos ja sao .78/.92 e o desfoque nao
   aparecia. Os outros tres irmaos de blur(8px) — #guided-hint, .howto-skip e
   #dia0-cap — ficaram como estao: medido, nem chegam a existir no DOM em regime,
   entao nao custam camada nenhuma e mexer neles seria diff sem ganho. */
.menu-btn{
  position:fixed;top:calc(12px + env(safe-area-inset-top));left:calc(12px + max(var(--sal), env(safe-area-inset-left, 0px)));
  /* [v81] reduzido de 44 → 38 pra equilibrar com pb-tom card */
  width:38px;height:38px;border-radius:9px;
  background:rgba(20,20,20,.92);border:1px solid var(--border2);
  color:var(--text);cursor:pointer;z-index:220;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  transition:background .15s,border-color .15s;
  -webkit-tap-highlight-color:transparent;
}
.menu-btn:active,.menu-btn:hover{background:var(--primary);border-color:var(--primary)}
.menu-btn span{display:block;width:16px;height:2px;background:currentColor;border-radius:2px;transition:transform .25s,opacity .25s}
body.sidebar-open .menu-btn span:nth-child(1){transform:translateY(5px) rotate(45deg)}
body.sidebar-open .menu-btn span:nth-child(2){opacity:0}
body.sidebar-open .menu-btn span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}
body.mode-print .menu-btn,body.mode-print .sidebar-overlay{display:none}
/* [v479] Menu lateral (☰) AGORA só na interface de piano (prática, landscape) → abre a sidebar de
   exercícios. Na unit-intro o ☰ aparece mas LEVA de volta pra "A Jornada Completa" (menu principal).
   Escondido na capa, no "como você vai praticar" e na própria Jornada. */
.menu-btn{ display:none; }
body.in-practice .menu-btn,
body[data-active-page$="-intro"] .menu-btn{ display:flex; }

/* ============ [v609] CONTA (Supabase Auth · Google) — botão discreto + modal ============ */
.account-btn{
  position:fixed;top:calc(12px + env(safe-area-inset-top));right:calc(12px + max(var(--sar), env(safe-area-inset-right, 0px)));
  width:38px;height:38px;border-radius:9px;
  background:rgba(20,20,20,.92);border:1px solid var(--border2);
  color:var(--text);cursor:pointer;z-index:220;
  display:none;align-items:center;justify-content:center;
  transition:background .15s,border-color .15s;
  -webkit-tap-highlight-color:transparent;
}
.account-btn:active,.account-btn:hover{background:var(--primary);border-color:var(--primary)}
.account-btn.is-in{border-color:var(--primary)}
.account-btn .account-dot{position:absolute;top:5px;right:5px;width:8px;height:8px;border-radius:50%;background:var(--primary);border:1.5px solid var(--card);display:none}
.account-btn.is-in .account-dot{display:block}
/* Só na home (Jornada/TOC). Escondido no boot/intro, na prática e no modo impressão. */
/* [F7] O botão de conta era visível SÓ no TOC. Agora aparece em toda RAIZ de
   aba, porque virou o lugar do progresso — e a ofensiva precisa estar à mão
   de qualquer aba, não só da Jornada. */
body[data-active-page="toc"] .account-btn,
body[data-active-page="songs"] .account-btn,
body[data-active-page="games"] .account-btn,
body[data-active-page="harmony"] .account-btn{display:flex}
html.intro-mode .account-btn{display:none!important}
body.mode-print .account-btn{display:none!important}

.account-modal{position:fixed;inset:0;z-index:2147483500;display:none;align-items:center;justify-content:center;padding:20px}
.account-modal[aria-hidden="false"]{display:flex}
.account-modal-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.62);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px)}
.account-card{position:relative;width:min(360px,92vw);background:var(--card);border:1px solid var(--border);border-radius:16px;padding:26px 22px 22px;box-shadow:0 20px 60px rgba(0,0,0,.5);text-align:center}
.account-close{position:absolute;top:6px;right:10px;background:none;border:none;color:var(--muted);font-size:26px;line-height:1;cursor:pointer;padding:4px 8px}
.account-close:hover{color:var(--text)}
.account-title{font-size:20px;margin:0 0 6px;color:var(--text);font-weight:700}
.account-sub{font-size:13.5px;color:var(--muted);margin:0 0 18px;line-height:1.5}
.account-sub strong{color:var(--text)}
.account-google-btn{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:12px 16px;border-radius:10px;background:#fff;color:#1f1f1f;border:1px solid #dadce0;font-size:15px;font-weight:600;cursor:pointer;transition:box-shadow .15s,background .15s}
.account-google-btn:hover{box-shadow:0 1px 8px rgba(0,0,0,.28);background:#f8f9fa}
.account-google-btn:disabled{opacity:.7;cursor:default}
.account-google-g{width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center}
.account-note{font-size:11.5px;color:var(--muted);opacity:.85;margin:16px 0 0;line-height:1.45}
.account-signout-btn{margin-top:4px;width:100%;padding:11px 16px;border-radius:10px;background:transparent;color:var(--text);border:1px solid var(--border2);font-size:14px;font-weight:600;cursor:pointer;transition:border-color .15s,color .15s}
.account-signout-btn:hover{border-color:var(--primary);color:var(--primary)}
.account-err{color:#ff6b6b;font-size:12.5px;margin:14px 0 0;line-height:1.4}

/* ============ MAIN ============ */
.main{flex:1;min-height:100vh;width:100%}
body.mode-interactive .main{margin-left:0}
body.mode-print .main{margin:0}

/* ============ GLOBAL PROGRESS (interactive) ============ */
.global-progress{position:fixed;top:0;left:0;right:0;height:3px;background:var(--card);z-index:200}
.global-progress-bar{height:100%;background:var(--primary);width:0%;transition:width .3s}
body.mode-print .global-progress{display:none}

/* ============ PAGE GENERIC ============ */
.page{min-height:100vh;padding:48px 56px;padding-bottom:calc(82px + env(safe-area-inset-bottom));position:relative}
.page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:32px;padding-bottom:14px;border-bottom:1px solid var(--border)}
.page-header .brand{font-family:var(--font-h);font-weight:900;font-size:11px;letter-spacing:2.5px;color:var(--primary);text-transform:uppercase}
.page-header .brand span{color:var(--text)}
.page-header .pn{font-family:var(--font-h);font-size:10px;color:var(--dim);letter-spacing:1.5px;text-transform:uppercase}

/* [v114→v117] Touch target hit-area helper REMOVIDO — sobreescrevia
   position:fixed do .menu-btn (e similares), causando deslocamento e
   mudança de tamanho do botão. A11y aceitável; layout original preservado. */

/* ============ COVER ============ */
.cover{
  /* [v478] Nova identidade: base translúcida (sem #0A0A0A sólido) pra a
     constelação global (#c) aparecer atrás; mantém os glows laranja + um véu
     escuro suave pra legibilidade do texto. */
  background:
    radial-gradient(ellipse 60% 50% at 95% 5%, rgba(232,105,10,.34) 0%, transparent 55%),
    radial-gradient(ellipse 50% 60% at 5% 95%, rgba(232,105,10,.20) 0%, transparent 55%),
    linear-gradient(180deg, rgba(7,7,7,.34) 0%, rgba(7,7,7,.58) 100%);
  min-height:100vh;height:100dvh;max-height:100dvh;
  display:flex;flex-direction:column;justify-content:space-between;align-items:center;
  padding:72px 80px calc(94px + env(safe-area-inset-bottom));text-align:center;position:relative;overflow:hidden;
  box-shadow:inset 0 0 0 1px rgba(232,105,10,.18)
}
/* [v72] Capa = autossuficiente. Trava scroll do body pra estabilizar glissando do piano. */
body.on-cover{ overflow:hidden!important; height:100vh; height:100dvh; touch-action:pan-x; overscroll-behavior:none; }  /* [v535] vh fallback p/ WebView Android antigo sem dvh */
body.on-cover .main{ overflow:hidden; height:100vh; height:100dvh; }
.cover-top,.cover-mid{width:100%;display:flex;flex-direction:column;align-items:center;justify-content:center}
.cover-top{flex:.9;padding-top:24px}
.cover-mid{flex:.9;gap:48px}
.cover h1{font-family:var(--font-h);font-weight:900;font-size:96px;line-height:.95;letter-spacing:-2.5px;color:var(--text)}
.cover h1 span{color:var(--primary)}
/* [v2.22] Tagline agora ABAIXO do título "Método TeclaTimbres".
   Itálico, aspas tipográficas, branco com leve opacity.
   margin-bottom 22px → margin-top 22px (espaço sob o h1). */
.cover-tagline{
  font-family:var(--font-h);font-style:italic;font-weight:400;
  font-size:clamp(16px,2vw,21px);
  color:var(--text);letter-spacing:.4px;opacity:.9;
  max-width:620px;text-align:center;margin:22px auto 0;line-height:1.4;
}

/* [v2.3] Texto explicativo central — substitui o antigo h2 com 3 parágrafos
   curtos, mobile-friendly. */
.cover-prose{
  display:flex;flex-direction:column;gap:14px;
  max-width:620px;margin:0 auto;text-align:center;
}
.cover-prose p{
  font-family:var(--font-b);
  font-size:clamp(13.5px,1.6vw,16px);line-height:1.65;
  color:var(--muted);
}
.cover-prose p:last-child{
  /* "Não é talento. É repetição inteligente..." — um pouco mais firme. */
  color:var(--text);font-weight:500;
}

/* [v2.3] Tags horizontais empilhadas — substituem o bloco antigo .cover-stats.
   3 linhas full-width (até 560px), com fade+slide-up em stagger ao montar. */
/* [v97] Cover tags reocupam o espaço deixado pelo piano virtual.
   Cards maiores, brilhantes (gradient laranja + glow), com gap maior. */
.cover-tags{
  display:flex;flex-direction:column;gap:clamp(14px, 2.4vh, 20px);
  width:100%;max-width:600px;margin:0 auto;
}
.cover-tag{
  display:flex;align-items:center;gap:clamp(16px, 2.5vw, 22px);
  padding:clamp(20px, 2.8vh, 28px) clamp(20px, 3.5vw, 30px);
  border-radius:16px;
  /* [v478] Glass fosco: glow laranja + camada translúcida + backdrop-blur
     (constelação visível atrás). */
  background:
    radial-gradient(ellipse 80% 120% at 0% 50%, rgba(232,105,10,.20) 0%, transparent 65%),
    linear-gradient(135deg, rgba(24,24,24,.34) 0%, rgba(18,18,18,.30) 100%);
  -webkit-backdrop-filter:blur(9px) saturate(1.1);
  backdrop-filter:blur(9px) saturate(1.1);
  border:1.5px solid rgba(232,105,10,.30);
  box-shadow:
    0 10px 32px rgba(0,0,0,.45),
    0 0 0 1px rgba(232,105,10,.05) inset,
    0 1px 0 rgba(255,255,255,.05) inset;
  text-align:left;
  position:relative;overflow:hidden;
  animation: coverTagIn .65s cubic-bezier(.2,.8,.2,1) backwards;
  transition: border-color .22s, box-shadow .22s, transform .22s, filter .18s;
  /* [v120] tags são botões interativos — disparam acordes */
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  user-select:none;
}
.cover-tag:active{ transform:translateY(0) scale(.98); }
.cover-tag.is-firing{
  border-color:rgba(232,105,10,.85);
  box-shadow:
    0 18px 48px rgba(232,105,10,.45),
    0 0 0 2px rgba(232,105,10,.35) inset,
    0 0 24px rgba(255,170,60,.25);
  filter: brightness(1.1);
}
.cover-tag.is-firing::before{ opacity:1; }
/* Brilho lateral animado pra dar vida */
.cover-tag::before{
  content:"";position:absolute;inset:-1px;border-radius:inherit;
  background:linear-gradient(120deg, transparent 35%, rgba(232,105,10,.18) 50%, transparent 65%);
  opacity:0;pointer-events:none;
  transition:opacity .35s;
}
.cover-tag:hover{
  border-color:rgba(232,105,10,.65);
  box-shadow:
    0 14px 40px rgba(232,105,10,.28),
    0 0 0 1px rgba(232,105,10,.12) inset,
    0 1px 0 rgba(255,255,255,.08) inset;
  transform:translateY(-2px);
}
.cover-tag:hover::before{opacity:1}
.cover-tag:nth-child(1){ animation-delay: .12s; }
.cover-tag:nth-child(2){ animation-delay: .24s; }
.cover-tag:nth-child(3){ animation-delay: .36s; }
@keyframes coverTagIn{
  0%   { opacity:0; transform:translateY(16px) scale(.96); }
  100% { opacity:1; transform:translateY(0)    scale(1); }
}
.ct-icon{
  font-size:clamp(28px, 4.5vw, 36px);line-height:1;flex-shrink:0;
  display:inline-flex;align-items:center;justify-content:center;
  width:clamp(44px, 6vw, 54px);height:clamp(44px, 6vw, 54px);
  border-radius:12px;
  background:rgba(232,105,10,.14);
  border:1px solid rgba(232,105,10,.32);
  box-shadow:0 0 16px rgba(232,105,10,.28) inset;
}
.ct-num{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(34px, 6vw, 48px);
  /* Gradient laranja brilhante no número — destaque tipográfico premium */
  background:linear-gradient(180deg, #FFB347 0%, var(--primary) 60%, var(--primary-2) 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  letter-spacing:-1.2px;line-height:1;
  min-width:1.6em;text-align:center;flex-shrink:0;
  filter:drop-shadow(0 4px 16px rgba(232,105,10,.45));
}
.ct-lbl{
  font-family:var(--font-h);font-weight:700;
  font-size:clamp(15px, 2vw, 18px);
  color:var(--text);letter-spacing:.06em;
  line-height:1.2;
  text-transform:uppercase;
}
@media (prefers-reduced-motion: reduce){
  .cover-tag{ opacity:1; animation:none; transform:none!important; }
}

/* Piano interativo na capa — abaixo das stats, com moldura laranja elegante */
.cover-piano-wrap{
  width:100%;max-width:680px;
  margin:18px auto 0;
  padding:6px;
  position:relative;
  border-radius:14px;
  background:
    linear-gradient(135deg, var(--primary-2) 0%, var(--primary) 50%, var(--primary-dark) 100%);
  box-shadow:
    0 0 0 1px rgba(232,105,10,.55) inset,
    0 0 0 1px rgba(0,0,0,.6),
    0 14px 32px rgba(0,0,0,.50),
    0 0 28px rgba(232,105,10,.30);
}
.cover-piano-wrap::before{
  /* Brilho sutil no topo da moldura */
  content:"";position:absolute;top:1px;left:6%;right:6%;height:2px;
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.45), transparent);
  border-radius:2px;
  pointer-events:none;
}
/* [U3] Em telas curtas (< 600px de altura) o piano comprime demais o resto da capa */
@media (max-height:600px){
  .cover-piano-wrap{display:none}
}
.cover-piano-wrap .kbd-svg{
  cursor:pointer;width:100%;height:auto;display:block;
  border-radius:8px;
  background:#0E0E0E; /* base preta caso o gradiente falhe — segurança */
}
.cover-piano-wrap .kbd-key{cursor:pointer;transition:fill .06s linear, filter .12s ease-out}
.cover-piano-wrap .kbd-w:hover{fill:#D6D6D6}
.cover-piano-wrap .kbd-b:hover{fill:#000000}

/* [v2.25/v2.26] .piano-fs-exit REMOVIDA — era usada por #howto-fs-exit/#howto-fs-next.
   O overlay #howto-fs e seus controles fixed foram totalmente removidos:
   tutorial agora roda SOBRE a página real u1-t0 via Walkthrough. */
/* [v77] Bloqueio cirúrgico de scroll APENAS no SVG do piano (e seus filhos).
   Não toca em body/practice-page/main → preserva 100% edge-to-edge.
   touch-action:none diz ao iOS "esse elemento consome o gesto" — sem pan,
   sem pinch, sem rubber-band. Glissando funciona porque os pointer events
   continuam disparando normalmente; só o gesto NATIVO é desativado. */
.practice-page .kbd-wrap,
.practice-page .kbd-svg,
.practice-page .kbd-key,
.practice-page .kbd-labels,
.practice-page .kbd-label{
  touch-action:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
  overscroll-behavior:none;
}

/* ============ [GuidedMode] Teclas da etapa Lento guiada ============ */

/* [v359] Tecla ACESA = OURO da arte de referência do Luis (cor + efeito EXATOS, extraídos
   por pixel do print): tecla BRANCA #FADE6E (rgb 250,222,110), tecla PRETA #FEFA95
   (rgb 254,250,149, mais clara/brilhante), borda dourada clara, glow quente suave em camadas,
   e ANIMAÇÃO CINTILANTE (keyShimmer) — respiração + pico de shine contínuo, como o brilho da arte.
   w/b separados (3 classes + !important) p/ vencer o fill base e os outros estados. */
.kbd-guided-active { fill:#FADE6E !important; } /* fallback genérico */
.kbd-key.kbd-guided-active.kbd-w{
  fill:#FADE6E !important; stroke:#000 !important; opacity:1; /* [v422] borda SEMPRE preta */
  filter: drop-shadow(0 0 6px rgba(250,225,120,.65)) drop-shadow(0 0 15px rgba(255,205,90,.40)) brightness(1.02);
  animation: keyShimmer 1.8s ease-in-out infinite;
}
.kbd-key.kbd-guided-active.kbd-b{
  fill:#FEFA95 !important; stroke:#000 !important; opacity:1; /* [v422] borda SEMPRE preta */
  filter: drop-shadow(0 0 7px rgba(254,250,150,.78)) drop-shadow(0 0 18px rgba(255,225,110,.50)) brightness(1.03);
  animation: keyShimmer 1.8s ease-in-out infinite;
}
/* Cintilância dourada: respiração suave + um PICO de shine (brilha/cintila) a cada ciclo. */
@keyframes keyShimmer {
  0%,100% { filter: drop-shadow(0 0 5px rgba(250,225,120,.50)) drop-shadow(0 0 12px rgba(255,205,90,.30)) brightness(1.00) saturate(1.00); }
  42%     { filter: drop-shadow(0 0 9px rgba(255,238,150,.85)) drop-shadow(0 0 20px rgba(255,215,100,.52)) brightness(1.14) saturate(1.07); }
  52%     { filter: drop-shadow(0 0 14px rgba(255,248,195,1))  drop-shadow(0 0 28px rgba(255,228,125,.70)) brightness(1.28) saturate(1.10); } /* cintila */
  62%     { filter: drop-shadow(0 0 9px rgba(255,238,150,.85)) drop-shadow(0 0 20px rgba(255,215,100,.52)) brightness(1.14) saturate(1.07); }
}

/* [v403] ACORDE da PROGRESSÃO — notas do acorde que NÃO são a da vez = LARANJA ESCURO SEMI-ACESO
   (ghost), pra a tecla da vez (gold cintilante) se destacar sem esconder o acorde inteiro. Sem
   shimmer, opacidade reduzida. Usado no GUIADO (kbd-guided-ghost) e no CLICK (mesma classe). */
.kbd-key.kbd-guided-ghost.kbd-w{ fill:#E8690A !important; stroke:#000 !important; opacity:1; } /* [v419] semi-aceso = brand #E8690A exato, opaco, divisão PRETA */
.kbd-key.kbd-guided-ghost.kbd-b{ fill:#E8690A !important; stroke:#000 !important; opacity:1; }

/* Tecla tocada corretamente — flash verde */
.kbd-guided-hit {
  fill: #4ADE80 !important;
  filter: drop-shadow(0 0 8px rgba(74,222,128,.9));
  transition: fill .05s, filter .05s;
}

/* Tecla tocada errada — flash vermelho */
.kbd-guided-miss {
  fill: #EF4444 !important;
  filter: drop-shadow(0 0 8px rgba(239,68,68,.9));
  transition: fill .05s, filter .05s;
}

/* [v367] PISCADA DE RE-ATAQUE (Click): apaga a tecla por ~70ms quando o cue cai numa tecla que já
   estava acesa (nota repetida no novo ciclo da escala) → marca o novo ataque (senão verde→ouro
   faz cross-fade imperceptível). opacity baixa + sem transição = corte limpo; depois re-acende. */
.kbd-key.kbd-cue-blink { opacity:.12 !important; filter:none !important; transition:none !important; }

/* [v355] MOLDURA NEON LARANJA que pisca em sincronia com o CLICK (qualquer BPM). Full-viewport,
   encaixa em qualquer tela. Borda interna + glow neon; só visível no instante da batida. */
#click-border-flash{
  position:fixed; inset:0; z-index:99990; pointer-events:none; opacity:0; border-radius:2px;
  /* [v368] glow da borda mais ESPALHADO pro centro + vinheta radial laranja (das bordas pro miolo). */
  box-shadow: inset 0 0 0 4px #FF7A1A,
              inset 0 0 44px 10px rgba(255,138,38,.80),
              inset 0 0 120px 28px rgba(255,110,20,.46);
  background: radial-gradient(ellipse at center, transparent 36%, rgba(255,120,25,.12) 64%, rgba(255,120,25,.30) 100%);
  transition: opacity 120ms ease-out; will-change:opacity;
}
#click-border-flash.on{ opacity:.92; transition:opacity 26ms ease-in; }       /* batida fraca */
#click-border-flash.on.strong{ opacity:1;
  box-shadow: inset 0 0 0 5px #FF8A1E,
              inset 0 0 60px 15px rgba(255,150,50,.92),
              inset 0 0 165px 40px rgba(255,120,25,.58);
  background: radial-gradient(ellipse at center, transparent 28%, rgba(255,130,30,.18) 56%, rgba(255,120,25,.44) 100%); } /* downbeat: mais forte + mais pro centro */

/* [v642] MOLDURA VERMELHA — cópia EXATA da moldura do click (mesma geometria/glow/vinheta), porém no
   VERMELHO da tecla errada (#EF4444 = .kbd-guided-miss). Pisca quando uma tecla vermelha (nota errada)
   acende. Full-viewport, pointer-events:none, puramente estética — não toca detecção/lógica. */
#wrong-border-flash{
  position:fixed; inset:0; z-index:99991; pointer-events:none; opacity:0; border-radius:2px;
  box-shadow: inset 0 0 0 5px #EF4444,
              inset 0 0 60px 15px rgba(239,68,68,.90),
              inset 0 0 165px 40px rgba(239,68,68,.55);
  background: radial-gradient(ellipse at center, transparent 28%, rgba(239,68,68,.16) 56%, rgba(239,68,68,.42) 100%);
  transition: opacity 120ms ease-out; will-change:opacity;
}
#wrong-border-flash.on{ opacity:1; transition:opacity 26ms ease-in; }

/* [corretivo] Banner de instrução do modo guiado (anti soft-lock) */
#guided-hint{
  position:fixed; top:calc(10px + env(safe-area-inset-top)); left:50%;
  transform:translateX(-50%); z-index:240;
  display:none; align-items:center; gap:14px;
  padding:10px 14px; border-radius:12px;
  background:rgba(20,20,20,.92);
  border:1px solid rgba(232,105,10,.45);
  box-shadow:0 8px 24px rgba(0,0,0,.5), 0 0 16px rgba(232,105,10,.2);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
  pointer-events:none;
  max-width:calc(100vw - 120px);
}
/* [v327] No modo guiado a notice fica DENTRO da barra (zona central), não flutuando. */
#guided-hint{display:none!important}
/* [v338/v342] Notice em 2 linhas. [v342] A largura é o VÃO REAL entre o card e o botão de
   olho: no guiado a zona central deixa de ser absoluta e vira flex:1 ENTRE as zonas esq/dir
   (que encolhem ao conteúdo) → o texto centraliza exatamente no vão card↔olho, em qualquer página. */
.pb-guided-notice{display:none;align-items:center;justify-content:center;white-space:normal;max-width:100%}
body.guided-active .pb-zone-center .pb-stages,
body.guided-active .pb-zone-center .pb-time-row,
body.guided-active .pb-zone-center .pb-progress{display:none!important}
/* [v329] No guiado: esconde play + reiniciar (liberam espaço); ficam só olho + nav-tag. */
body.guided-active .pb-zone-right [data-mt-act="play"],
body.guided-active .pb-zone-right [data-mt-act="reset"]{display:none!important}
/* [v342 → 22/07] Este bloco ERA a exceção que fazia a centralização real no vão; virou
   a regra base de todas as barras. Fica só a nota histórica — nada a sobrescrever. */
body.guided-active .pb-guided-notice{display:flex}
/* [v329] mesma fonte do nome do acorde no card ("Maior"): Montserrat 800, sem caixa-alta,
   maior pra preencher o espaço liberado. As partes <b> (número da repetição) em laranja. */
.pgn-text{display:flex;flex-direction:column;align-items:center;gap:2px;color:var(--text);
  font-family:var(--font-h);letter-spacing:-.2px;line-height:1.16;text-align:center;max-width:100%}
.pgn-text b{color:var(--primary)}
/* [v342] tamanho IDEAL pra ocupar bem o vão (era pequeno demais no v338): linha 1 = ação
   (maior), linha 2 = contadores (um pouco menor, fosca). */
.pgn-main{font-weight:800;font-size:clamp(17px,2.5vw,26px)}
.pgn-sub{font-weight:700;font-size:clamp(14px,1.75vw,19px);color:#aab0b6}
.pgn-sub b{color:var(--primary)}
/* Não vazar sobre overlays de rotação / fase bloqueada / impressão / tutorial */
body.locked-phase-blocked #guided-hint,
body.howto-fs-on #guided-hint,
body.mode-print #guided-hint{display:none!important}
#guided-hint .gh-text{
  color:var(--text); font-family:var(--font-h); font-weight:700;
  font-size:13px; letter-spacing:.02em; white-space:nowrap;
}
#guided-hint .gh-text b{ color:var(--primary); }
#guided-hint .gh-skip{
  pointer-events:auto; cursor:pointer; flex-shrink:0;
  background:rgba(232,105,10,.16); color:var(--primary);
  border:1px solid rgba(232,105,10,.5); border-radius:8px;
  padding:6px 12px; font-family:var(--font-h); font-weight:800;
  font-size:11px; letter-spacing:.08em; text-transform:uppercase;
  -webkit-tap-highlight-color:transparent;
}
#guided-hint .gh-skip:active{ background:var(--primary); color:#fff; }

/* [remoção timbres] .pb-timbre-btn removido (botão de timbre da practice-bar). */

/* ============ A TELA "VIRE O CELULAR" FOI REMOVIDA (05/09 · Luis) ============
   Saiu a tela que mandava DEITAR o telefone — a que bloqueava prática, pauta e tutorial em
   retrato — e com ela todo o vestuário do lock: a classe de lock no body escondia #app, a
   tabbar e o balão do walkthrough e forçava o ☰ por cima; a classe de tutorial-pendente
   reaproveitava o mesmo overlay enquanto ele esperava o giro. Nenhuma tela é mais bloqueada
   por estar em pé: girar troca de tela e pronto.
   🔑 O QUE FICA É O COMPONENTE, porque a variante RETRATO ("gire para a vertical") continua
   viva mais abaixo: o que era em pé continua em pé. Aqui ficam só a caixa e a tipografia que
   ela usa — nenhuma regra de lock, nenhum seletor da tela horizontal. */
.rotation-overlay{
  /* z-index 9998 (acima de tudo menos o splash 9999) + isolation: stacking context próprio,
     pra nada da página de baixo vazar por cima. padding respeita a safe-area. */
  position:fixed;inset:0;z-index:9998;isolation:isolate;
  background:transparent;   /* transparente sobre a constelação global (#c atrás) */
  display:none;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(26px,5.5vh,50px);
  padding:max(32px, env(safe-area-inset-top, 0px) + 16px)
         max(32px, max(var(--sar), env(safe-area-inset-right, 0px)) + 16px)
         max(32px, env(safe-area-inset-bottom, 0px) + 16px)
         max(32px, max(var(--sal), env(safe-area-inset-left, 0px)) + 16px);
  text-align:center;
  -webkit-tap-highlight-color:transparent;
}
/* ============ [06/09 · Luis] A TELA "VIRE O CELULAR" DE VOLTA (revert do giro por CSS) — regras de 22/08 ============ */
body.rotation-locked .rotation-overlay{display:flex}
body.rotation-locked .rotation-overlay{animation:roFadeIn .45s ease both}
body.rotation-locked .rotation-overlay .ro-phone-wrap{animation:roRise .7s cubic-bezier(.2,.8,.2,1) both}
body.rotation-locked .rotation-overlay .ro-title{animation:roRise .7s cubic-bezier(.2,.8,.2,1) .1s both}
body.rotation-locked .rotation-overlay .ro-sub{animation:roRise .7s cubic-bezier(.2,.8,.2,1) .18s both}
body.rotation-locked #app{visibility:hidden!important}
body.rotation-locked .tabbar,
body.rotation-locked #tabbar{display:none!important}
body.rotation-locked .wt-tooltip{visibility:hidden!important}
body.rotation-locked .wt-target-active{animation:none!important;outline:none!important;z-index:auto!important;visibility:hidden!important}
body.howto-fs-pending .rotation-overlay{display:flex}
body.rotation-locked .menu-btn{display:flex!important;z-index:290!important}
body.rotation-locked #guided-hint{display:none!important}
#rotation-overlay .ro-phone-svg{animation:roTilt 2.8s ease-in-out infinite}
@keyframes roTilt{0%,26%,100%{transform:rotate(0deg)}52%,76%{transform:rotate(-90deg)}}
/* entrada animada (fade + ícone/texto surgindo), constelação persistente atrás (#c) */
@keyframes roFadeIn{from{opacity:0}to{opacity:1}}
@keyframes roRise{from{opacity:0;transform:translateY(26px) scale(.96);filter:blur(6px)}to{opacity:1;transform:none;filter:none}}
.rotation-overlay .ro-phone-wrap{
  position:relative;width:clamp(176px,46vw,236px);height:clamp(176px,46vw,236px);
  display:flex;align-items:center;justify-content:center;
}
.rotation-overlay .ro-phone-wrap::before{
  content:"";position:absolute;inset:0;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle at 50% 50%,rgba(232,105,10,.32) 0%,rgba(232,105,10,.09) 46%,transparent 70%);
  animation:roGlow 2.6s ease-in-out infinite;
}
/* sem drop-shadow no svg que gira; o halo fica no ::before estático. Quem dá o giro do
   telefone é a variante (roTiltBack) — o desenho que tombava pra deitado saiu com a tela. */
.rotation-overlay .ro-phone-svg{
  position:relative;width:clamp(104px,28vw,144px);height:auto;color:#FFB347;
  will-change:transform;transform-origin:center;
}
.rotation-overlay .ro-title{
  font-family:var(--font-h);font-weight:900;font-size:clamp(46px,11.5vw,92px);
  letter-spacing:-2px;line-height:1.02;color:var(--text);margin:0;
}
.rotation-overlay .ro-title span{color:var(--primary)}
.rotation-overlay .ro-sub{
  font-family:var(--font-h);font-weight:700;font-size:clamp(14px,3.6vw,17px);
  line-height:1.45;color:rgba(255,255,255,.74);max-width:336px;margin:0;
}
@keyframes roGlow{0%,100%{opacity:.6;transform:scale(.95)}50%{opacity:1;transform:scale(1.05)}}
body.mode-print .rotation-overlay{display:none!important}
/* [v520] Ao girar de landscape (piano) → portrait, o handleOrientationChange navega da practice-page pra
   unit-intro. Durante a saída (.page-leaving por ~600ms) a practice-page (piano landscape) é renderizada
   em PORTRAIT → teclas ESTICADAS, visual feio por um instante. Em portrait, escondemos qualquer
   practice-page que está SAINDO ([data-tom] = prática) → só se vê o fundo até a unit-intro entrar limpa.
   ⚠️ Esta regra NÃO é do overlay: agora que nada cobre a virada, ela é o que a deixa limpa. */
@media (orientation:portrait){
  .page.page-leaving[data-tom]{ visibility:hidden!important; }
}

/* ============ [v122] LOCKED PHASE OVERLAY — landscape ============
 * Disparado quando o usuário gira pra landscape em uma unit-intro cuja
 * fase ainda está bloqueada. Substitui o auto-jump pra prática (que
 * antes bypassava o lock — falha de segurança). Mostra mensagem
 * progressiva e CTA pra voltar ao sumário. */
.locked-phase-overlay{
  position:fixed;inset:0;z-index:500;
  background:
    radial-gradient(ellipse 70% 55% at 50% 25%, rgba(232,105,10,.18) 0%, transparent 60%),
    radial-gradient(ellipse 50% 45% at 80% 100%, rgba(232,105,10,.10) 0%, transparent 60%),
    #0A0A0A;
  display:none;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(10px, 1.6vh, 18px);
  padding:clamp(20px, 4vh, 40px) clamp(24px, 5vw, 64px);
  text-align:center;
  -webkit-tap-highlight-color:transparent;
  animation: lpoIn .42s cubic-bezier(.2,.8,.2,1);
}
body.locked-phase-blocked .locked-phase-overlay{display:flex}
/* Mantém o menu acessível por cima do overlay */
body.locked-phase-blocked .menu-btn{display:flex!important;z-index:520!important}
.lpo-icon{
  width:clamp(80px, 14vh, 120px);height:clamp(80px, 14vh, 120px);
  border-radius:50%;
  background:rgba(232,105,10,.12);
  border:2.2px solid rgba(232,105,10,.55);
  display:flex;align-items:center;justify-content:center;
  font-size:clamp(38px, 7vh, 54px);
  box-shadow:0 0 36px rgba(232,105,10,.32);
  animation: lpoIconPulse 2.4s ease-in-out infinite;
}
.lpo-tag{
  font-family:var(--font-h);font-weight:800;
  font-size:clamp(10px, 1.5vh, 12px);
  color:var(--primary);letter-spacing:2.5px;text-transform:uppercase;
}
.lpo-title{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(20px, 3.6vh, 30px);line-height:1.1;letter-spacing:-.5px;
  color:#FFFFFF;max-width:720px;margin:0;
}
.lpo-body{
  font-family:var(--font-b);
  font-size:clamp(13px, 1.85vh, 15.5px);line-height:1.55;
  color:var(--muted);max-width:560px;margin:0;
}
.lpo-body strong{color:var(--text)}
.lpo-phase-info{
  display:inline-flex;align-items:center;gap:10px;
  padding:8px 16px;border-radius:999px;
  background:rgba(232,105,10,.10);
  border:1px solid rgba(232,105,10,.32);
  font-family:var(--font-h);font-weight:700;
  font-size:clamp(11px, 1.6vh, 13px);
  color:var(--primary);letter-spacing:.06em;
  text-transform:uppercase;
}
.lpo-actions{
  display:flex;gap:12px;flex-wrap:wrap;justify-content:center;
  margin-top:clamp(4px, 1vh, 10px);
}
.lpo-btn{
  display:inline-flex;align-items:center;gap:8px;
  padding:clamp(11px, 1.6vh, 14px) clamp(20px, 3.5vw, 28px);
  border-radius:12px;border:none;cursor:pointer;
  font-family:var(--font-h);font-weight:700;
  font-size:clamp(13px, 1.7vh, 14.5px);letter-spacing:.04em;
  background:var(--primary);color:#FFFFFF;
  box-shadow:0 8px 24px rgba(232,105,10,.40);
  transition:background .15s, transform .12s, box-shadow .2s;
  -webkit-tap-highlight-color:transparent;
}
.lpo-btn:hover{background:var(--primary-2)}
.lpo-btn:active{transform:translateY(1px) scale(.98)}
.lpo-btn.secondary{
  background:rgba(255,255,255,.06);
  color:#FFFFFF;
  border:1px solid rgba(255,255,255,.16);
  box-shadow:none;
}
.lpo-btn.secondary:hover{background:rgba(255,255,255,.12)}
@keyframes lpoIn{
  0%{ opacity:0; transform:scale(.97); }
  100%{ opacity:1; transform:scale(1); }
}
@keyframes lpoIconPulse{
  0%,100%{ box-shadow:0 0 22px rgba(232,105,10,.28) }
  50%   { box-shadow:0 0 44px rgba(232,105,10,.55) }
}
@media (prefers-reduced-motion: reduce){
  .locked-phase-overlay{animation:none}
  .lpo-icon{animation:none}
}
body.mode-print .locked-phase-overlay{display:none!important}

.lpo-btn[disabled]{opacity:.55;cursor:default;pointer-events:none}

/* [v611] CAMADA VISUAL DE PAYWALL (não-pagante). A tela cheia de paywall foi REMOVIDA:
   o bloqueio agora se apresenta como cadeados na Jornada + unit-intro bloqueada + botão de compra.
   Tudo dirigido por body.pw-nonpayer (togglado em updateAccountUI) — pagante = zero mudança. */

/* Cadeado nos cards da Jornada Completa — só p/ não-pagante, em TODOS os cards.
   [v612] ícone de LINHA verde (estilo dos ícones do app), à DIREITA do card, centralizado
   verticalmente (onde ficaria o toque/seta) — não colado ao título. */
.toc-phase-lock{display:none;position:absolute;right:14px;top:50%;transform:translateY(-50%);
  width:20px;height:20px;color:#22C55E;pointer-events:none}
.toc-phase-lock svg{width:100%;height:100%;display:block}
body.pw-nonpayer .toc-phase-lock{display:block}
body.pw-nonpayer .toc-phase{padding-right:46px}   /* espaço p/ o cadeado não sobrepor título/sub */
body.pw-nonpayer .toc-phase-title{opacity:.82}

/* [v615] TESTE GRÁTIS — o card do Acorde Maior (Dó/Sol/Fá/Ré) parece ACESSÍVEL p/ o não-pagante:
   sem cadeado, padding normal, título em opacidade cheia + selo "GRÁTIS" verde. Demais cards: cadeado normal. */
.toc-phase-free{display:none;font-family:var(--font-h);font-weight:800;font-size:10px;letter-spacing:1px;
  text-transform:uppercase;padding:2px 8px;border-radius:999px;background:rgba(34,197,94,.14);
  border:1px solid rgba(34,197,94,.5);color:#22C55E;line-height:1.4}
body.pw-nonpayer .toc-phase.is-trial-free .toc-phase-lock{display:none}
body.pw-nonpayer .toc-phase.is-trial-free{padding-right:16px}
body.pw-nonpayer .toc-phase.is-trial-free .toc-phase-title{opacity:1}
body.pw-nonpayer .toc-phase.is-trial-free .toc-phase-free{display:inline-flex;align-items:center}

/* [v615] Cadeado nos tons bloqueados do menu de tonalidades (não-pagante no teste grátis): os 8 tons
   fora de Dó/Sol/Fá/Ré ficam esmaecidos, sem check, com cadeado verde no lugar. */
.t-opt.is-locked{opacity:.5}
.t-opt.is-locked .t-opt-dot{filter:grayscale(1)}
.t-opt-lock{width:15px;height:15px;color:#22C55E;flex-shrink:0;display:inline-flex}
.t-opt-lock svg{width:100%;height:100%;display:block}

/* [v615] ===== LOGIN GATE + TELA DE 3 OPÇÕES (momentos de conversão · kit de marca) =====
   Ambos cobrem o app e revelam SÓ o fundo estelar global (#c). Ficam ABAIXO do modal de conta
   (z 2147483500) e do install-gate (2147483600) — assim o "Verificar código" abre POR CIMA. */
/* [DIA0-DEC 04/08] a decisão do Dia 0 (#dia0-dec) entra em TODOS os grupos abaixo junto com a
   antiga: é o mesmo tipo de tela (gate de tela cheia sobre o fundo estelar), e ficar de fora de
   um só destes seletores deixaria mobília do app (tab bar, botões, sidebar) vazando por trás. */
body.login-gate-on #app, body.three-on #app, body.dia0-dec-on #app,
body.login-gate-on #menu-btn, body.three-on #menu-btn, body.dia0-dec-on #menu-btn,
body.login-gate-on #nextBtn, body.three-on #nextBtn, body.dia0-dec-on #nextBtn,
body.login-gate-on #prevBtn, body.three-on #prevBtn, body.dia0-dec-on #prevBtn,
body.login-gate-on .account-btn, body.three-on .account-btn, body.dia0-dec-on .account-btn,
/* [v626] a SIDEBAR (menu lateral) é aberta no encerramento do tutorial (startEncerramento → __ttOpenSidebar)
   e ficava VISÍVEL por trás do gate TRANSPARENTE (v625: #three-options/#login-gate background:transparent p/
   revelar as estrelas) → o menu/piano vazava atrás do botão. Esconder aqui garante TELA CHEIA LIMPA: só #c
   (estrelas) + os elementos do gate. Cinto-e-suspensório junto do remove('sidebar-open') no JS. */
body.login-gate-on .sidebar, body.three-on .sidebar, body.dia0-dec-on .sidebar,
body.login-gate-on #sidebar-overlay, body.three-on #sidebar-overlay, body.dia0-dec-on #sidebar-overlay,
body.login-gate-on #tom-menu, body.three-on #tom-menu, body.dia0-dec-on #tom-menu,
body.login-gate-on #splash, body.three-on #splash, body.dia0-dec-on #splash{display:none!important}
.tt-gate{position:fixed;inset:0;z-index:2147483000;display:none;flex-direction:column;
  box-sizing:border-box;overflow:hidden;color:var(--text);
  /* leve escurecida sobre o #c: dá contraste ao texto sem apagar as estrelas */
  background:radial-gradient(125% 95% at 50% 0%, rgba(10,10,12,.34) 0%, rgba(6,6,8,.72) 68%, rgba(4,4,6,.9) 100%)}
body.login-gate-on #login-gate{display:flex}
body.three-on #three-options{display:flex}
body.dia0-dec-on #dia0-dec{display:flex}
/* ---- Login = a TELA INICIAL ORIGINAL (piano + fundo estelar + título laranja) + só o CTA de login ---- */
#login-gate{background:transparent}   /* [v618] fundo estelar global (#c) em intensidade CHEIA, igual às outras telas — sem véu escuro */
#three-options{background:transparent}   /* [v625] fundo estelar CHEIO atrás da decisão — remove o véu escuro do .tt-gate (cards têm backdrop próprio p/ legibilidade) */
/* [v628] retrato nativo: centraliza quando cabe (margin:auto no inner) e ROLA quando é alto (teclado iOS,
   viewport baixa) — nada cortado no topo/base. Mesmo padrão do .three-options. */
.login-gate{align-items:center;justify-content:flex-start;overflow-y:auto;-webkit-overflow-scrolling:touch;
  padding:32px 22px calc(env(safe-area-inset-bottom) + 20px)}
.login-gate-inner{width:100%;max-width:460px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:16px;margin:auto 0}
/* título no MESMO estilo dos títulos do app (=.toc-mega-title): pesado, com palavra em laranja da marca */
.login-title{font-family:var(--font-h);font-weight:900;font-size:clamp(34px,9vw,64px);line-height:.98;letter-spacing:-1.8px;color:var(--text);margin:0}
.login-title span{color:var(--primary)}
.login-sub{font-family:var(--font-b);font-size:15px;line-height:1.5;color:var(--muted);margin:0}
.login-otp-btn{display:flex;align-items:center;justify-content:center;gap:11px;width:100%;max-width:340px;
  margin-top:4px;padding:15px 18px;border-radius:14px;background:var(--primary);color:#fff;border:0;cursor:pointer;
  font-family:var(--font-h);font-weight:800;font-size:16px;
  box-shadow:0 10px 30px rgba(0,0,0,.42),0 2px 0 rgba(255,255,255,.18) inset;transition:transform .12s,filter .12s}
.login-otp-btn:hover{transform:translateY(-1px);filter:brightness(1.05)}
.login-otp-btn:active{transform:translateY(0)}
.login-otp-btn:disabled{opacity:.7;cursor:default}
/* ---- [v621] form de login por OTP (login-gate + modal de conta): campos, ações, mensagem ---- */
.otp-form{width:100%;max-width:340px;display:flex;flex-direction:column;align-items:stretch;gap:10px;margin:0 auto}
.otp-step-email,.otp-step-code{display:flex;flex-direction:column;gap:10px}
/* o atributo [hidden] precisa vencer o display:flex acima (senão os 2 passos aparecem juntos) */
.otp-step-email[hidden],.otp-step-code[hidden],.otp-msg[hidden]{display:none}
.otp-input{width:100%;padding:14px 16px;border-radius:12px;border:1px solid rgba(255,255,255,.16);
  background:rgba(255,255,255,.06);color:var(--text);font-family:var(--font-b);font-size:16px;text-align:center;
  outline:none;transition:border-color .12s,background .12s}
.otp-input::placeholder{color:var(--muted);opacity:.8}
.otp-input:focus{border-color:var(--primary);background:rgba(255,255,255,.09)}
.otp-code{letter-spacing:6px;font-weight:700;font-family:var(--font-h)}
.otp-actions{display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap;margin-top:2px}
.otp-link{background:none;border:0;cursor:pointer;color:var(--muted);font-family:var(--font-b);font-size:13.5px;
  text-decoration:underline;padding:4px;transition:color .12s}
.otp-link:hover{color:var(--text)}
.otp-link:disabled{opacity:.55;cursor:default;text-decoration:none}
.otp-msg{font-family:var(--font-b);font-size:13px;line-height:1.45;color:var(--muted);margin:2px 0 0;text-align:center}
.otp-msg.is-err{color:#ff6b6b}
/* piano da tela inicial — VERBATIM da experiencia.html (.piano/.wkrow/.wk/.bkrow/.bk), apenas movido pro rodapé */
.login-piano{position:absolute;left:0;right:0;bottom:calc(env(safe-area-inset-bottom) + 26px);display:flex;justify-content:center;pointer-events:none}
#login-gate .piano{position:relative;width:clamp(230px,54vw,380px);height:clamp(72px,12vh,98px);margin:0 auto}
#login-gate .wkrow{position:absolute;inset:0;display:flex;gap:2px}
#login-gate .wk{flex:1;border-radius:0 0 7px 7px;
  background:linear-gradient(180deg,rgba(255,245,235,.17),rgba(255,238,224,.045));
  border:1px solid rgba(255,225,200,.16);border-top:0;
  box-shadow:inset 0 -11px 15px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.10)}
#login-gate .bkrow{position:absolute;inset:0;pointer-events:none}
#login-gate .bk{position:absolute;top:0;height:62%;width:9%;border-radius:0 0 5px 5px;
  background:linear-gradient(180deg,#1a1510,#050505);border:1px solid rgba(255,170,60,.18);border-top:0;
  box-shadow:0 5px 9px rgba(0,0,0,.6), inset 0 -4px 7px rgba(0,0,0,.6)}
/* ---- 3 opções ---- */
/* [v625] rola quando não cabe (landscape logo após o tutorial) e centraliza quando cabe (margin:auto no
   inner + justify flex-start) — nada cortado em nenhuma orientação. .tt-gate é overflow:hidden; sobrescrevo. */
.three-options{align-items:center;justify-content:flex-start;overflow-y:auto;-webkit-overflow-scrolling:touch;
  padding:24px 20px calc(env(safe-area-inset-bottom) + 24px)}
.three-inner{width:100%;max-width:920px;display:flex;flex-direction:column;align-items:center;gap:20px;margin:auto 0}

/* [22/07 · Luis] BARRA DE NAVEGAÇÃO DECORATIVA NA DECISÃO.
 * Espelha a .tabbar do app (mesmas medidas, mesmo vidro, mesmos ícones) e a .tut-tabbar
 * da experiencia.html: as três são a MESMA coisa aos olhos do aluno — ele reconhece a
 * casa antes de entrar. Inerte: sem handlers, aria-hidden, pointer-events:none.
 * `position:absolute` dentro do gate (que é position:fixed) → NÃO entra no fluxo em
 * coluna do .three-inner, então a coreografia do dv2 (título imóvel via margin:auto)
 * segue com exatamente os mesmos itens de antes.
 * ⚠️ Diferente da barra do iframe, AQUI o env() resolve nativo: este é o documento de
 * TOPO. Sem ponte, sem var, sem snapshot — ver o bloco __ttSyncSab no index.html. */
.gate-tabbar{
  position:absolute;left:0;right:0;bottom:0;z-index:3;
  display:flex;align-items:stretch;
  height:calc(58px + env(safe-area-inset-bottom));
  padding-bottom:env(safe-area-inset-bottom);
  background:rgba(12,12,12,.82);
  backdrop-filter:blur(14px) saturate(1.1);
  -webkit-backdrop-filter:blur(14px) saturate(1.1);
  border-top:1px solid var(--border);
  pointer-events:none;
}
.gate-tab{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;color:var(--muted)}
.gate-tab.is-on{color:var(--primary)}
.gate-tab svg{width:22px;height:22px;display:block;flex:none}
.gate-tab em{font-family:var(--font-h);font-style:normal;font-weight:800;font-size:9px;
  /* [F1-Partitura] mesmo pacote tipográfico da .tabbar (5 itens) — as duas barras
     são a MESMA coisa aos olhos do aluno */
  letter-spacing:.04em;text-transform:uppercase;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* o conteúdo rolável tem que terminar ACIMA da barra (o inner centraliza dentro do
   padding box, então isto sobe a composição ~29px — uma vez só, nunca durante a fala) */
.three-options{padding-bottom:calc(env(safe-area-inset-bottom) + 24px + 58px)}
/* desktop navega pelo RAIL de 88px à esquerda, não por barra inferior — igual ao app */
html.dt .gate-tabbar{display:none}
html.dt .three-options{padding-bottom:calc(env(safe-area-inset-bottom) + 24px)}
.three-title{font-family:var(--font-h);font-weight:800;font-size:clamp(30px,8.2vw,56px);line-height:1.04;
  letter-spacing:-1px;text-align:center;margin:0}
.three-title .accent{color:var(--primary);display:block}
.three-sub{font-family:var(--font-b);font-size:15px;line-height:1.5;color:var(--muted);text-align:center;margin:0;max-width:46ch}
.three-cards{display:grid;gap:13px;width:100%;grid-template-columns:1fr;max-width:640px;margin:0 auto}
@media(min-width:720px){.three-cards{grid-template-columns:repeat(2,1fr)}}
/* [v772·F1-desktop] Decisão com card ÚNICO VISÍVEL (antiga/kiwify, vitalício-only): na grid 2-col
   (≥720px) o card caía na célula ESQUERDA com a direita vazia (auditoria F0). :only-child do v771
   NÃO casava — a grid tem 2 BUTTONs no DOM (o 2º display:none). Mira real: decisão SEM dv2-on =
   1 card visível → primário centrado no span todo. V2 (dv2-on, 2+ cards) e mobile (grid 1fr,
   container <400px → min()=100%) intocados. */
body:not(.dv2-on) .three-cards > .three-card.is-primary{ grid-column:1 / -1; justify-self:center; width:min(100%, 400px); }
/* [v628 · mantido em 05/09] RETRATO NATIVO + bloqueio de landscape reusando o overlay de rotação.
   🔑 A tela "vire o celular" (deitar) saiu do app; ESTA aqui FICA — é ela que faz o que o Luis
   pediu: "o que era em pé continua em pé". Decisão, login e pagamento são retrato-nativas e
   continuam protegidas quando o aparelho está deitado.
   Antes (v626) girávamos o gate 90° por CSS (rotate-hack) — REMOVIDO. Agora a decisão/login vivem
   naturalmente em retrato (layout em coluna acima); em landscape, escondemos o gate e mostramos a
   VARIANTE retrato do overlay (mesmo componente/estilo, copy "gire pra vertical").
   - pointer:coarse = só toque (celular/tablet). Desktop/laptop = pointer:fine → nunca trava (igual _isDesktop).
   - Girou pra vertical → media query descasa → overlay some, gate limpo aparece. */
/* variante retrato oculta por padrão: ela só acende no bloco de media query abaixo.
   (O lock da prática, que era quem mais acendia .rotation-overlay, saiu em 05/09.) */
#rotation-overlay-portrait{display:none}
/* 🔑 [09/09 · Luis] O PAYWALL ENTROU NESTA LISTA. "o paywall é exibido
   exclusivamente na vertical; ao clicar em avançar aparece a tela de vire o
   celular, que faz voltar para a página vertical já com o paywall aberto."
   Ele nasceu em 31/08 como a tela que SUBSTITUI a decisão (`#three-options`) e
   herdou tudo dela menos isto: `paywall-on` já estava no `__ttVeuDePeAberto()`
   do index.html (que é o que impede o app de DEITAR por baixo dela), mas não
   aqui — então, deitado, a carta desenhava-se em paisagem, com uma folha de
   96dvh de altura num quadro de 400px. Dizer "gire" é o mesmo que a decisão e o
   login já dizem, e agora o pop-up do fim da peça nativa manda gente para cá
   estando DEITADO, que é o caminho onde isso se vê todas as vezes.
   `visibility:hidden` e não `display:none` pelo mesmo motivo dos outros três: o
   .tt-gate tem z-index 2147483000 e o overlay 9998 — esconder é o que o põe por
   baixo, e de quebra o gate para de comer o toque. */
@media (orientation:landscape) and (pointer:coarse){
  body.three-on #three-options,
  body.dia0-dec-on #dia0-dec,
  body.paywall-on #tt-paywall,
  body.login-gate-on #login-gate{visibility:hidden}
  body.three-on #rotation-overlay-portrait,
  body.dia0-dec-on #rotation-overlay-portrait,
  body.paywall-on #rotation-overlay-portrait,
  body.login-gate-on #rotation-overlay-portrait{display:flex;animation:roFadeIn .45s ease both}
  body.three-on #rotation-overlay-portrait .ro-phone-wrap,
  body.dia0-dec-on #rotation-overlay-portrait .ro-phone-wrap,
  body.paywall-on #rotation-overlay-portrait .ro-phone-wrap,
  body.login-gate-on #rotation-overlay-portrait .ro-phone-wrap{animation:roRise .7s cubic-bezier(.2,.8,.2,1) both}
  body.three-on #rotation-overlay-portrait .ro-title,
  body.dia0-dec-on #rotation-overlay-portrait .ro-title,
  body.paywall-on #rotation-overlay-portrait .ro-title,
  body.login-gate-on #rotation-overlay-portrait .ro-title{animation:roRise .7s cubic-bezier(.2,.8,.2,1) .1s both}
  body.three-on #rotation-overlay-portrait .ro-sub,
  body.dia0-dec-on #rotation-overlay-portrait .ro-sub,
  body.paywall-on #rotation-overlay-portrait .ro-sub,
  body.login-gate-on #rotation-overlay-portrait .ro-sub{animation:roRise .7s cubic-bezier(.2,.8,.2,1) .18s both}
  /* a variante aparece SÓ em landscape (viewport BAIXA) → dimensiona por vh p/ caber inteiro (o overlay
     original é medido pra retrato e estouraria a altura aqui, cortando o sub). */
  #rotation-overlay-portrait{gap:clamp(10px,2.6vh,22px);
    padding:max(16px,env(safe-area-inset-top,0px)) max(24px,max(var(--sar), env(safe-area-inset-right, 0px)))
            max(16px,env(safe-area-inset-bottom,0px)) max(24px,max(var(--sal), env(safe-area-inset-left, 0px)))}
  #rotation-overlay-portrait .ro-phone-wrap{width:clamp(84px,22vh,132px);height:clamp(84px,22vh,132px)}
  #rotation-overlay-portrait .ro-phone-svg{width:clamp(52px,13vh,80px)}
  #rotation-overlay-portrait .ro-title{font-size:clamp(30px,8.6vh,60px);letter-spacing:-1.2px;line-height:1.0}
  #rotation-overlay-portrait .ro-sub{font-size:clamp(12px,3vh,16px)}
}
/* telefone da variante gira o SENTIDO oposto (deitado → em pé), reforçando "gire pra vertical". */
#rotation-overlay-portrait .ro-phone-svg{animation:roTiltBack 2.8s ease-in-out infinite}
@keyframes roTiltBack{0%,26%,100%{transform:rotate(-90deg)}52%,76%{transform:rotate(0deg)}}
/* [v624] "Já comprei — ativar" (ação de retorno pós-compra): discreta, abaixo dos 2 cards. Login por OTP. */
/* [22/07 · Luis] TROCA DE POSIÇÃO E DE PESO entre os dois secundários.
   Antes: teste grátis EM CIMA e VERMELHO; "já comprei" embaixo e discreto.
   Agora: teste grátis EMBAIXO, preto e branco. O vermelho SAIU de vez desta tela.
   ⚠️ [24/07] A parte do "já comprei EM CIMA e VERDE" foi REVERTIDA — ver o bloco
   .three-activate-btn abaixo (o verde custou metade dos cliques de compra).
   Nenhum secundário é laranja de propósito: laranja é a cor da COMPRA aqui (os
   cards de plano usam --primary), então pintar um secundário de laranja faria dele
   um terceiro card de plano. */
.three-free{display:flex;justify-content:center;margin-top:4px}
.three-free-btn{display:flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.22);border-radius:12px;cursor:pointer;
  padding:12px 24px;transition:background .12s,border-color .12s,transform .1s;-webkit-tap-highlight-color:transparent}
.three-free-btn:hover{border-color:var(--primary)}
.three-free-btn:active{transform:scale(.985)}
.three-free-title{font-family:var(--font-b);font-weight:600;font-size:15px;color:var(--text);line-height:1.2;text-align:center}
body.dv-on #three-options .three-free{display:none!important}
/* entra junto do "já comprei" (mesma classe tc-in, mesmo momento: fim da locução) */
#three-options .three-free.three-choreo{opacity:0;transform:translateY(10px)}
/* [24/07 · Luis] O VERDE SAIU. Em 22/07 este botão virou card largo com contorno
   #22C55E — e verde é a cor universal de "prosseguir": a tela passou a ter DOIS
   elementos de ação disputando o toque, com o "já comprei" logo abaixo do card de
   compra. O funil registrou a troca no MESMO dia: InitiateCheckout caiu de ~124/dia
   pra 45 enquanto o Lead (login OTP concluído) subiu de 21 pra 57, com OpenApp no
   MAIOR valor do período — ou seja, as pessoas entravam igual e o clique migrava de
   COMPRAR pra JÁ COMPREI. Volta ao peso de LINK discreto: quem já comprou procura
   esse link; quem não comprou não deve tropeçar nele. O laranja fica sendo o único
   elemento de ação da tela.
   Alvo de toque preservado (~38px de altura) mesmo sem moldura. */
.three-activate{display:flex;flex-direction:column;align-items:center;gap:2px;margin-top:14px;text-align:center}
.three-activate-q{font-family:var(--font-b);font-size:13.5px;color:var(--muted)}
.three-activate-btn{display:inline-flex;align-items:center;justify-content:center;
  background:none;border:0;border-radius:0;cursor:pointer;
  color:var(--muted);font-family:var(--font-b);font-weight:600;font-size:14px;line-height:1.2;
  padding:10px 12px;text-decoration:underline;text-underline-offset:3px;
  transition:color .14s;-webkit-tap-highlight-color:transparent}
.three-activate-btn:hover{color:var(--text)}
.three-activate-btn:active{opacity:.7}
/* [v624] "voltar" do login-gate (login virou opcional): canto superior esquerdo, discreto. */
.login-back{position:absolute;top:calc(env(safe-area-inset-top) + 14px);left:16px;z-index:2;
  display:flex;align-items:center;gap:5px;background:none;border:0;cursor:pointer;
  color:var(--muted);font-family:var(--font-b);font-size:14px;padding:8px 6px;transition:color .12s}
.login-back:hover{color:var(--text)}
.login-back svg{width:20px;height:20px;display:block}
.three-card{display:flex;flex-direction:column;gap:7px;text-align:left;cursor:pointer;
  padding:20px 18px;border-radius:16px;border:1px solid var(--border2);
  background:rgba(18,18,20,.55);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  transition:transform .14s,border-color .14s,background .14s;color:inherit}
.three-card:hover{transform:translateY(-2px);border-color:var(--primary);background:rgba(24,24,26,.68)}
.three-card:active{transform:translateY(0)}
.three-card-ico{width:32px;height:32px;color:var(--primary)}
.three-card-ico svg{width:100%;height:100%;display:block}
.three-card-title{font-family:var(--font-h);font-weight:800;font-size:18px;letter-spacing:-.2px}
.three-card-sub{font-family:var(--font-b);font-size:13.5px;line-height:1.45;color:var(--muted)}
/* [Fase6] Preço regional no card de compra (BR R$49,90 · LatAm US$19,90 · Mundo US$49,90) */
.three-card-price{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;font-family:var(--font-h);font-weight:800;font-size:clamp(20px,5.4vw,26px);color:var(--primary);margin-top:6px;letter-spacing:.2px}
/* ===== [F3 · TT_DECISION_V2] decisão nova: vitalício LARANJA (padrão ms-card--kbd) + mensal AZUL
   (padrão ms-card--screen do mode-select). Fora do body.dv2-on NADA disto se aplica — BR e flag off
   ficam com a decisão atual byte-idêntica. Vitalício entra COLAPSADO (max-height/opacity) e o
   .dv2-vit-in expande — o mensal desce junto no fluxo (o "arrastado pra baixo" da spec). */
#three-mensal{display:none}
body.dv2-on .three-sub{display:none}
body.dv2-on #three-buy{border:1.5px solid rgba(232,105,10,.50);border-radius:18px;
  background:radial-gradient(ellipse 100% 130% at 18% 18%,rgba(232,105,10,.22) 0%,rgba(232,105,10,.06) 50%,transparent 74%),rgba(16,10,6,.55);
  box-shadow:0 16px 44px rgba(0,0,0,.50);
  max-height:0;opacity:0;overflow:hidden;padding-top:0;padding-bottom:0;border-width:0;pointer-events:none;
  transition:max-height .55s cubic-bezier(.2,.9,.3,1),opacity .5s ease,padding .4s ease}
body.dv2-on #three-buy.dv2-vit-in{max-height:380px;opacity:1;padding:20px 18px;border-width:1.5px;pointer-events:auto}
body.dv2-on #three-buy .three-card-title{color:var(--primary-2)}
body.dv2-on #three-mensal.dv2-men-in{display:flex;border:1px solid rgba(100,200,255,.42);
  background:radial-gradient(ellipse 100% 130% at 18% 18%,rgba(100,200,255,.16) 0%,rgba(100,200,255,.05) 50%,transparent 74%),rgba(8,14,20,.55);
  animation:dv2MenIn .5s ease}
body.dv2-on #three-mensal .three-card-title{color:#64C8FF}
/* [F4.2 · Luis device 17/07] título "muito seco" → entrada BEM mais suave no V2 (fade longo, deslize
   curto); e RESERVA de altura no bloco de cards — o vitalício expandindo empurrava o conteúdo e o
   título (centralizado por margin:auto) SUBIA. Com o espaço reservado desde o início, o título fica
   IMÓVEL e o mensal ainda desce quando o vitalício entra (efeito da spec preservado). */
body.dv2-on #three-options .three-title.three-choreo{transform:translateY(8px);transition:opacity 1.6s ease,transform 1.6s cubic-bezier(.16,.7,.3,1)}
body.dv2-on .three-cards{min-height:352px;align-content:start}
/* [F4.6 · Luis device 17/07] título MAIOR e com respiro do topo (estava colado na extremidade) */
body.dv2-on .three-title{font-size:clamp(36px,10.4vw,68px);margin-top:clamp(34px,7vh,72px)}
body.dv2-on #three-mensal[disabled]{opacity:1;cursor:default}   /* bloqueado SEM acinzentar (spec): visual normal, só inerte */
body.dv2-on #three-mensal[disabled]:hover,body.dv2-on #three-mensal[disabled]:active{transform:none;
  border-color:rgba(100,200,255,.42)}
@keyframes dv2MenIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.three-card-price .tcp-once{font-family:var(--font-b);font-weight:600;font-size:12px;color:var(--muted);letter-spacing:.4px;text-transform:uppercase}
.three-card-price .tcp-loading{opacity:.4;font-weight:700}
/* [Fase5·Alt-A] Coreografia da decisão: card de compra entra primeiro (+shimmer), secundários (grátis + "já comprei") depois. */
body.three-on #three-options .three-choreo{opacity:0;transform:translateY(16px);transition:opacity .5s ease,transform .55s cubic-bezier(.2,.9,.3,1)}
body.three-on #three-options .three-choreo.tc-in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){ body.three-on #three-options .three-choreo{transition-duration:.01ms} }
#three-buy.tc-shimmer{position:relative;overflow:hidden}
/* [Fase5·quebra] shimmer em LOOP enquanto a locução decisao-vende fala; JS remove .tc-shimmer no fim da fala p/ parar. */
#three-buy.tc-shimmer::after{content:'';position:absolute;inset:0;pointer-events:none;background:linear-gradient(105deg,transparent 32%,rgba(255,178,74,.20) 50%,transparent 68%);transform:translateX(-130%);animation:tcShimmer 2.6s ease-in-out .35s infinite}
@keyframes tcShimmer{to{transform:translateX(130%)}}
/* [karaoke/v707] LEGENDA da locução decisao-vende. Enquanto o karaokê está ativo (body.dv-on) o secundário
   ("já comprei — ativar"; card teste grátis REMOVIDO no v707) fica display:none (não reserva espaço → o texto
   ocupa o lugar dele, logo abaixo do card de compra). Ao sair (dv-out) o texto colapsa animado e ele entra. */
body.dv-on #three-options .three-activate{display:none!important}
/* [22/07 · Luis] REDE. O caminho normal é: fim da locução → __ttRevealThreeSecondary →
 * karaokê.out() TIRA o dv-on → os dois secundários entram com tc-in. Se aquele out()
 * falhar (karaokê ausente/erro), o dv-on ficaria pendurado e os dois sumiriam PRA
 * SEMPRE — que é a família de bug do relato de hoje. Com isto, quem manda é o tc-in:
 * revelou, aparece, independente do estado do karaokê. (Substitui o par dv-act-in do
 * 21/07, removido junto com o __ttArmActivateReveal.) */
body.dv-on #three-options .three-activate.tc-in,
body.dv-on #three-options .three-free.tc-in{display:flex!important}
.dv-karaoke{max-width:580px;margin:8px auto 0;text-align:center;font-family:var(--font-h);
  font-weight:600;font-size:clamp(11.5px,3.05vw,13.5px);line-height:1.5;color:rgba(255,255,255,.82);letter-spacing:.003em;
  max-height:54vh;overflow:hidden;transition:opacity .5s ease, transform .5s ease, max-height .55s ease, margin .5s ease}
.dv-karaoke .dv-word{display:inline;opacity:0;filter:blur(4px);transform:translateY(7px);
  transition:opacity .45s ease, transform .5s cubic-bezier(.2,.85,.25,1), filter .45s ease}
.dv-karaoke .dv-word.on{opacity:1;filter:none;transform:none}
.dv-karaoke .dv-word.kw{color:var(--primary);font-weight:700}          /* palavras-chave em laranja de marca */
.dv-karaoke.dv-out{opacity:0;transform:translateY(-12px);max-height:0;margin-top:0;pointer-events:none}
/* [v771·F1-desktop] Karaokê NUNCA estoura a tela (pendente do v743, visto na auditoria F0 em janela
   baixa de desktop): altura máx ~3 linhas + rolagem interna programática (follow no tickOnce do
   DVKaraoke). Overflow SÓ aqui dentro — título/cards INTOCADOS (coreografia V2 preservada).
   position:relative ancora o offsetTop dos .dv-word no próprio host. .dv-out (acima) segue vencendo
   por especificidade (max-height:0 na saída). */
.dv-karaoke{ position:relative; max-height:4.6em; overflow:hidden; }
/* [v743·REVERT karaoke-fit na V2] REMOVIDO o override de layout (padding-top/gap/margin-top/margin-bottom:auto
   + sub). Ele vazava POR CIMA da coreografia V2, que DEPENDE de `.three-inner{margin:auto 0}` (centralizado)
   + `.three-cards{min-height:352px}` pra manter o TÍTULO IMÓVEL e o mensal descer quando o vitalício entra
   (design F4.2/F4.6). O top-align do v738 matava o margin:auto durante a narração (dv-on) → título subia/cortava
   = a regressão. Coreografia restaurada. O overflow do karaokê é tratado SEM mexer na coreografia (só entrelinha
   do próprio #dv-karaoke, abaixo — não move título nem cards). */
body.dv-on.dv2-on .dv-karaoke{line-height:1.4}
/* [ESCUDO DE TOQUE DURANTE A LOCUÇÃO — REVOGADO em 01/09/2026 pelo Luis] --
   "permita os cliques durante a locução."

   O que morreu aqui: `body.loc-block:not(.tt-walk)` punha `pointer-events:none`
   em `#app`, `#three-options` e `#login-gate` enquanto QUALQUER locução tocava
   (o poll rAF do index lê `__locucaoActive` / `__dvVozOn` / `tt-narrating`). Era
   o escudo de 29/07 — mais velho e MUITO mais largo que o véu do tt-loc.js, e o
   que de fato prendia o dedo: MEDIDO nesta bancada, com o véu ja aposentado o
   `elementFromPoint` sobre o botão de importar da mp-folha ainda devolvia o
   BODY, e o Playwright não conseguia clicar. Tirar só o véu seria entregar
   meia ordem.

   As duas exceções que existiam (mode-select e score-play em
   `pointer-events:auto`, e o `#three-buy` sempre clicável) saíram junto: sem
   escudo não há o que reabrir. O tutorial (`.tt-walk`) nunca esteve aqui —
   segue gerenciando os próprios toques.

   A classe `loc-block` CONTINUA sendo ligada pelo poll do index: ela e um
   marcador de estado ("há voz no ar") e ha quem a leia. O que sumiu é o
   bloqueio. Se um dia ele voltar, volta AQUI — num lugar só. */
@media (prefers-reduced-motion: reduce){
  .dv-karaoke .dv-word{transition:opacity .2s ease;filter:none;transform:none}
}
.three-card.is-primary{border-color:var(--primary);
  background:linear-gradient(160deg,rgba(232,105,10,.17) 0%,rgba(18,18,20,.6) 62%)}
.three-card.is-primary .three-card-title{color:var(--primary)}

/* "Ouvir referência" desabilitado na unit-intro bloqueada. */
.audio-btn[disabled]{opacity:.45;cursor:default;pointer-events:none;filter:grayscale(1)}

/* Teclas C-E-G "acionadas" em P&B (sem laranja) na unit-intro bloqueada + label "?". */
.kbd-key.kbd-locked{fill:#7C7C7C;stroke:#000}
.kbd-letter.kbd-letter-locked{fill:#FFFFFF;font-size:16px}

/* Botão de compra laranja no modal "Sua conta" (não-pagante). Mesma forma dos botões do modal. */
.account-buy-btn{margin-top:2px;width:100%;padding:12px 16px;border-radius:10px;border:0;cursor:pointer;
  background:linear-gradient(135deg,#F2790E 0%,#E8690A 55%,#C9550A 100%);color:#fff;
  font-family:var(--font-h);font-weight:800;font-size:14.5px;letter-spacing:.2px;
  box-shadow:0 8px 24px rgba(232,105,10,.35),0 1px 0 rgba(255,255,255,.14) inset;transition:transform .12s,filter .12s}
.account-buy-btn:hover{transform:translateY(-1px);filter:brightness(1.04)}
.account-buy-btn:active{transform:translateY(0);filter:brightness(.96)}
.account-recheck-btn{margin-top:8px}
.account-recheck-note{font-size:12px;color:var(--muted);margin:8px 0 0;line-height:1.4;min-height:1em}
.account-recheck-note.ok{color:#22C55E}

/* [3.2b-3] Resgate de código no modal (não-pagante). Fica acima de Comprar. */
.account-redeem{margin:2px 0 14px;padding-bottom:14px;border-bottom:1px solid rgba(255,255,255,.08)}
.account-redeem-label{display:block;font-size:12.5px;color:var(--muted);margin:0 0 8px;line-height:1.35}
.account-code-input{width:100%;padding:12px 14px;border-radius:10px;border:1px solid rgba(255,255,255,.16);
  background:rgba(0,0,0,.28);color:#fff;font-family:var(--font-h);font-weight:800;font-size:16px;letter-spacing:2px;
  text-align:center;text-transform:uppercase;box-sizing:border-box;outline:none;transition:border-color .12s}
.account-code-input::placeholder{color:rgba(255,255,255,.32);letter-spacing:2px;font-weight:700}
.account-code-input:focus{border-color:#E8690A}
.account-redeem-btn{margin-top:8px}
.account-redeem-note{font-size:12px;color:var(--muted);margin:8px 0 0;line-height:1.4;min-height:1em}
.account-redeem-note.ok{color:#22C55E}
.account-redeem-wa{color:#25D366;font-weight:700;text-decoration:none;white-space:nowrap}
.account-redeem-wa:hover{text-decoration:underline}

/* Toast de placeholder do checkout (Fase 3). */
.checkout-toast{position:fixed;left:50%;bottom:calc(24px + env(safe-area-inset-bottom));transform:translate(-50%,12px);
  z-index:2000;max-width:min(360px,90vw);padding:13px 18px;border-radius:12px;
  background:rgba(20,20,20,.96);border:1px solid var(--primary);color:var(--text);
  font-family:var(--font-b);font-size:13px;line-height:1.45;text-align:center;
  box-shadow:0 12px 40px rgba(0,0,0,.5);opacity:0;pointer-events:none;transition:opacity .22s,transform .22s}
.checkout-toast.show{opacity:1;transform:translate(-50%,0)}
body.mode-print .checkout-toast{display:none!important}

/* ============ LEVEL-UP TOAST ============ */
.lvl-toast{
  position:fixed;left:50%;top:calc(80px + env(safe-area-inset-top));
  transform:translate(-50%, -20px);
  z-index:600;opacity:0;pointer-events:none;
  transition:opacity .35s ease, transform .45s cubic-bezier(.2,.8,.2,1);
}
.lvl-toast.show{opacity:1;transform:translate(-50%, 0);pointer-events:auto}
.lvl-toast-inner{
  display:flex;align-items:center;gap:14px;
  padding:14px 22px;border-radius:14px;
  background:linear-gradient(135deg, rgba(28,28,28,.95) 0%, rgba(20,20,20,.95) 100%);
  border:1px solid rgba(232,105,10,.55);
  box-shadow:0 18px 48px rgba(0,0,0,.55), 0 0 32px rgba(232,105,10,.35);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  min-width:260px;
}
.lvl-toast-icon{
  width:42px;height:42px;border-radius:50%;
  background:color-mix(in srgb, var(--accent) 18%, transparent);
  border:2px solid var(--accent);
  color:var(--accent);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-h);font-weight:900;font-size:20px;
}
.lvl-toast-text{display:flex;flex-direction:column;gap:2px}
.lvl-toast-text strong{
  font-family:var(--font-h);font-weight:700;font-size:11px;letter-spacing:.18em;
  color:var(--muted);text-transform:uppercase;
}
.lvl-toast-text span{
  font-family:var(--font-h);font-weight:900;font-size:18px;letter-spacing:-.2px;
}
/* [v327] botão "Iniciar Click" no aviso de conclusão do guiado */
.lvl-toast-btn{
  margin-left:6px;cursor:pointer;white-space:nowrap;-webkit-tap-highlight-color:transparent;
  background:var(--primary);color:#fff;border:0;border-radius:10px;padding:10px 16px;
  font-family:var(--font-h);font-weight:800;font-size:13px;letter-spacing:.06em;text-transform:uppercase;
  box-shadow:0 4px 14px rgba(232,105,10,.4);
}
.lvl-toast-btn:active{filter:brightness(.9)}
/* [v330] Aviso de conclusão do guiado — card próprio (sem ícone): título
   centralizado em 1 linha + linha inferior (texto esquerda / botão direita). */
.guided-done .gd-card{
  display:flex;flex-direction:column;gap:12px;
  padding:16px 22px;border-radius:14px;
  background:linear-gradient(135deg, rgba(28,28,28,.95) 0%, rgba(20,20,20,.95) 100%);
  border:1px solid rgba(232,105,10,.55);
  box-shadow:0 18px 48px rgba(0,0,0,.55), 0 0 32px rgba(232,105,10,.35);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  min-width:300px;max-width:min(92vw,440px);
}
.guided-done .gd-title{
  font-family:var(--font-h);font-weight:900;font-size:clamp(15px,4.2vw,18px);
  letter-spacing:-.2px;color:var(--text);text-align:center;white-space:nowrap;
}
.guided-done .gd-row{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
}
.guided-done .gd-text{
  font-family:var(--font-h);font-weight:700;font-size:14px;line-height:1.25;
  color:var(--muted);text-align:left;
}
.guided-done .lvl-toast-btn{ flex:0 0 auto;margin-left:0; }

/* [remoção timbres] MOOG INFO MODAL (pitch bend tip) removido. */

/* ============ POPOVER MENU (base reaproveitada por tom-menu e rhythm-menu) ============ */
/* [remoção timbres] .timbre-host / .timbre-chip removidos. As classes
   .timbre-menu / .t-opt / .t-opt-dot / .t-opt-info / .t-opt-check seguem aqui
   porque #tom-menu e #rhythm-menu as reaproveitam. */
.timbre-menu{
  position:absolute;top:calc(100% + 8px);right:0;
  width:240px;
  background:rgba(18,18,18,.96);border:1px solid var(--border2);
  border-radius:12px;
  box-shadow:0 18px 40px rgba(0,0,0,.55), 0 0 0 1px rgba(232,105,10,.10) inset;
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  padding:6px;
  display:none;
  animation: tMenuIn .18s ease-out;
}
.timbre-menu.open{display:block}
@keyframes tMenuIn{
  0%  {opacity:0;transform:translateY(-6px) scale(.98)}
  100%{opacity:1;transform:translateY(0)    scale(1)}
}
/* [v508] SAÍDA animada dos menus (.timbre-menu): o close por display:none não animava → o menu sumia
   SECO (relatado pelo Luis no #tom-menu ao entrar o encerramento). .closing mantém o menu visível
   tocando tMenuOut (espelho do tMenuIn) e só então o JS (closeAnimated) o remove. Fundo estelar #c
   persiste por baixo; a nova tela (sidebar) entra com seu próprio slide. */
.timbre-menu.closing{display:block;pointer-events:none;animation:tMenuOut .16s ease-in both}
@keyframes tMenuOut{
  0%  {opacity:1;transform:translateY(0)    scale(1)}
  100%{opacity:0;transform:translateY(-6px) scale(.98)}
}
.t-menu-head{
  font-family:var(--font-h);font-weight:800;font-size:10px;letter-spacing:.20em;
  color:var(--muted);text-transform:uppercase;
  padding:8px 12px 6px;
}
.t-opt{
  display:flex;align-items:center;gap:10px;width:100%;
  background:transparent;border:none;color:var(--text);
  padding:9px 12px;border-radius:8px;cursor:pointer;text-align:left;
  font-family:var(--font-b);
  -webkit-tap-highlight-color:transparent;
  transition:background .12s;
}
.t-opt:hover{background:var(--card2)}
/* [v251] Popover de levadas em Progressão: opção corrente destacada;
   se demo loop ativo, ganha pulse verde discreto. */
#rhythm-menu .rp-opt.is-current{background:rgba(232,105,10,.12);box-shadow:inset 0 0 0 1px rgba(232,105,10,.28)}
#rhythm-menu .rp-opt.is-current .t-opt-dot{color:#FF7B1A}
/* [v257] Ícones das levadas (■ ⠁ ♪ ∿) maiores pra leitura fácil. O menu de
   timbres compartilha `.t-opt-dot` mas mantém 14px/11px (escopo só aqui). */
#rhythm-menu .rp-opt .t-opt-dot{
  width:24px;height:24px;font-size:18px;line-height:1;
}
#rhythm-menu .rp-opt.is-playing{
  animation: rpPlaying 1.8s ease-in-out infinite;
}
@keyframes rpPlaying{
  0%,100%{box-shadow:inset 0 0 0 1px rgba(74,222,128,.45),0 0 0 0 rgba(74,222,128,.45)}
  50%    {box-shadow:inset 0 0 0 1px rgba(74,222,128,.85),0 0 10px 1px rgba(74,222,128,.0)}
}
/* [v397] Menu de inversões — opção corrente destacada (laranja), espelhando o
   menu de levadas. O check (.t-opt-check) já aparece via .t-opt.is-current. */
#inversion-menu .iv-opt.is-current{background:rgba(232,105,10,.12);box-shadow:inset 0 0 0 1px rgba(232,105,10,.28)}
#inversion-menu .iv-opt.is-current .t-opt-dot{color:#FF7B1A}
#inversion-menu .iv-opt .t-opt-dot{width:22px;height:22px;font-size:16px}
/* [v397] Tag laranja "Inversões" na practice-bar (zona direita), com chevron de
   dropdown. Visível SÓ na etapa Click de acordes elegíveis (classe stage-click);
   escondida no Guiado (body.guided-active), no IDLE e fora de acorde. */
/* [v425] Inversões virou um BOTÃO REDONDO do menu (.pb-icon-btn) só com ícone de
   dropdown — sem o pill laranja "Inversões" que sobrepunha o Click. Toda a aparência
   (círculo, tamanho, hover) vem de .pb-icon-btn; aqui só controlamos a VISIBILIDADE:
   aparece só na etapa Click de acordes elegíveis; some no Guiado, IDLE e fora de acorde. */
.pb-icon-btn.pb-inv-btn{display:none} /* compound vence o .pb-icon-btn{display:flex} (mesma 0,1,0, mas vem depois) */
.practice-page.stage-click .pb-icon-btn.pb-inv-btn{display:inline-flex}
body.guided-active .pb-icon-btn.pb-inv-btn{display:none!important}
.t-opt-dot{
  width:14px;height:14px;border-radius:50%;flex-shrink:0;
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--font-h);font-weight:900;color:var(--primary);font-size:11px;
}
.t-opt-info{flex:1;display:flex;flex-direction:column;gap:1px}
.t-opt-info strong{color:var(--text);font-size:12.5px;font-family:var(--font-h);font-weight:700;letter-spacing:.02em}
.t-opt-info em{color:var(--muted);font-style:normal;font-size:11px}
.t-opt-check{color:var(--primary);font-weight:900;font-size:13px;opacity:0;flex-shrink:0;transition:opacity .12s}
.t-opt.is-current .t-opt-check{opacity:1}
/* [v199] Menu de tonalidades — visual idêntico ao timbre-menu, mas com
   altura limitada (3 opções visíveis) + scroll vertical pra ver os 12 tons. */
.tom-menu{ width:230px; }
.tom-menu .tom-menu-scroll{
  max-height:calc(3 * 44px + 4px); /* ~3 itens visíveis */
  overflow-y:auto;
  scrollbar-width:thin;
  scrollbar-color:rgba(232,105,10,.5) transparent;
}
.tom-menu .tom-menu-scroll::-webkit-scrollbar{ width:6px }
.tom-menu .tom-menu-scroll::-webkit-scrollbar-track{ background:transparent }
.tom-menu .tom-menu-scroll::-webkit-scrollbar-thumb{
  background:rgba(232,105,10,.45); border-radius:3px;
}
/* [v210] dot colore por --accent (tier do tom): verde/laranja/vermelho */
.tom-menu .t-opt-dot{
  background:color-mix(in srgb, var(--accent, #E8690A) 12%, transparent);
  border:1.5px solid color-mix(in srgb, var(--accent, #E8690A) 58%, transparent);
  color:var(--accent, #E8690A);
  font-family:var(--font-h);font-weight:900;
  font-size:11px;letter-spacing:-.5px;
  width:26px;height:26px;
}
.tom-menu .t-opt.is-current .t-opt-dot{
  background:color-mix(in srgb, var(--accent, #E8690A) 24%, transparent);
  border-color:color-mix(in srgb, var(--accent, #E8690A) 80%, transparent);
  box-shadow:0 0 8px color-mix(in srgb, var(--accent, #E8690A) 50%, transparent);
}
.tom-menu .t-opt-check{ color:var(--accent, var(--primary)); }
@media (max-width:480px){
  .timbre-menu{width:220px}
}

/* ============ NAV TAGS — pílulas com 3 variantes ============
 * .is-round (default): círculo só com seta — para páginas onde texto distrairia
 * (com label visível): pílula com texto — capa ("Avançar") e prática ("Próximo tom Sol") */
.nav-tags{
  position:fixed;left:0;right:0;
  bottom:calc(14px + env(safe-area-inset-bottom));
  display:flex;justify-content:space-between;align-items:center;
  padding:0 12px;pointer-events:none;z-index:150;
}
.nav-tag{
  pointer-events:auto;
  display:inline-flex;align-items:center;gap:8px;
  padding:7px 14px;border-radius:999px;
  background:rgba(18,18,18,.78);
  border:1px solid rgba(232,105,10,.42);
  color:var(--primary);
  font-family:var(--font-h);font-weight:800;font-size:10.5px;letter-spacing:.16em;
  text-transform:uppercase;cursor:pointer;white-space:nowrap;
  -webkit-tap-highlight-color:transparent;
  /* [v144] touch-action: manipulation — remove delay de 300ms do double-tap-zoom
     e força o navegador a tratar tap como click imediato */
  touch-action: manipulation;
  transition:background .15s, color .15s, transform .12s, box-shadow .15s, padding .15s, width .15s;
  box-shadow:0 4px 14px rgba(0,0,0,.32);
}
/* [v144] :hover ESCOPADO em devices que realmente têm hover (mouse/trackpad).
   Em touch (iOS/Android), o :hover era "sticky" — primeiro tap ativava só o
   hover, segundo tap disparava o click. Fix conhecido: gate via media query. */
@media (hover: hover) and (pointer: fine){
  .nav-tag:hover{background:var(--primary);color:#FFFFFF;border-color:var(--primary);box-shadow:0 6px 18px rgba(232,105,10,.40)}
}
.nav-tag:active{transform:translateY(1px); background:var(--primary); color:#FFFFFF; border-color:var(--primary);}
.nav-tag svg{width:12px;height:12px;flex-shrink:0;display:block}
.nav-tag-label{display:inline-block}
.nav-tag.is-hidden{visibility:hidden;pointer-events:none}
/* Variante redonda — só seta, sem label */
.nav-tag.is-round{
  width:38px;height:38px;padding:0;justify-content:center;gap:0;
}
.nav-tag.is-round .nav-tag-label{display:none}
.nav-tag.is-round svg{width:14px;height:14px}
body.mode-print .nav-tags{display:none}
@media (max-width:480px){
  .nav-tag{padding:6px 12px;font-size:9.5px;letter-spacing:.12em;gap:6px}
  .nav-tag.is-round{width:36px;height:36px;padding:0}
}
/* Em prática (label longo "Próximo tom Sol"): permite quebrar pra evitar pílula gigante */
.nav-tag.is-practice{font-size:10px;letter-spacing:.10em}
@media (max-width:380px){
  .nav-tag.is-practice .nav-tag-label{font-size:9px}
}
/* [v2.1] Glow pulsante sutil só no #nextBtn. Não anima quando escondido. */
@keyframes navTagNextPulse{
  0%,100%{box-shadow:0 4px 14px rgba(0,0,0,.32), 0 0 0 0 rgba(232,105,10,.45)}
  50%    {box-shadow:0 6px 18px rgba(232,105,10,.30), 0 0 0 9px rgba(232,105,10,0)}
}
#nextBtn:not(.is-hidden){
  animation: navTagNextPulse 2s ease-in-out infinite;
}
/* [v369] No GUIADO o #practiceNext vira o botão PULAR: pílula LARANJA com texto "Pular" e seta à
   DIREITA (v402 — pular = avançar), que pula DIRETO pro Click (não pro próximo tom). É a ÚNICA nav
   de pular — sem botão redundante (o antigo botão Pular foi reaproveitado aqui). */
.practice-bar .nav-tag.is-pb-nav.is-guided-skip{
  width:auto; height:auto; padding:8px 16px; gap:7px; border-radius:999px;
  background:var(--primary); border-color:var(--primary); color:#fff;
  box-shadow:0 4px 16px rgba(232,105,10,.5);
}
.nav-tag.is-pb-nav.is-guided-skip .nav-tag-label{ display:inline-block; font-size:11px; letter-spacing:.12em; }
.nav-tag.is-pb-nav.is-guided-skip svg{ width:14px; height:14px; } /* [v402] seta pra DIREITA (pular = avançar pro Click) */

/* ============ PRACTICE PAGE — fullscreen-only horizontal sem scroll ============ */
.practice-page{
  /* [v78] position:fixed com inset:0 garante que a página cubra fisicamente
     100% do viewport, INCLUSIVE atrás de dynamic island/notch. Sem isso,
     o flex layout do body deixava margem preta na lateral do cutout em iOS PWA.
     Só aplica quando .visible (display:flex) — `display:none` por padrão. */
  height:100vh;height:100dvh;
  min-height:0;
  max-height:100dvh;
  width:100vw;max-width:100vw;
  margin:0;
  overflow:hidden;
  padding:0!important;
  display:flex;flex-direction:column;
}
body.mode-interactive .practice-page.visible{
  position:fixed;
  inset:0;
  top:0;left:0;right:0;bottom:0;
  z-index:5;
}
.practice-bar{
  /* [v2.26] position:relative pra zone-center poder ser absolute-centered
     ao centro geométrico da bar — independente do conteúdo das zonas
     left/right, garantindo simetria perfeita. */
  position:relative;
  /* [v425] Diâmetro ÚNICO dos 5 botões redondos do menu (inversões, cifras, reiniciar,
     play, avançar). Define aqui e todos herdam → tamanhos sempre simétricos por breakpoint. */
  --pb-btn:40px; --pb-btn-fz:15px; /* [v645] 44→40: device 926pt usa BASE; botão um pouco menor abre a folga Click↔inversão */
  display:flex;
  align-items:center;
  gap:clamp(12px, 2vw, 24px);
  /* [v81] padding-left ≥ 78 desloca o card pb-tom pra direita, evitando
     sobreposição com #menu-btn/#practicePrev (agora 38x38, left:12+safe).
     Botões fixos ocupam até ~12+38+8 = 58px da borda; pad 78 dá folga visual. */
  /* [v154] Padding assimétrico: esquerda grande (folga pro #practicePrev fixo),
     direita pequena (navTag avança fica perto da borda, mas nunca toca). */
  /* [v644] padding-left INCLUI a safe area: em landscape com notch à esquerda o
     .menu-btn (left:12+safe) era empurrado pra dentro e o pb-tom ficava ATRÁS dele. Sem notch
     (safe=0) o valor não muda. */
  /* [22/07 · Luis] "o card de tonalidade permanece afastado dos ícones da esquerda".
     MEDIDO no print do device (844×390, notch ≈42pt): o card nascia em 171pt com os
     ícones terminando em 88 — 83pt de vão morto. Eram DUAS somas do mesmo inset:
     este padding (env + até 110) mais um `padding-left:env()` na .pb-zone-left. E o
     clamp(78,10vw,110) era generoso demais: a pilha fixa (☰ / voltar / modo) ocupa
     12 + 38 = 50px a partir da borda segura, nada além disso precisa ser liberado.
     Agora: inset + 50 da pilha + 14 de respiro = o card encosta nos ícones. */
  padding: 10px max(14px, calc(max(var(--sar), env(safe-area-inset-right, 0px)) + 10px))
           10px calc(max(var(--sal), env(safe-area-inset-left, 0px)) + 64px);
  /* [v2.28] background com gradiente brand: glow laranja sutil radial no
     centro + linear vertical card→bg. Coerente com a identidade visual
     (mesma curva radial usada na .practice-intro-page). Borda inferior
     com tinta laranja ao invés de border2 puro. */
  /* [INTEGRAÇÃO] barra TRANSPARENTE sobre a constelação (unifica o app com a experiência);
     gradiente laranja antigo removido. Divisor neutro discreto. */
  background:transparent;
  border-bottom:1px solid rgba(255,255,255,.07);
  box-shadow:none;
  flex-shrink:0;
  /* [v2.27] altura aumentada novamente — barra ainda mais robusta,
     piano com proporção mais próxima do natural. min 140, ideal 35vh, max 220. */
  height:auto;
  min-height:clamp(140px, 35vh, 220px);
  max-height:none;
}
/* [v2.26] Layout flexível com zone-center absoluto:
   - left e right: flex:1 (espaços simétricos esquerda/direita)
   - center: position:absolute, ancorada no centro geométrico da bar */
/* [22/07 · Luis, layout definitivo da barra de prática]
   O pedido, literal: "ícones da esquerda + card em uma ponta, ícones redondos da
   direita na ponta direita, e o menu central sempre centralizado nesse espaço que
   ficou no meio (cada exercício vai ter um espaço diferente). Nunca sobrepor."
   Isso é exatamente o que o FLUXO FLEX faz de graça — e é a razão de a arquitetura
   antiga não parar de quebrar: a zona central era `position:absolute; left:50%`
   (centro da TELA, não do vão) e um resolvedor em JS calculava um empurrão pra
   compensar. Empurrão calculado uma vez fica velho quando o conteúdo muda (trocar
   de GUIADO pra CLICK acende estágios + BPM e engorda o centro), e aí ele pousa em
   cima dos botões da direita. Foi o que apareceu no print do Luis.
   Em fluxo, esquerda e direita ocupam o que precisam, o centro fica com o resto e se
   centraliza nele. Sobreposição deixa de ser possível por construção — não por conta.
   (O modo guiado já fazia assim desde o F-lote; agora é o padrão de todos.) */
.pb-zone-left{flex:0 1 auto;display:flex;align-items:center;gap:14px;min-width:0;justify-content:flex-start}
/* [21/07 · Luis] O troca-de-modo entra na PILHA FIXA da esquerda, abaixo da nav
 * tag — que é ☰ (12px) → #practicePrev (12+38+8) → modo (mais um degrau).
 * A nav tag é position:fixed, então uma coluna flex normal NÃO a continha: ela
 * já estava fora do fluxo e o botão de modo ficava sozinho AO LADO do card,
 * comendo largura. Com display:contents o wrapper some do layout e os dois se
 * posicionam sozinhos — é isso que devolve espaço pro menu central. */
.pb-navcol{ display:contents; }
.practice-page .pb-mode-btn{
  position:fixed;
  top: calc(12px + env(safe-area-inset-top) + 38px + 8px + 38px + 8px);
  left: calc(12px + max(var(--sal), env(safe-area-inset-left, 0px)));
  width:38px; height:38px; z-index:220;
  background:rgba(20,20,20,.92); border:1px solid var(--border2);
}
.pb-mode-btn svg{ width:15px;height:15px; }
@media (orientation:landscape) and (max-width:720px){
  .practice-page .pb-mode-btn{
    width:34px; height:34px;
    top: calc(12px + env(safe-area-inset-top) + 34px + 6px + 34px + 6px);
  }
}
/* [22/07 · Luis] "o card de tonalidade está bem afastado dos ícones da esquerda"
   — e estava: eram DOIS recuos empilhados. A .practice-bar já recua
   clamp(78px,10vw,110px), o que sozinho passa MUITO longe da pilha fixa (que
   termina em x=50); estes 38px extras eram cópia da mesma proteção, medidos em
   64px de vão morto entre os ícones e o card.
   Além de feio, o vão era o que fazia o menu central não caber no corredor e
   cair no ramo de truncagem do resolvedor — que é onde nascia a SOBREPOSIÇÃO.
   Devolvendo esses 38px ao corredor, o menu volta a caber e se centraliza.
   O safe-area continua: em landscape com notch à esquerda ele é real. */
/* [22/07] o `padding-left:env(safe-area-inset-left)` que existia aqui foi REMOVIDO:
   a .practice-bar já soma o inset no padding dela, e somar de novo dobrava o vão. */
.pb-zone-right{flex:0 0 auto;display:flex;align-items:center;gap:10px;justify-content:flex-end;flex-wrap:nowrap}
.pb-zone-center{
  /* em FLUXO: fica com o vão que sobrar e centraliza o conteúdo nele.
     min-width:0 deixa encolher em vez de empurrar a direita pra fora. */
  flex:1 1 auto;min-width:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  pointer-events:none; /* deixa cliques chegarem em filhos sem bloquear left/right */
}
.pb-zone-center > *{pointer-events:auto}
/* [22/07] Os shifts históricos (v2.29 · v425 · v646 · v763 · pb-right-heavy) foram
   REMOVIDOS junto com o posicionamento absoluto. Todos existiam pra compensar o fato
   de o centro se alinhar ao meio da TELA; em fluxo não há o que compensar. */
.pb-tom{
  font-family:var(--font-h);font-weight:900;
  /* [v2.26] aumentado: era clamp(20px,3vw,30px) */
  font-size:clamp(26px, 4vw, 38px);
  color:var(--primary);letter-spacing:-1px;line-height:1;
  border:1.5px solid rgba(232,105,10,.45);background:rgba(232,105,10,.10);
  /* [v2.26] aumentado: era 5px 10px */
  padding:7px 14px;border-radius:12px;flex-shrink:0;
  display:flex;flex-direction:column;align-items:center;gap:1px;
  /* [v199] clicável — abre menu de seleção de tonalidade */
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:background .15s, border-color .15s, transform .12s;
}
.pb-tom:hover{
  background:rgba(232,105,10,.18);
  border-color:rgba(232,105,10,.7);
}
.pb-tom:active{ transform:scale(.96); }
.pb-tom.is-open{
  background:rgba(232,105,10,.22);
  border-color:rgba(232,105,10,.85);
}
.pb-tom-pt{font-size:.40em;color:var(--muted);font-weight:700;letter-spacing:.05em}
.pb-info{min-width:0;display:flex;flex-direction:column;gap:2px}
/* [22/07] O resolvedor em JS (pblayout.js) e a classe .pb-solved saíram. Ele existia
   pra calcular um empurrão do centro absoluto; em fluxo não há empurrão a calcular, e
   o teto do card virou o encolhimento natural do flex (min-width:0 + ellipsis). */
/* [v224] Pílula de seção (Acorde / Modo / Escala) acima do nome do exercício.
   Pequeno e discreto pra não roubar destaque do nome ("Maior Natural").
   Como .pb-zone-left tem align-items:center, o bloco .pb-info de 3 linhas
   fica vertical-center com a .pb-tom — não desalinha. */
.pb-info-section{
  font-family:var(--font-h);font-weight:800;
  font-size:clamp(9.5px, 1.15vw, 11.5px);
  color:var(--primary);letter-spacing:1.6px;
  text-transform:uppercase;line-height:1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  margin-bottom:2px;
}
.pb-info-title{
  font-family:var(--font-h);font-weight:800;
  /* [v186] reduzido: era clamp(17px,2.3vw,22px). Nomes longos
     ("Maior com 7ª Maior") estavam invadindo a zona-center. */
  font-size:clamp(14px, 1.9vw, 18px);
  color:var(--text);letter-spacing:-.2px;line-height:1.15;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.pb-sym{color:var(--primary);font-weight:900;margin-left:5px}
/* [v2.27] fórmula matemática (1-3-5, 1-♭3-5, T-T-S-T-T-T-S, etc.) abaixo
   do nome do exercício. Acordes têm fórmulas curtas; escalas/modos têm
   sequência T-T-S que pode ficar longa — overflow:hidden + ellipsis evita
   quebra de layout. */
.pb-info-formula{
  font-family:var(--font-h);font-weight:700;
  font-size:clamp(11px, 1.4vw, 14px);
  color:var(--muted);letter-spacing:1px;
  white-space:nowrap;line-height:1;
  margin-top:3px;
  max-width:240px;overflow:hidden;text-overflow:ellipsis;
}
/* [v245→v249] Sequência de Progressão: compacta, na paleta laranja do app.
   v249: era azul + 280px + setas largas; encolheu pra caber sem invadir
   a zona-center (stages/BPM/tempo). */
.pb-info-progression{
  color:var(--primary);font-weight:800;letter-spacing:.3px;
  font-size:clamp(10.5px, 1.25vw, 12.5px);
  max-width:170px;
  display:inline-flex;align-items:center;gap:2px;flex-wrap:nowrap;
  overflow:hidden;text-overflow:ellipsis;
}
/* [v246→v249] Highlight do acorde corrente em laranja brand. */
.pb-prog-step{
  display:inline-block;padding:1px 3px;border-radius:4px;
  transition:color .18s ease-out, background .18s ease-out, transform .18s ease-out, text-shadow .18s ease-out;
}
.pb-prog-sep{opacity:.4;padding:0 1px;font-weight:500;font-size:.85em}
.pb-prog-step.is-current{
  color:#fff;background:rgba(232,105,10,.30);
  transform:scale(1.18);
  text-shadow:0 0 10px rgba(232,105,10,.95),0 0 3px rgba(255,255,255,.55);
  box-shadow:0 0 0 1px rgba(232,105,10,.50) inset;
}
@media(prefers-reduced-motion: reduce){
  .pb-prog-step{transition:none}
  .pb-prog-step.is-current{transform:none}
}
@media(max-width:600px){
  .pb-info-progression{font-size:10.5px;max-width:140px}
}

/* [v2.28] grid 1fr 1fr: ambos os botões ocupam células de largura idêntica,
   garantindo que a divisão entre LENTO e CLICK fique exatamente no centro
   geométrico do .pb-stages (e portanto no centro do .pb-zone-center, que
   já está ancorado em 50% da viewport). LENTO right-aligned na sua célula,
   CLICK left-aligned na sua — o gap fica no centro. */
.pb-stages{display:grid;grid-template-columns:1fr 1fr;gap:6px;flex-shrink:0;align-items:center}
.pb-stages > .pb-stage:first-child{justify-self:end}
.pb-stages > .pb-stage:last-child{justify-self:start}
.pb-stage{
  border:1px solid var(--border2);background:var(--card2);color:var(--muted);
  padding:8px 16px;border-radius:999px;font-size:13px;font-weight:700;
  letter-spacing:.10em;text-transform:uppercase;cursor:pointer;
  font-family:var(--font-h);-webkit-tap-highlight-color:transparent;
  white-space:nowrap;
}
.pb-stage.mt-active{
  background:var(--primary);border-color:var(--primary);color:#fff;
  box-shadow:0 0 12px rgba(232,105,10,.45);
}
.pb-stage.mt-done{opacity:.5}

.pb-time-row{display:flex;align-items:center;gap:14px;flex-shrink:0}
.pb-time{
  font-family:var(--font-h);font-weight:900;font-size:clamp(18px,2.5vw,26px);
  color:var(--text);font-variant-numeric:tabular-nums;line-height:1;
}
/* Dots antigos viram invisíveis (mantidos só pra preservar API do MetronomeTimer) */
.pb-pulse{display:none}
.pb-beat{
  width:0;height:0;border-radius:50%;display:none;
}
.pb-beat.mt-beat-flash-strong{
  background:var(--primary);color:#fff;border-color:var(--primary);
  transform:scale(1.18);box-shadow:0 0 10px rgba(232,105,10,.7);
}
.pb-beat.mt-beat-flash-weak{
  background:var(--primary-dark);color:#fff;border-color:var(--primary-dark);
}
.pb-bpm{display:none;align-items:center;gap:5px}
.pb-bpm.mt-show{display:inline-flex}
.pb-bpm-fixed{display:none;font-family:var(--font-h);font-weight:800;font-size:13px;color:var(--primary)}
.pb-bpm-fixed.mt-show{display:inline-block}
.pb-bpm > [data-mt-bpm-val]{
  font-family:var(--font-h);font-weight:900;font-size:14px;color:var(--primary);
  min-width:30px;text-align:center;
}
.pb-bpm-btn{
  width:26px;height:26px;border-radius:50%;
  background:var(--card2);border:1px solid var(--border2);color:var(--text);
  font-family:var(--font-h);font-size:14px;font-weight:700;cursor:pointer;
  display:flex;align-items:center;justify-content:center;line-height:1;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation;
  user-select:none;-webkit-user-select:none;
}
.pb-bpm-btn:active{background:var(--primary);color:#fff;border-color:var(--primary)}
.pb-progress{
  width:min(280px, 28vw);height:3px;background:var(--card2);border-radius:3px;overflow:hidden;
}
.pb-progress-bar{height:100%;background:var(--primary);width:100%;transition:width .25s}

.pb-icon-btn{
  width:var(--pb-btn,38px);height:var(--pb-btn,38px);border-radius:50%;
  background:var(--card2);border:1px solid var(--border2);color:var(--text);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-h);font-size:var(--pb-btn-fz,14px);font-weight:700;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:background .12s, border-color .12s, transform .1s;
  flex-shrink:0;padding:0;
}
.pb-icon-btn:hover{background:var(--card);border-color:var(--primary)}
.pb-icon-btn:active{transform:scale(.94)}
.pb-icon-btn.demo-looping{background:rgba(74,222,128,.18);border-color:#4ADE80;color:#4ADE80;box-shadow:0 0 10px rgba(74,222,128,.30)}
.pb-icon-btn-primary{
  background:var(--primary);border-color:var(--primary);color:#fff;
  box-shadow:0 4px 14px rgba(232,105,10,.42);
  /* [v425] Sem tamanho próprio: herda o diâmetro único de .pb-icon-btn (--pb-btn).
     O play continua LARANJA pra se destacar, mas do MESMO tamanho dos outros 4. */
}
.pb-icon-btn-primary:hover{background:var(--primary-2);border-color:var(--primary-2);color:#fff}
/* [v361] FIM do minuto do Click: o play NÃO vira mais "→" (era uma nav tag laranja
   REDUNDANTE com o #practiceNext). O play fica '▶' (recomeça o Click) e o menu do Click
   continua on (stages + BPM +/-). A ÚNICA nav de avançar é o #practiceNext, agora um pouco
   MAIOR e LARANJA (com pulse) sinalizando "minuto concluído — avance quando quiser". */
.practice-page.mt-completed #practiceNext.nav-tag.is-pb-nav{
  width:48px; height:48px;
  background:var(--primary); border-color:var(--primary); color:#fff;
  box-shadow:0 4px 16px rgba(232,105,10,.5);
  animation: pbReady 1.4s ease-in-out infinite;
}
.practice-page.mt-completed #practiceNext.nav-tag.is-pb-nav svg{ width:16px; height:16px; }
@media (orientation:landscape) and (max-width:720px){
  .practice-page.mt-completed #practiceNext.nav-tag.is-pb-nav{ width:42px; height:42px; }
}

/* [human_sim] Pulse sutil no botão play quando timer está em IDLE */
.practice-page:not(.mt-completed) [data-pb-play]{
  animation: pbPlayIdle 2.4s ease-in-out infinite;
}
@keyframes pbPlayIdle{
  0%,80%,100%{box-shadow:0 4px 14px rgba(232,105,10,.42)}
  90%       {box-shadow:0 4px 14px rgba(232,105,10,.42), 0 0 0 8px rgba(232,105,10,0)}
}
.practice-page.mt-running [data-pb-play],
.practice-page.mt-paused [data-pb-play]{animation:none}
@keyframes pbReady{
  0%,100%{box-shadow:0 0 0 0 rgba(255,123,26,.55)}
  50%    {box-shadow:0 0 0 10px rgba(255,123,26,0)}
}

/* [v2.24] .practice-nav e .pn-btn REMOVIDOS — viraram nav-tags circulares
 * dentro da .practice-bar via .nav-tag.is-round.is-pb-nav (modifier).
 * Resultado: zero fileira intermediária entre barra e piano. */
.practice-bar .nav-tag.is-pb-nav{
  flex-shrink:0;
  /* [v425] mesmo diâmetro dos outros botões redondos do menu */
  width:var(--pb-btn,38px);height:var(--pb-btn,38px);
  /* sem backdrop-filter dentro da bar (já tem gradiente próprio) */
  backdrop-filter:none; -webkit-backdrop-filter:none;
}
.practice-bar .nav-tag.is-pb-nav.is-hidden{
  visibility:hidden; pointer-events:none;
}
/* [v2.27] Voltar (#practicePrev) sai do fluxo da practice-bar e fica
   posicionado abaixo do .menu-btn, alinhado verticalmente. Avançar
   (#practiceNext) permanece inline na zone-right. */
.practice-page #practicePrev.nav-tag.is-pb-nav{
  position: fixed;
  /* [v81] alinhado com menu-btn novo (38px) — top = 12+safe + 38 + 8 */
  top: calc(12px + env(safe-area-inset-top) + 38px + 8px);
  left: calc(12px + max(var(--sal), env(safe-area-inset-left, 0px)));
  width: 38px; height: 38px;
  z-index: 220;
  background: rgba(20,20,20,.92);
  border: 1px solid var(--border2);
}
.practice-page #practicePrev.nav-tag.is-pb-nav svg{ width:13px; height:13px; }
@media (orientation:landscape) and (max-width:720px){
  /* [v425] nav herda o diâmetro único (--pb-btn) — não força mais 32px (era menor que os demais) */
  .practice-bar .nav-tag.is-pb-nav svg{ width:13px; height:13px; }
  .practice-page #practicePrev.nav-tag.is-pb-nav{
    width:34px; height:34px;
    top: calc(12px + env(safe-area-inset-top) + 34px + 6px);
  }
}

.practice-page .kbd-wrap{
  flex:1 1 auto;
  min-height:0;
  margin:0!important;
  /* [v2.26] padding ZERO horizontal — piano cola nas bordas laterais da tela. */
  padding:0!important;
  /* [v2.26] align-items:stretch + width/height:100% no SVG → piano preenche
     100% do espaço disponível abaixo da practice-bar. preserveAspectRatio="none"
     no SVG aceita a leve distorção pra eliminar gap. */
  display:flex;align-items:stretch;justify-content:stretch;
  max-width:none;
  overflow:hidden;
  /* [v2.28] permite que .kbd-labels (HTML overlay das cifras) usem
     position:absolute ancorado no kbd-wrap. */
  position:relative;
}
.practice-page .kbd-svg{
  width:100%;height:100%;max-width:none;max-height:none;
  display:block;
}
/* Quando o user está em prática, esconde os nav-tags FIXOS globais
   (eles foram movidos pra DENTRO da .practice-bar via .is-pb-nav) */
body.in-practice .nav-tags{display:none!important}

/* Em portrait, o overlay de rotação cobre tudo (já implementado).
   A página só "vive" em landscape. */
@media (orientation:landscape) and (max-width:900px){
  .practice-bar{
    /* [v2.27] Layout flex preservado; altura aumentada em landscape mobile.
       padding-left ≥ 70 garante que pb-tom não fique atrás do menu-btn/Voltar. */
    gap:12px;
    /* [v644] +safe-area-inset-left no padding-left: tira o pb-tom de trás do ☰ no notch */
    padding: 8px max(12px, calc(max(var(--sar), env(safe-area-inset-right, 0px)) + 8px))
             8px calc(max(var(--sal), env(safe-area-inset-left, 0px)) + 60px);   /* [22/07] mesma conta do base, com a pilha de 34px do ≤720 */
    min-height:clamp(120px, 35vh, 180px);max-height:none;
  }
  /* [v637] tom/botões ALINHADOS ao visual base (>900, o "perfeito"): o ≤900 inchava os botões (44→48) e
     encolhia o tom (37→28). Revertido: tom volta a escala 4vw (grande) e botões ao diâmetro base 44px. */
  .pb-tom{font-size:clamp(26px, 4vw, 34px); padding:6px 13px}
  .pb-info-title{font-size:clamp(13px, 1.7vw, 16px)}
  .pb-time{font-size:clamp(20px, 3vw, 28px)}
  .pb-time-row{gap:12px}
  .practice-bar{--pb-btn:40px;--pb-btn-fz:15px} /* [v645] 44→40 (alinhado ao base) — botão um pouco menor */
  .pb-stage{padding:7px 14px;font-size:12px}
  .pb-bpm-btn{width:30px;height:30px;font-size:15px}
  .pb-bpm > [data-mt-bpm-val]{font-size:16px;min-width:34px}
  .pb-meter{width:36px;height:36px;font-size:16px}
  .pb-progress{width:min(280px, 28vw);height:4px}
  .pb-zone-right{gap:10px}
  .pb-zone-left{gap:12px; justify-content:flex-start}
  /* [v691/v763 → 22/07] os dois re-asserts de "centro puro" deste breakpoint saíram junto
     com o posicionamento absoluto: em fluxo o centro já é o centro do vão, em toda largura. */
  /* teto da fórmula abaixo do base 240: garante que nem a mais longa alcance a zona-center */
  .pb-info-formula{max-width:clamp(140px, 22vw, 200px)}
}
/* [v2.23] Landscape compacto (~660×380) — proporção reduzida mas ainda generosa. */
@media (orientation:landscape) and (max-width:720px){
  .practice-bar{gap:10px;padding:6px 12px;min-height:clamp(76px, 20vh, 100px)}
  .pb-tom{font-size:18px;padding:3px 8px}
  .pb-info-title{font-size:12px}
  .practice-bar{--pb-btn:40px;--pb-btn-fz:15px} /* [v645] 44→40 diâmetro único dos 5 botões (landscape ≤720) */
  .pb-stage{padding:6px 12px;font-size:11.5px}
  .pb-meter{width:32px;height:32px;font-size:14px}
  .pb-progress{width:min(220px, 22vw)}
  .pb-zone-right{gap:8px}
  /* [v691] landscape compacto: teto da fórmula ainda menor (viewport ~660×380 tem menos folga) */
  .pb-info-formula{max-width:clamp(110px, 20vw, 160px)}
}

/* ============ HOWTO PAGE — slides paginados premium ============ */
/* "Pular tutorial" — pílula vermelha FIXA no topo direito (não scrolla com a página).
   [v2.1] position:absolute → fixed. Top idêntico ao .menu-btn; right simétrico ao left
   do menu-btn. z-index ≥ 220 pra ficar acima do .menu-btn. */
.howto-skip{
  position:fixed;
  top:calc(12px + env(safe-area-inset-top));
  right:calc(12px + max(var(--sar), env(safe-area-inset-right, 0px)));
  display:none;align-items:center;gap:6px;
  padding:7px 14px;border-radius:999px;
  background:rgba(220,38,38,.12);
  border:1px solid rgba(220,38,38,.50);
  color:#DC2626;
  font-family:var(--font-h);font-weight:800;font-size:10.5px;letter-spacing:.18em;
  text-transform:uppercase;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  transition:background .15s, color .15s, transform .12s;
  z-index:230;
}
.howto-skip:hover{background:#DC2626;color:#FFFFFF;border-color:#DC2626}
.howto-skip:active{transform:scale(.97)}
.howto-page[data-howto-cur-idx]:not([data-howto-cur-idx="0"]) .howto-skip{display:inline-flex}
/* App-like, mas com scroll vertical PERMITIDO quando o conteúdo passa do viewport */
.howto-page{
  display:flex;flex-direction:column;align-items:center;
  height:100vh;
  height:100dvh;
  padding:0 clamp(20px,5vw,40px);
  position:relative;
  overflow:hidden;
  background:
    radial-gradient(ellipse 60% 40% at 80% 0%,  rgba(232,105,10,.12) 0%, transparent 60%),
    radial-gradient(ellipse 50% 40% at 20% 100%, rgba(232,105,10,.08) 0%, transparent 60%),
    var(--bg);
}

/* Slide individual — não fixa altura; deixa o conteúdo definir tamanho.
   Adiciona padding generoso pra header/dots não cobrirem nada. */
.howto-slide{
  display:none;flex-direction:column;align-items:center;
  width:100%;max-width:680px;
  height:100vh;
  height:100dvh;
  padding:calc(64px + env(safe-area-inset-top)) 0 calc(52px + env(safe-area-inset-bottom));
  text-align:center;
  justify-content:flex-start;
  box-sizing:border-box;
}
.howto-slide.active{display:flex;animation: howtoSlideIn .55s cubic-bezier(.2,.8,.2,1)}
/* Dots de progresso no rodapé */
.howto-dots{
  position:fixed;left:0;right:0;
  bottom:calc(14px + env(safe-area-inset-bottom));
  display:flex;justify-content:center;align-items:center;gap:8px;
  z-index:151;pointer-events:none;
}
.howto-dot{
  width:8px;height:8px;border-radius:50%;
  background:rgba(232,105,10,.20);
  transition:background .25s, transform .25s, width .25s;
}
.howto-dot.active{
  background:var(--primary);width:24px;border-radius:6px;
  box-shadow:0 0 8px rgba(232,105,10,.6);
}
@keyframes howtoSlideIn{
  0%  {opacity:0;transform:translateY(18px) scale(.985)}
  100%{opacity:1;transform:translateY(0)    scale(1)}
}
/* [v2.1] Subtítulos repetitivos (.howto-step) e ícones circulares (.howto-icon)
   removidos — o título principal de cada slide já carrega a frase de impacto. */

/* Título unificado — MESMO tamanho em todas as páginas
   [v2.8] opacity:0 inicial removido — se a animação não rodar (ex: navegador
   pausando timelines em display:none), o elemento agora permanece visível
   por default em vez de ficar invisível pra sempre. A animação é apenas
   uma entrada decorativa via from{} no keyframe. */
.howto-title{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(28px, 6.5vw, 46px);line-height:1.10;letter-spacing:-1.2px;
  color:var(--text);margin-bottom:18px;
  animation: howtoFadeUp .6s cubic-bezier(.2,.8,.2,1) .25s backwards;
  border-left:none;padding-left:0;
  flex-shrink:0;
}
.howto-title span{color:var(--primary)}
.howto-title span{color:var(--primary)}

.howto-lead{
  font-family:var(--font-b);
  font-size:clamp(15px, 2.7vw, 17px);line-height:1.6;
  color:var(--muted);max-width:540px;
  margin:auto auto;
  animation: howtoFadeUp .55s cubic-bezier(.2,.8,.2,1) .35s backwards;
  flex-shrink:0;
  padding:8px 0;
}
/* No slide de boas vindas, o lead fica BEM no meio entre o título e o body */
.howto-slide.is-welcome .howto-lead{
  font-size:clamp(16px, 3vw, 19px);
  line-height:1.65;
  margin:auto auto;
  padding:24px 0;
}
.howto-lead strong{color:var(--text)}

/* Conteúdo principal de cada slide — flex:1 pra ocupar o espaço disponível
   [v2.8] opacity:0 inicial removido (mesmo motivo de .howto-title/.howto-lead). */
.howto-content{
  width:100%;max-width:580px;
  animation: howtoFadeUp .60s cubic-bezier(.2,.8,.2,1) .45s backwards;
  flex:1 1 auto;
  display:flex;flex-direction:column;justify-content:center;gap:10px;
  min-height:0;
}
/* [v2.8] from explícito pra `backwards` aplicar o estado inicial só durante
   o delay da animação. Sem from, `backwards` não tem o que aplicar. */
@keyframes howtoFadeUp{
  from{opacity:0;transform:translateY(10px)}
  to{opacity:1;transform:translateY(0)}
}

/* Stats row do slide 1 */
.howto-stats-row{
  display:flex;justify-content:center;gap:clamp(18px, 5vw, 48px);margin-top:8px;
}
.howto-stat .num{
  font-family:var(--font-h);font-weight:900;font-size:clamp(36px, 7vw, 56px);
  color:var(--primary);line-height:1;letter-spacing:-1.5px;
}
.howto-stat .lbl{
  font-family:var(--font-h);font-weight:700;font-size:clamp(10px,1.6vw,12px);
  letter-spacing:.20em;color:var(--text);text-transform:uppercase;margin-top:6px;
}

/* [v89/v92] Slide "Caminho — A ordem inteligente" (movido de tonalitiesPage pro howto) */
.howto-tons-list{
  display:grid;
  /* [v92] 4 colunas fixas (3 fileiras de 4 = 12 tons) ocupando 100% da largura */
  grid-template-columns:repeat(4,1fr);
  gap:clamp(8px, 1.5vw, 14px);
  width:100%;max-width:none;margin:0 auto;
}
.howto-tn-card{
  background:var(--card2);border:1px solid var(--border2);
  border-left:4px solid var(--accent,var(--primary));
  border-radius:12px;
  /* [v94] cards ainda maiores — preenchem mais a tela verticalmente */
  padding:16px 14px;min-height:clamp(110px, 16vh, 150px);
  display:flex;flex-direction:column;justify-content:space-between;gap:8px;
  text-align:left;
  box-shadow:0 4px 12px rgba(0,0,0,.25);
  /* [v133] Cards são botões — tocam Royal Grand pra respectiva nota */
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  user-select:none;
  transition: transform .12s ease, border-color .22s, box-shadow .22s, filter .18s;
}
.howto-tn-card:hover{
  border-color: var(--accent, var(--primary));
  box-shadow:
    0 8px 24px rgba(0,0,0,.35),
    0 0 0 1px var(--accent, var(--primary)) inset;
  transform: translateY(-2px);
}
.howto-tn-card:active{ transform: translateY(0) scale(.98); }
.howto-tn-card.is-firing{
  border-color: var(--accent, var(--primary));
  box-shadow:
    0 14px 36px rgba(232,105,10,.36),
    0 0 0 2px var(--accent, var(--primary)) inset,
    0 0 28px rgba(255,170,60,.28);
  filter: brightness(1.18);
}
.howto-tn-card:focus-visible{
  outline: 2px solid var(--accent, var(--primary));
  outline-offset: 2px;
}
.howto-tn-name{
  font-family:var(--font-h);font-weight:900;
  /* [v92] aumentado: era 18px → agora clamp 22-32px */
  font-size:clamp(22px, 4vw, 32px);
  color:var(--text);letter-spacing:-.6px;line-height:1;
}
.howto-tn-info{display:flex;justify-content:space-between;align-items:baseline;gap:6px}
.howto-tn-pt{font-family:var(--font-h);font-weight:600;font-size:clamp(12px, 1.6vw, 14px);color:var(--muted);letter-spacing:.03em}
.howto-tn-acc{font-family:var(--font-h);font-weight:700;font-size:clamp(10px, 1.3vw, 12px);color:var(--accent,var(--primary));letter-spacing:.05em}
.howto-tn-bar{display:flex;gap:3px;margin-top:auto}
.howto-tn-seg{flex:1;height:4px;border-radius:2px;background:rgba(255,255,255,.06)}
.howto-tn-seg.filled{background:var(--accent,var(--primary))}

/* [v98] Legenda explicando cor + pontos = dificuldade.
   Tamanho aumentado: padding, gap e fontes maiores pra texto bem visível. */
.howto-tons-legend{
  display:flex;align-items:center;justify-content:center;
  gap:clamp(14px, 2vw, 20px);
  margin: clamp(4px, .8vh, 8px) auto 0;
  padding:clamp(14px, 2vh, 20px) clamp(18px, 2.5vw, 24px);
  border:1.5px solid rgba(232,105,10,.30);
  background:
    radial-gradient(ellipse 80% 120% at 0% 50%, rgba(232,105,10,.12) 0%, transparent 65%),
    var(--card2);
  border-radius:14px;
  max-width:680px;
  box-shadow:0 6px 20px rgba(0,0,0,.30);
}
.howto-tons-legend-bar{
  display:inline-flex;gap:3px;flex-shrink:0;
}
.howto-tons-legend-bar > span{
  width:clamp(16px, 2vw, 20px);height:clamp(16px, 2vw, 20px);
  border-radius:50%;display:inline-block;
}
.howto-tons-legend-text{
  font-family:var(--font-b);
  font-size:clamp(13.5px, 2vw, 16px);
  line-height:1.45;color:var(--text);text-align:left;
}
.howto-tons-legend-text strong{color:var(--primary);font-weight:800}

@media (max-width:520px){
  .howto-tons-list{grid-template-columns:repeat(4,1fr);gap:6px}
  /* [v94] cards mobile também maiores */
  .howto-tn-card{padding:12px 10px;min-height:100px;border-left-width:3px}
  .howto-tn-name{font-size:20px}
  .howto-tn-pt{font-size:11px}
  .howto-tn-acc{font-size:9.5px}
  /* [v98] mobile: tamanho da legenda também aumentado */
  .howto-tons-legend{gap:12px;padding:12px 14px;margin-top:5px}
  .howto-tons-legend-bar{gap:2px}
  .howto-tons-legend-bar > span{width:14px;height:14px}
  .howto-tons-legend-text{font-size:13px;line-height:1.4}
}

/* Tiles (slide nav e tocando) */
.howto-tiles{display:grid;gap:12px;grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));text-align:left}
.howto-tiles-3{grid-template-columns:repeat(auto-fit, minmax(150px, 1fr))}
/* Mobile: tiles em pílulas horizontais (1 linha cada) — evita assimetria do grid 2x2 */
@media (max-width:520px){
  .howto-tiles{grid-template-columns:1fr;gap:8px}
  .howto-tiles-3{grid-template-columns:1fr}
  .howto-tile{flex-direction:row;align-items:center;gap:14px;padding:12px 14px}
  .howto-tile .ic{margin-bottom:0}
  .howto-tile-text{flex:1;display:flex;flex-direction:column;gap:2px}
}
.howto-tile{
  background:var(--card2);
  /* [v203] borda base mais grossa (1px → 1.5px) com tom laranja sutil */
  border:1.5px solid rgba(232,105,10,.22);
  border-radius:12px;
  padding:14px 14px 16px;display:flex;flex-direction:column;gap:6px;
  transition:border-color .15s, transform .15s, box-shadow .15s, background .15s;
}
/* [v203] Hover/active: laranja mais presente — borda mais grossa, glow externo
   e leve tinta laranja no fundo. Sem exagerar. */
.howto-tile:hover{
  border-color:rgba(232,105,10,.7);
  border-width:2px;
  background:linear-gradient(180deg, rgba(232,105,10,.05), transparent 60%), var(--card2);
  box-shadow:0 6px 22px rgba(232,105,10,.22), 0 0 0 1px rgba(232,105,10,.18) inset;
  transform:translateY(-2px);
}
.howto-tile:active{
  border-color:rgba(232,105,10,.85);
  background:linear-gradient(180deg, rgba(232,105,10,.10), rgba(232,105,10,.02) 70%), var(--card2);
  box-shadow:0 4px 14px rgba(232,105,10,.30), 0 0 0 1.5px rgba(232,105,10,.32) inset;
}
.howto-tile .ic{
  font-family:var(--font-h);font-weight:900;
  font-size:22px;color:var(--primary);
  display:inline-flex;align-items:center;justify-content:center;
  width:38px;height:38px;border-radius:10px;
  /* [v203] ícone laranja mais saturado e com glow interno */
  background:rgba(232,105,10,.20);
  border:1.5px solid rgba(232,105,10,.55);
  box-shadow:0 0 14px rgba(232,105,10,.20) inset, 0 2px 8px rgba(232,105,10,.18);
  text-shadow:0 0 8px rgba(232,105,10,.45);
  margin-bottom:4px;
}
.howto-tile strong{color:var(--text);font-family:var(--font-h);font-weight:800;font-size:13.5px;letter-spacing:.04em}
.howto-tile span{color:var(--muted);font-size:12.5px;line-height:1.45}
.howto-tile code{font-family:'SF Mono', monospace;background:rgba(232,105,10,.12);color:var(--primary);padding:1px 5px;border-radius:4px;font-size:11.5px}
.howto-tile-emph{
  border-color:rgba(232,105,10,.55);
  background:linear-gradient(180deg, rgba(232,105,10,.10) 0%, rgba(232,105,10,.04) 100%);
  box-shadow:0 6px 20px rgba(232,105,10,.18);
}
.howto-tile-emph .ic{
  background:rgba(232,105,10,.22);border-color:rgba(232,105,10,.55);
  color:var(--primary-2);
}
/* Tile inteiramente clicável */
.howto-tile-btn{
  appearance:none;text-align:left;
  cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;
  width:100%;
}
.howto-tile-btn:active{ opacity:.75;transform:scale(.98) !important; }
/* Texto sublinhado puro dentro das tiles-link */
.tile-link{ text-decoration:underline;text-underline-offset:3px; }
/* Tile tema verde — promo do modo "Toque no seu Teclado" */
.howto-tile-green{
  border-color:rgba(34,197,94,.45);
  background:linear-gradient(180deg, rgba(34,197,94,.10) 0%, rgba(34,197,94,.04) 100%);
  box-shadow:0 6px 20px rgba(34,197,94,.15);
}
.howto-tile-green .ic{
  background:rgba(34,197,94,.15);border-color:rgba(34,197,94,.45);color:#4ADE80;
}
.howto-tile-green strong{ color:#4ADE80; }
@media (hover:hover) and (pointer:fine){
  .howto-tile-green:hover{ border-color:rgba(34,197,94,.70); }
}

/* Protocolo (slide 3) — duas etapas como cards */
.howto-protocol{display:flex;flex-direction:column;gap:14px;text-align:left}
.howto-stage{
  display:flex;gap:16px;align-items:flex-start;
  background:var(--card2);border:1px solid var(--border2);border-left:3px solid var(--accent, var(--primary));
  border-radius:12px;padding:16px 18px;
}
.howto-stage .stage-num{
  font-family:var(--font-h);font-weight:900;font-size:30px;line-height:1;
  color:var(--accent, var(--primary));
  width:48px;flex-shrink:0;
}
.howto-stage .stage-info{display:flex;flex-direction:column;gap:4px;flex:1}
.howto-stage strong{color:var(--text);font-size:15px;font-family:var(--font-h);font-weight:800;letter-spacing:.02em}
.howto-stage em{color:var(--accent, var(--primary));font-style:normal;font-weight:700;font-size:12px;letter-spacing:.10em;text-transform:uppercase}
.howto-stage span{color:var(--muted);font-size:13px;line-height:1.5;margin-top:2px}

/* Pillars (slide "Por que funciona" — Três Molas)
   [v2.11] Apertado pra caber em viewport mobile (~380×700) sem scroll: card +
   3 dele + dots no rodapé tudo visível. Cortes feitos em padding interno, gap
   entre cards, gap interno do card, número circular menor, e fontes ligeiramente
   reduzidas. Conteúdo textual preservado. */
.howto-pillars{display:flex;flex-direction:column;gap:8px;text-align:left}
.pillar{
  background:var(--card2);border:1px solid var(--border2);border-radius:12px;
  padding:10px 14px;display:flex;flex-direction:column;gap:5px;
}
.pillar-head{display:flex;align-items:center;gap:10px}
.pillar .pillar-num{
  flex-shrink:0;
  font-family:var(--font-h);font-weight:900;font-size:15px;
  color:var(--primary);line-height:1;
  display:flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:50%;
  border:1.5px solid rgba(232,105,10,.45);
  background:rgba(232,105,10,.10);
}
.pillar strong{color:var(--text);font-size:13.5px;font-family:var(--font-h);font-weight:800;letter-spacing:.02em;line-height:1.25}
.pillar > span{color:var(--muted);font-size:12.5px;line-height:1.45;display:block}

/* Rules (slide 6) */
.howto-rules{display:flex;flex-direction:column;gap:8px;text-align:left;margin-bottom:20px}
.howto-rules .rule{
  display:flex;gap:12px;align-items:flex-start;
  font-size:14px;line-height:1.5;color:var(--text);
  padding:10px 0;border-bottom:1px solid var(--border);
}
.howto-rules .rule:last-child{border-bottom:none}
.howto-rules .rule-mark{
  flex-shrink:0;width:22px;height:22px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--primary);color:#0A0A0A;font-weight:900;font-size:13px;
}
.howto-final-tip{
  background:linear-gradient(135deg, rgba(232,105,10,.12) 0%, rgba(232,105,10,.04) 100%);
  border:1px solid rgba(232,105,10,.40);
  border-radius:12px;padding:14px 18px;text-align:left;
  display:flex;flex-direction:column;gap:6px;
}
.howto-final-tip strong{color:var(--primary);font-family:var(--font-h);font-weight:800;font-size:11.5px;letter-spacing:.18em;text-transform:uppercase}
.howto-final-tip span{color:var(--text);font-size:13.5px;line-height:1.55}

/* Hint após tiles */
.howto-hint{
  margin-top:16px;font-size:13px;line-height:1.5;color:var(--muted);
  background:rgba(232,105,10,.08);border:1px solid rgba(232,105,10,.20);
  border-radius:10px;padding:10px 14px;text-align:left;
}
.howto-hint strong{color:var(--primary)}

/* [v2.1] CTAs internos do tutorial (.howto-arrow / .howto-finish-btn) removidos
   — eram redundantes com o #nextBtn da .nav-tags fixa. */

/* Mobile ajustes */
@media (max-width:480px){
  .howto-page{padding:0 18px}
  .howto-progress{top:calc(14px + env(safe-area-inset-top))}
  .howto-slide{padding-top:48px}
  .howto-tile .ic{width:34px;height:34px;font-size:18px}
  .howto-stage{padding:14px}
  .howto-stage .stage-num{width:38px;font-size:24px}
}

/* [remoção timbres] CSS do slide "Experimente" (3 timbres / .howto-tcircle*) removido. */
/* Sugestão "Quer evoluir mais rápido?" no slide 3 */
.howto-extend{
  display:flex;gap:14px;align-items:center;
  background:linear-gradient(135deg, rgba(232,105,10,.10) 0%, rgba(232,105,10,.03) 100%);
  border:1px solid rgba(232,105,10,.32);border-radius:12px;
  padding:12px 16px;margin-top:14px;text-align:left;
}
.howto-extend .ext-icon{
  flex-shrink:0;width:36px;height:36px;border-radius:50%;
  background:rgba(232,105,10,.15);border:1.5px solid rgba(232,105,10,.50);
  color:var(--primary);font-family:var(--font-h);font-weight:900;font-size:22px;
  display:flex;align-items:center;justify-content:center;line-height:1;
}
.howto-extend .ext-info{display:flex;flex-direction:column;gap:3px;flex:1}
.howto-extend strong{color:var(--primary);font-family:var(--font-h);font-weight:800;font-size:11.5px;letter-spacing:.18em;text-transform:uppercase}
.howto-extend span{color:var(--text);font-size:13px;line-height:1.55}
.howto-extend em{color:var(--primary);font-style:normal;font-weight:700}
/* [v2.22] Caixa "Dica extra" do slide Protocolo — espelha .howto-extend
   visualmente, mas em âmbar/dourado (#FCD34D) pra distinguir como dica
   secundária ("se quiser evoluir mais rápido") em contraste com o
   .howto-extend que é o reforço da regra principal. */
.howto-tip{
  display:flex;gap:14px;align-items:center;
  background:linear-gradient(135deg, rgba(252,211,77,.10) 0%, rgba(252,211,77,.03) 100%);
  border:1px solid rgba(252,211,77,.32);border-radius:12px;
  padding:12px 16px;margin-top:10px;text-align:left;
}
.howto-tip .tip-icon{
  flex-shrink:0;width:36px;height:36px;border-radius:50%;
  background:rgba(252,211,77,.15);border:1.5px solid rgba(252,211,77,.50);
  color:#FCD34D;font-size:20px;
  display:flex;align-items:center;justify-content:center;line-height:1;
}
.howto-tip .tip-info{display:flex;flex-direction:column;gap:3px;flex:1}
.howto-tip strong{color:#FCD34D;font-family:var(--font-h);font-weight:800;font-size:11.5px;letter-spacing:.18em;text-transform:uppercase}
.howto-tip span{color:var(--text);font-size:13px;line-height:1.55}
.howto-tip em{color:#FCD34D;font-style:normal;font-weight:700}
/* [v2.10] Reescrita: agora é um bloco de 3 parágrafos curtos com tipografia
   maior, cor clara e sem o background-box agressivo. Preenche o espaço
   livre entre os 3 ícones e o CTA. */
.howto-task-instruction{
  background:transparent;border:none;border-radius:0;padding:0;
  margin: 0 auto 18px;
  display:flex;flex-direction:column;gap:10px;
  max-width:560px;text-align:center;
}
.howto-task-instruction p{
  font-family:var(--font-h);font-weight:500;
  font-size:clamp(16px, 3vw, 24px);
  line-height:1.45;letter-spacing:-.1px;
  color:var(--text);
  margin:0;
}
.howto-task-instruction strong{
  color:var(--primary);font-weight:800;
}
.hf-pill{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:36px;padding:3px 9px;margin:0 2px;border-radius:999px;
  background:rgba(232,105,10,.12);border:1px solid rgba(232,105,10,.45);
  color:var(--primary);font-family:var(--font-h);font-weight:800;font-size:11.5px;letter-spacing:.10em;
  transition:all .25s ease;
}
.hf-pill.hf-done{
  background:linear-gradient(135deg, var(--primary) 0%, var(--primary-2) 100%);
  color:#FFF;border-color:var(--primary);
  box-shadow:0 0 14px rgba(232,105,10,.55);
  transform:scale(1.05);
}
/* [v2.10] CTA reforçado: padding maior, borda mais firme, sombra sutil pra
   destacar como única ação visível na parte inferior do slide. */
.howto-fs-cta{
  display:inline-flex;align-items:center;gap:10px;
  padding:16px 28px;border-radius:999px;cursor:pointer;
  background:rgba(232,105,10,.14);border:1.5px solid var(--primary);
  color:var(--primary);
  font-family:var(--font-h);font-weight:800;font-size:13.5px;letter-spacing:.16em;
  text-transform:uppercase;
  -webkit-tap-highlight-color:transparent;
  transition:background .15s, color .15s, transform .12s, box-shadow .15s;
  margin:8px auto 0;
  box-shadow:0 6px 18px rgba(232,105,10,.20);
}
.howto-fs-cta:hover{
  background:var(--primary);color:#FFF;
  box-shadow:0 8px 24px rgba(232,105,10,.40);
}
.howto-fs-cta:active{transform:translateY(1px)}
.howto-fs-cta svg{width:14px;height:14px;flex-shrink:0;display:block}

/* [v2.1] .howto-next.is-locked removido junto com .howto-arrow / .howto-finish-btn. */

/* === Walkthrough — destaque animado em alvos da prática === */
.wt-target-active{
  position:relative;
  outline:3px solid var(--primary)!important;
  outline-offset:6px;
  border-radius:14px;
  animation:wtPulse 1.6s ease-in-out infinite;
  z-index:430;
}
@keyframes wtPulse{
  0%,100%{box-shadow:0 0 0 0 rgba(232,105,10,.65), 0 0 18px rgba(232,105,10,.55)}
  50%   {box-shadow:0 0 0 10px rgba(232,105,10,0),   0 0 28px rgba(232,105,10,.85)}
}
/* [v2.26] Durante o walkthrough (body.howto-fs-on), a interface fica
   VISUALMENTE IDÊNTICA à prática real — todos os elementos preservados
   (hamburger, navtags, etc). Apenas pointer-events é bloqueado pra
   prevenir interação acidental. O alvo destacado recupera pointer-events. */
body.howto-fs-on .practice-page .practice-bar{
  pointer-events:none;
}
body.howto-fs-on .practice-page .practice-bar .wt-target-active{
  pointer-events:auto!important;cursor:pointer;
}
/* [v230] Durante o walkthrough qualquer alvo é clicável — usuário pode
   voltar pra um passo anterior ou pular pra um seguinte tocando o ícone
   correspondente. O ALVO ATIVO continua tendo o pulse via .wt-target-active. */
body.howto-fs-on .practice-page .practice-bar [data-wt-target]{
  pointer-events:auto!important;cursor:pointer;
}
/* [v234] Nav-tags (prev/next) ficam clicáveis durante o walkthrough mas
   navegam os POPUPS do tutorial — não os exercícios. O guard global no
   document intercepta os cliques (vide setupWalkthroughClickGuard). */
body.howto-fs-on .practice-page #practicePrev,
body.howto-fs-on .practice-page .practice-bar #practiceNext{
  pointer-events:auto!important;cursor:pointer;
}
body.howto-fs-on .practice-page .kbd-wrap{
  pointer-events:none;
}
body.howto-fs-on .menu-btn{
  pointer-events:none;
}
.wt-target-active{pointer-events:auto!important;cursor:pointer}
/* [v476] Beckon/alvo REDONDO em botões de ícone. .wt-target-active força border-radius:14px (ótimo p/
   itens retangulares: opções do menu, card de tom), mas em botões redondos (.pb-icon-btn → olho,
   inversões) isso deixava o anel do .tt-beckon (border-radius:inherit) e o outline QUADRADOS. Mantém 50%. */
.pb-icon-btn.wt-target-active{ border-radius:50%!important; }
/* [v476] Walkthrough: mantém o botão de inversões VISÍVEL na etapa olho. Ao escolher a 1ª inversão,
   switchChordInversion zera o stageIdx → render() tira o .stage-click (que mostra o botão). tt-keep-inv
   força a visibilidade — exceto no guiado (tocar), onde o botão deve sumir (body.guided-active). */
body:not(.guided-active) .practice-page.tt-keep-inv .pb-icon-btn.pb-inv-btn{ display:inline-flex!important; }
/* [2b] Gating do walkthrough da prática REAL: enquanto body.tt-walk, a barra real, o ☰ e os menus
   (#inversion-menu/#tom-menu) ficam INERTES; só o alvo liberado (.wt-target-active, via ttWalk.live)
   é clicável. O teclado é gateado à parte (ttWalk.inertKbd, inline) pois nas etapas de TOCAR ele
   precisa responder. Garante que durante a locução nada além do alvo cintilante reage ao toque. */
body.tt-walk .practice-bar button,
body.tt-walk .practice-bar [role="button"],
body.tt-walk .practice-bar .nav-tag,
body.tt-walk .menu-btn,
body.tt-walk #inversion-menu .iv-opt,
body.tt-walk #tom-menu .t-opt{ pointer-events:none; }
body.tt-walk .wt-target-active{ pointer-events:auto!important; cursor:pointer; }
/* [v485] LOCUÇÃO INTERRUMPÍVEL = NÃO. Enquanto um clipe do walkthrough está TOCANDO (body.tt-narrating,
   setado em ttWalk.playClip e tirado em onClipEnded), o ALVO cintilante (olho/tom/inversão) fica INERTE —
   tocar nele NÃO corta a narração. Quando a fala termina, o alvo (que segue piscando) volta a clicar.
   Vem DEPOIS da regra .tt-walk .wt-target-active (mesma especificidade) → vence por ordem de fonte.
   O piano NÃO é .wt-target-active → segue tocável na etapa de TOCAR (mic/guiado durante a fala). */
body.tt-narrating .wt-target-active{ pointer-events:none!important; cursor:default!important; }
/* [2b] No tutorial o metrônomo NÃO roda: a pb-time ("X/Y"), o meter (o "(1)") e o ♩fixo não fazem
   sentido (o progresso do guiado vive no AVISO "Repetição X de N"). Some com eles — sobram os stages
   (Guiado/Click) e o −BPM+ do menu Click. */
html.tt-tutorial .practice-bar .pb-time,
html.tt-tutorial .practice-bar .pb-meter,
html.tt-tutorial .practice-bar .pb-bpm-fixed,
html.tt-tutorial .practice-bar .pb-progress{ display:none!important; }
/* [v474] Tutorial do Click: com pb-time/meter/♩fixo escondidos acima, o controle −BPM+ (pb-bpm) É o
   "display central" da etapa. Pedido: descer um pouco + aumentar, centralizando no vão entre as abas
   (Guiado/Click) e a base da barra (divisão barra↔piano). Escopo SÓ no tutorial → prática real intacta.
   translateY desce só o −BPM+ (transform não altera o fluxo, então as abas NÃO sobem). Ajuste fino = só
   mudar o translateY (mais/menos) e os tamanhos abaixo. */
html.tt-tutorial .practice-page.stage-click .pb-time-row{
  transform: translateY(10px);   /* centraliza o −BPM+ no vão abas↔base (medido: centro do vão; tunável) */
}
html.tt-tutorial .practice-page.stage-click .pb-bpm{ gap:10px; }
html.tt-tutorial .practice-page.stage-click .pb-bpm > [data-mt-bpm-val]{
  font-size:22px; min-width:46px;
}
html.tt-tutorial .practice-page.stage-click .pb-bpm-btn{
  width:38px; height:38px; font-size:20px;
}
/* [2b] zoom momentâneo das cifras do teclado quando a locução diz "troca as cifras…" (preserva o
   transform base de cada label: branca translateX(-50%), preta translate(-50%,-160%)). */
.kbd-label-w.wt-zoom{ animation:wtZoomW .9s ease; }
.kbd-label-b.wt-zoom{ animation:wtZoomB .9s ease; }
@keyframes wtZoomW{ 0%,100%{transform:translateX(-50%) scale(1)} 35%{transform:translateX(-50%) scale(1.5)} }
@keyframes wtZoomB{ 0%,100%{transform:translate(-50%,-160%) scale(1)} 35%{transform:translate(-50%,-160%) scale(1.5)} }
.wt-tooltip{
  position:fixed;display:none;flex-direction:column;gap:6px;
  max-width:300px;padding:14px 18px;border-radius:14px;
  background:linear-gradient(135deg, rgba(28,28,28,.97) 0%, rgba(20,20,20,.97) 100%);
  border:1.5px solid var(--primary);
  box-shadow:0 18px 44px rgba(0,0,0,.55), 0 0 28px rgba(232,105,10,.35);
  z-index:450;
  font-family:var(--font-b);
  animation:wtFadeIn .35s cubic-bezier(.2,.8,.2,1);
}
.wt-tooltip.show{display:flex}
.wt-tooltip::before{
  /* [v261] Seta apontando pro alvo. left usa --wt-arrow-x (offset em px
     a partir da borda esquerda do tooltip) pra apontar no centro horizontal
     do alvo real, mesmo quando o tooltip foi clampado pra caber no viewport. */
  content:"";position:absolute;width:14px;height:14px;
  background:linear-gradient(135deg, rgba(28,28,28,.97), rgba(20,20,20,.97));
  border-left:1.5px solid var(--primary);border-top:1.5px solid var(--primary);
  transform:rotate(45deg);
  top:-7px;left:var(--wt-arrow-x, 50%);margin-left:-7px;
}
.wt-tooltip[data-arrow="bottom"]::before{top:auto;bottom:-7px;border-left:none;border-top:none;border-right:1.5px solid var(--primary);border-bottom:1.5px solid var(--primary)}
@keyframes wtFadeIn{
  0%  {opacity:0;transform:translateY(8px) scale(.96)}
  100%{opacity:1;transform:translateY(0)    scale(1)}
}
.wt-tooltip-step{
  font-family:var(--font-h);font-weight:800;font-size:10.5px;letter-spacing:.18em;
  color:var(--muted);text-transform:uppercase;
}
.wt-tooltip-title{
  font-family:var(--font-h);font-weight:900;font-size:15px;color:var(--primary);
  letter-spacing:-.2px;
}
.wt-tooltip-desc{font-size:13px;line-height:1.5;color:var(--text)}
.wt-tooltip-btn{
  margin-top:6px;padding:8px 14px;
  border:none;border-radius:999px;cursor:pointer;
  background:var(--primary);color:#FFFFFF;
  font-family:var(--font-h);font-weight:800;font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;
  -webkit-tap-highlight-color:transparent;
}
.wt-tooltip-btn:active{transform:scale(.96)}
/* Métrica circular — substitui os 4 dots */
.pb-meter{
  width:30px;height:30px;border-radius:50%;
  background:var(--card2);border:1.5px solid var(--border2);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-h);font-weight:900;font-size:14px;color:var(--muted);
  flex-shrink:0;line-height:1;
  transition:all .12s;
}
.pb-meter.beat-on{
  background:var(--primary);border-color:var(--primary);color:#FFF;
  transform:scale(1.12);box-shadow:0 0 12px rgba(232,105,10,.7);
}
.pb-meter.beat-strong{
  background:var(--primary-2);border-color:var(--primary-2);
  transform:scale(1.20);box-shadow:0 0 16px rgba(255,123,26,.85);
}

/* === Walkthrough fullscreen — esconde elementos globais durante o tutorial ===
   [v2.26] O overlay #howto-fs e a .howto-fs-mock foram removidos.
   O tutorial agora roda SOBRE a página real u1-t0 (mesma DOM da prática).
   As regras abaixo apenas escondem chrome global (menu, navtags, progress,
   etc.) enquanto body.howto-fs-on está ativo. */
body.howto-fs-on{overflow:hidden}
/* [v2.26] .menu-btn NÃO é escondido — fica visível mas com pointer-events:none
   pra preservar paridade visual estrita entre tutorial e prática real. */
body.howto-fs-on .nav-tags,
body.howto-fs-on .global-progress,
body.howto-fs-on .pwa-banner,
body.howto-fs-on .sidebar,
body.howto-fs-on .sidebar-overlay{display:none!important}

/* Reduced motion */
@media (prefers-reduced-motion: reduce){
  #nextBtn{animation:none}
}

/* ============ PRACTICE INTRO PAGE (fecha o tutorial) ============ */
/* [v2.4] Página independente entre howto e tons. Layout app-like, parágrafos
   centralizados, CTA grande com pulso laranja. Nav-tags escondidas via
   data-hide-nav-tags="true" + lógica em updateNavTags. */
/* ============ [v141] PRACTICE INTRO — CARROSSEL DESLIZÁVEL ============
 * Cards quadrados em carrossel horizontal com scroll-snap. 1 card visível
 * por vez, centralizado. Dots de navegação abaixo (clicáveis + sync com
 * scroll). Brand kit: glass card + sheen + shimmer + halo. Tipografia
 * coerente com o resto do app, fonte branca pura.
 * ==================================================================== */
/* [v144] Specificity .page.practice-intro-page (0,2,0) protege contra os
   overrides genéricos `.page { padding ... }` em @media. */
.page.practice-intro-page{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  width:100vw;
  height:100vh;
  height:100dvh;
  min-height:100vh;
  min-height:100dvh;
  box-sizing:border-box;
  padding:
    calc(env(safe-area-inset-top) + clamp(50px, 7vh, 80px))
    0
    calc(env(safe-area-inset-bottom) + clamp(20px, 3vh, 40px));
  gap: clamp(18px, 3vh, 30px);
  background:
    radial-gradient(ellipse 60% 40% at 80% 0%,  rgba(232,105,10,.12) 0%, transparent 60%),
    radial-gradient(ellipse 50% 40% at 20% 100%, rgba(232,105,10,.08) 0%, transparent 60%),
    var(--bg);
  text-align:center;
  overflow:hidden;
}
.pi-title{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(32px, 7.5vw, 50px);line-height:1.04;letter-spacing:-1.6px;
  color:var(--text);
  margin:0;
  max-width:720px;
  flex-shrink:0;
  padding: 0 clamp(20px, 6vw, 40px);
  /* Posicionamento mantido — pull-up visual sem afetar layout */
  position:relative;
  top: clamp(-56px, -6vh, -28px);
}
.pi-title span{color:var(--primary)}

/* ---------- [v142] Tags fixas (substitui o carrossel) ---------- */
.pi-tags{
  display:grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: clamp(14px, 2.4vh, 22px);
  width:100%;
  max-width: 880px;
  padding: 0 clamp(20px, 5vw, 36px);
  box-sizing:border-box;
}
.pi-tag{
  position:relative;
  display:flex;
  align-items:center;
  gap: clamp(14px, 2.2vw, 22px);
  padding: clamp(16px, 2.4vh, 22px) clamp(18px, 2.8vw, 26px);
  border-radius:16px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.04) 0%, rgba(255,255,255,0) 30%),
    radial-gradient(ellipse 95% 140% at 0% 0%, rgba(232,105,10,.18) 0%, rgba(232,105,10,.06) 40%, transparent 72%),
    linear-gradient(135deg, rgba(34,26,20,.94) 0%, rgba(20,16,14,.94) 100%);
  border:1.5px solid rgba(232,105,10,.38);
  box-shadow:
    0 10px 28px rgba(0,0,0,.45),
    0 0 0 1px rgba(232,105,10,.10) inset,
    0 1px 0 rgba(255,255,255,.08) inset;
  overflow:hidden;
}
.pi-tag::before{
  content:"";
  position:absolute;
  inset:0;
  border-radius:inherit;
  background: linear-gradient(120deg, transparent 30%, rgba(255,200,140,.05) 50%, transparent 70%);
  pointer-events:none;
}
.pi-tag > *{ position:relative; z-index:1; }
.pi-tag-key{
  flex-shrink:0;
  font-family:var(--font-h);
  font-weight:900;
  font-size: clamp(22px, 3.2vh, 30px);
  letter-spacing:-1px;
  line-height:1;
  color: var(--primary);
  text-shadow: 0 0 18px rgba(232,105,10,.30);
  white-space:nowrap;
}
.pi-tag-key small{
  font-size:.45em;
  font-weight:700;
  letter-spacing:.04em;
  color:var(--muted);
  margin-left:2px;
}
.pi-tag-body{
  display:flex;
  flex-direction:column;
  gap:4px;
  min-width:0;
}
.pi-tag-body strong{
  font-family:var(--font-h);
  font-weight:800;
  font-size: clamp(13.5px, 1.9vh, 16px);
  letter-spacing:.02em;
  color:var(--text);
}
.pi-tag-body span{
  font-family:var(--font-b);
  font-size: clamp(12.5px, 1.65vh, 14px);
  line-height:1.5;
  color:var(--muted);
}
.pi-cta{
  display:inline-block;text-decoration:none;
  /* [v88] CTA naturalmente no bottom — sem auto margin. flex-shrink:0 garante
     que ele NUNCA encolhe nem some. */
  margin:0 auto;
  flex-shrink:0;
  padding:16px 36px;border:none;border-radius:12px;cursor:pointer;
  background:var(--primary);color:#FFFFFF;
  font-family:var(--font-h);font-weight:700;font-size:16px;letter-spacing:.04em;
  -webkit-tap-highlight-color:transparent;
  box-shadow:0 8px 24px rgba(232,105,10,.40);
  animation: piCtaPulse 2.4s ease-in-out infinite;
  transition: background .15s, transform .12s;
}
.pi-cta:hover{background:var(--primary-2);color:#FFFFFF}
.pi-cta:active{transform:translateY(1px) scale(.98)}
@keyframes piCtaPulse{
  0%,100%{ box-shadow:0 8px 24px rgba(232,105,10,.40), 0 0 0 0 rgba(232,105,10,.45); }
  50%   { box-shadow:0 12px 32px rgba(232,105,10,.55), 0 0 0 12px rgba(232,105,10,0); }
}
@media (prefers-reduced-motion: reduce){
  .pi-cta{animation:none}
}
@media (max-width:520px){
  .page.practice-intro-page,
  .page.why-minutes-page{
    gap: clamp(16px, 2.6vh, 24px);
  }
  .pi-title{
    font-size:clamp(28px,8.5vw,38px); letter-spacing:-1.2px;
    padding: 0 16px;
  }
  .pi-tags{
    grid-template-columns: 1fr;
    gap: 14px;
    padding: 0 16px;
  }
  .pi-tag{
    padding: 16px 18px;
    gap: 14px;
  }
}

/* ============ [v144] Página "Por que 30 minutos" ============
 * Mesma estrutura visual da .practice-intro-page (pi-title + pi-tags reusados).
 *
 * BLINDAGEM CONTRA `.page` GENÉRICO:
 *   `.page { padding: 48px 56px }` (linha 124) e os overrides em
 *   @media(max-width:900px)/@media(max-width:480px) têm a MESMA especificidade
 *   que `.why-minutes-page` mas vêm DEPOIS no source → vencem o cascade.
 *   Resultado: padding/min-height locais eram silenciosamente sobrescritos no
 *   mobile, o que travava ajustes finos de layout.
 *   Solução: usar `.page.why-minutes-page` (specificity 0,2,0 > 0,1,0). */
.page.why-minutes-page{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  width:100vw;
  height:100vh;
  height:100dvh;
  min-height:100vh;
  min-height:100dvh;
  box-sizing:border-box;
  padding:
    calc(env(safe-area-inset-top) + clamp(50px, 7vh, 80px))
    0
    calc(env(safe-area-inset-bottom) + clamp(20px, 3vh, 40px));
  gap: clamp(18px, 3vh, 30px);
  background:
    radial-gradient(ellipse 60% 40% at 80% 0%,  rgba(232,105,10,.12) 0%, transparent 60%),
    radial-gradient(ellipse 50% 40% at 20% 100%, rgba(232,105,10,.08) 0%, transparent 60%),
    var(--bg);
  text-align:center;
  overflow:hidden;
}

/* ============ SPECIAL PAGES ============ */
.special h1{font-family:var(--font-h);font-weight:900;font-size:42px;color:var(--text);margin-bottom:8px;border-left:4px solid var(--primary);padding-left:18px}
.special .lead{color:var(--muted);font-size:15px;max-width:680px;margin:0 0 28px 22px}
.special h3{font-family:var(--font-h);font-weight:800;font-size:18px;color:var(--text);margin:24px 0 10px}
.special p{color:var(--text);font-size:14px;margin-bottom:12px;max-width:780px}
.special ul{padding-left:22px;margin:8px 0 16px}
.special li{color:var(--text);font-size:14px;margin-bottom:6px}
.callout{background:var(--card2);border:1px solid var(--border);border-left:3px solid var(--primary);border-radius:8px;padding:16px 20px;margin:20px 0;max-width:780px}
.callout h4{font-family:var(--font-h);font-weight:800;font-size:13px;color:var(--primary);margin-bottom:8px;text-transform:uppercase;letter-spacing:1px}
.callout p{font-size:13px;color:var(--text);margin:0}

/* ============ TONS PAGE — cards verticais animados ============ */
/* [v2.5] Substitui o antigo .tons-tbl/.tier-pill/.lvl-bar (formato planilha)
   por uma lista de cards horizontais, cada um com nome do tom + descrição PT
   + barra de dificuldade animada (7 segmentos com gradiente verde→vermelho). */
.tons-page{
  display:flex;flex-direction:column;align-items:center;
  min-height:100vh;
  padding: calc(72px + env(safe-area-inset-top)) clamp(20px,5vw,40px) calc(48px + env(safe-area-inset-bottom));
  background:
    radial-gradient(ellipse 60% 40% at 80% 0%,  rgba(232,105,10,.12) 0%, transparent 60%),
    radial-gradient(ellipse 50% 40% at 20% 100%, rgba(232,105,10,.08) 0%, transparent 60%),
    var(--bg);
  text-align:center;
}
.tn-title{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(28px,6.5vw,46px);line-height:1.10;letter-spacing:-1.2px;
  color:var(--text);margin-bottom:24px;max-width:680px;
}
.tn-title span{color:var(--primary)}
.tn-prose{
  display:flex;flex-direction:column;gap:14px;
  max-width:620px;width:100%;margin:0 auto 28px;text-align:center;
}
.tn-prose p{
  font-family:var(--font-b);
  font-size:clamp(13.5px,1.6vw,16px);line-height:1.65;
  color:var(--muted);
}
.tn-prose strong{color:var(--text)}

.tn-list{
  display:flex;flex-direction:column;gap:8px;
  width:100%;max-width:560px;margin:0 auto;
}
/* Card horizontal — entrada com fade+slide-up em stagger via --idx
   [v2.8] opacity:0 inicial removido. Default = visível; animação só decora. */
.tn-card{
  display:flex;align-items:center;gap:14px;
  padding:14px 16px;border-radius:12px;
  background:var(--card);border:1px solid var(--border);
  animation: tnCardIn .55s cubic-bezier(.2,.8,.2,1) backwards;
  animation-delay: calc(var(--idx, 0) * 50ms);
  transition: border-color .15s, box-shadow .15s, transform .15s;
}
.tn-card:hover{
  border-color: rgba(232,105,10,.35);
  box-shadow: 0 6px 18px rgba(0,0,0,.32);
  transform: translateY(-1px);
}
@keyframes tnCardIn{
  from{ opacity:0; transform:translateY(10px); }
  to{ opacity:1; transform:translateY(0); }
}
.tn-name{
  font-family:var(--font-h);font-weight:800;font-size:28px;
  width:70px;flex-shrink:0;text-align:center;line-height:1;
  letter-spacing:-1px;color:var(--text);
}
.tn-info{
  flex:1 1 auto;display:flex;flex-direction:column;gap:2px;
  text-align:left;min-width:0;
}
.tn-pt{
  font-family:var(--font-h);font-weight:600;font-size:14px;
  color:var(--text);opacity:.9;letter-spacing:.04em;
}
.tn-acc{
  font-family:var(--font-b);font-size:12px;
  color:var(--muted);letter-spacing:.02em;
}
/* Barra de 7 segmentos. Os preenchidos animam com scaleX (esq→dir),
   stagger por --seg-idx em cima do delay do card-pai (--idx). */
.tn-bar{
  display:flex;gap:3px;
  width:120px;flex-shrink:0;
}
.tn-seg{
  flex:1 1 0;height:6px;border-radius:3px;
  background:rgba(255,255,255,.06);
  position:relative;overflow:hidden;
}
.tn-seg.filled::after{
  content:'';position:absolute;inset:0;border-radius:3px;
  background:var(--lvl-color);
  transform-origin:left center;
  /* [v2.8] sem scaleX(0) inicial — default = barra cheia (scaleX 1).
     A animação faz fade-in só durante o delay. Se não rodar, barra
     fica visível no estado final. */
  animation: tnSegFill .6s ease-out backwards;
  animation-delay: calc(var(--idx, 0) * 50ms + var(--seg-idx, 0) * 50ms + 200ms);
}
@keyframes tnSegFill{
  from{ transform:scaleX(0); }
  to{ transform:scaleX(1); }
}
@media (prefers-reduced-motion: reduce){
  .tn-card{ opacity:1; transform:none; animation:none; }
  .tn-seg.filled::after{ transform:scaleX(1); animation:none; }
}
@media (max-width:520px){
  .tons-page{padding-top:calc(56px + env(safe-area-inset-top))}
  .tn-card{padding:12px 14px;gap:10px}
  .tn-name{width:54px;font-size:22px;letter-spacing:-.5px}
  .tn-bar{width:100px}
  .tn-pt{font-size:13px}
  .tn-acc{font-size:11.5px}
}
@media (max-width:380px){
  .tn-name{width:48px;font-size:20px}
  .tn-bar{width:84px;gap:2px}
}

/* ============ TOC ============ */
/* ============ SUMÁRIO POR FASES (novo) ============ */
.toc-phases{display:flex;flex-direction:column;gap:10px;max-width:760px;margin:0 auto}

/* [v2.6] Subtítulo "Aprendizado progressivo" (.toc-tag-progressive) removido. */

/* [v2.6] Tags dos 3 tiers (Iniciante / Intermediário / Avançado) — agora
   empilhadas verticalmente, cada uma full-width na sua linha. */
.toc-tier-row{
  display:flex;flex-direction:column;gap:8px;
  /* [v107] margin-bottom 0 — o progress-line abaixo controla seu próprio gap
     pra centralização vertical entre AVANÇADO e o 1º card INICIANTE.
     [v223] margin-top 14 → 26 pra mais respiro entre título e tag INICIANTE. */
  max-width:560px;margin:26px auto 0;width:100%;
}
.toc-tier-tag{
  width:100%;
  display:flex;align-items:center;gap:12px;
  padding:11px 16px;border-radius:12px;
  background:color-mix(in srgb, var(--accent) 8%, var(--card2));
  border:1.5px solid var(--accent);
  /* [v190] clicável — rola até a 1ª fase do tier no sumário */
  cursor:pointer;font:inherit;color:inherit;text-align:left;
  -webkit-tap-highlight-color:transparent;
  transition:transform .12s, background .15s, box-shadow .15s;
}
.toc-tier-tag:hover{
  background:color-mix(in srgb, var(--accent) 14%, var(--card2));
  box-shadow:0 4px 14px color-mix(in srgb, var(--accent) 22%, transparent);
}
.toc-tier-tag:active{transform:scale(.98)}
.toc-tier-tag:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.toc-tier-tag .ttag-dots{
  display:inline-flex;align-items:center;color:var(--accent);
  width:24px;height:11px;flex-shrink:0;
}
.toc-tier-tag .ttag-dots svg{width:100%;height:100%}
.toc-tier-tag .ttag-info{display:flex;flex-direction:column;gap:1px;text-align:left;min-width:0}
.toc-tier-tag strong{
  font-family:var(--font-h);font-weight:800;font-size:11.5px;letter-spacing:.10em;
  color:var(--accent);text-transform:uppercase;
}
.toc-tier-tag em{font-style:normal;font-size:10.5px;color:var(--muted);font-family:var(--font-h);font-weight:600}
.toc-progress-line{
  font-family:var(--font-h);font-weight:600;font-size:12.5px;color:var(--muted);
  /* [v107] Centralizado verticalmente entre a tag AVANÇADO (acima) e o 1º
     card INICIANTE (abaixo). Margins iguais nos dois lados. */
  text-align:center;margin:18px auto;
}
.toc-progress-line strong{color:var(--primary);font-weight:900;font-size:14px}

/* (legacy) banner original — mantém oculto */
.toc-explainer{display:none}
/* [v2.6] Card de fase — sem .toc-phase-num e .toc-phase-arrow.
   Layout muda de grid 3-col para flex/block simples; o body ocupa 100%. */
/* [08/09 · ANDROID_BLUR] O `backdrop-filter` destes cartoes SAI — medido invisivel,
   e cada um deles custava uma RENDER SURFACE propria.
   Censo em Android emulado (360x800, dpr 3): a Jornada criava 62 superficies de
   `backdrop-filter` e 74 camadas compostas; com o blur fora sao 10. As 64 camadas
   que somem sao 62 cartoes + o #sidebar-overlay + a barra de abas.
   Porque doi no Android e nao no iPhone: no WebKit o desfoque de fundo e o caminho
   nativo do CoreAnimation; no Chrome/Android e readback + blur por Skia, uma render
   surface por elemento — e o fundo SUJA A CADA QUADRO, porque o #c da constelacao
   corre a 60 fps em toda tela que nao e pratica. Eram ~62 passagens de blur por
   quadro so para o aluno ler o sumario.
   🔑 O FUNDO NAO MUDA: o vidro era invisivel porque a constelacao atras ja e quase
   preta. Medido com o ceu CONGELADO (senao o diff mede o ceu, nao o cartao),
   diferenca por pixel contra o mesmo build com o blur de volta: toc 0,079 de media
   e p99 = 1 · harmony 0,022 · games 0,499 · score 0,124 · songs 0,105. Compensar a
   opacidade so PIORAVA (a .62 a media subia para 0,741) — por isso nada de alpha novo.
   ⚠️ A `.tabbar::before` FICOU COM O BLUR de proposito: ela desfoca conteudo que ROLA
   por baixo e ali o efeito VE-SE (p99 = 29), e nem opacidade 1,0 o reproduz (p99 = 9).
   E 1 superficie das 64 — nao paga o risco. */
.toc-phase{
  display:block;
  padding:14px 16px;border-radius:12px;
  /* [v478] glass fosco — constelação global visível atrás dos cards da Jornada */
  background:rgba(18,18,18,.34);
  border:1px solid var(--border2);
  border-left:3px solid var(--accent);
  position:relative;
}
.toc-phase.is-locked{
  opacity:.55;background:var(--card2);border-left-color:var(--border2);
  filter:grayscale(.6);cursor:not-allowed;
}
.toc-phase.is-current{
  border-color:var(--accent);
  box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent), 0 6px 22px color-mix(in srgb, var(--accent) 18%, transparent);
}
.toc-phase.is-done{
  background:color-mix(in srgb, var(--accent) 4%, var(--card));
}
.toc-phase-body{display:flex;flex-direction:column;gap:6px;min-width:0}
.toc-phase-title-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
/* [v2.6] white-space:nowrap pra evitar quebra "Acorde\nMaior". O título prevalece
   sobre a pílula em width — pílula vai pra próxima linha se faltar espaço. */
.toc-phase-title{
  font-family:var(--font-h);font-weight:800;font-size:16px;color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  min-width:0;flex:0 1 auto;
}
.toc-phase-pill{
  display:inline-flex;align-items:center;
  padding:2px 9px;border-radius:999px;
  background:color-mix(in srgb, var(--accent) 16%, transparent);
  border:1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  font-family:var(--font-h);font-weight:800;font-size:9.5px;letter-spacing:.18em;
  color:var(--accent);text-transform:uppercase;
  white-space:nowrap;flex-shrink:0;
}
.toc-phase-pill.done{background:rgba(34,197,94,.12);border-color:rgba(34,197,94,.45);color:#22C55E}
/* [v2.12] Pílula de status "BLOQUEADA" — tom neutro pra contrastar com
   "EM ANDAMENTO" (laranja) e "CONCLUÍDA" (verde). */
.toc-phase-pill.locked{
  background:rgba(255,255,255,.04);
  border-color:var(--border2);
  color:var(--muted);
}
/* [v2.12] Removidos:
   .toc-phase-progress + .toc-phase-progress > div  (barra fina horizontal)
   .toc-phase-pills + .tpill-mini variants  (4 caixinhas com tons C/G/F/D)
   Cards agora têm só a pílula principal (título + status). */
/* [v2.16] .toc-phase-sub RESTAURADO — distingue fases que compartilham
   unidade (ex: "Acorde Maior · Em Dó, Sol, Fá, Ré" vs "Acorde Maior · Em
   Si♭, Lá, Mi♭, Mi"). Uma linha, sem quebra, var(--muted). */
.toc-phase-sub{
  font-family:var(--font-b);font-size:12.5px;font-weight:400;line-height:1.3;
  color:var(--muted);letter-spacing:.01em;
  margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.toc-phase.is-locked .toc-phase-sub{opacity:.55}
.toc-phase.is-done   .toc-phase-sub{color:rgba(34,197,94,.75)}
.toc-phase-head-link{display:none}
/* Card de fase como link inteiro — toda a área é clicável */
.toc-phase-link{
  text-decoration:none;color:inherit;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:transform .12s, border-color .12s, background .12s;
}
.toc-phase-link:hover{transform:translateY(-1px);background:color-mix(in srgb, var(--accent) 6%, var(--card))}
.toc-phase-link:active{transform:scale(.99)}
/* [v2.6] .toc-phase-arrow removido junto com .toc-phase-num. */

/* ============ SUMÁRIO (toc) — layout app-like com cards e tabs ============ */
/* [human_sim] Banner explicativo no sumário pra iniciantes */
.toc-explainer{
  display:none;
  max-width:560px;margin:6px auto 16px;
  padding:10px 14px;border-radius:10px;
  background:rgba(34,197,94,.08);border:1px solid rgba(34,197,94,.30);
  font-size:12.5px;line-height:1.5;color:var(--text);text-align:center;
}
.toc-explainer strong{color:#22C55E;font-family:var(--font-h);font-weight:800;letter-spacing:.04em}
.toc-page[data-tier="iniciante"] .toc-explainer{display:block}
.toc-page{padding:32px clamp(16px,4vw,40px) calc(96px + env(safe-area-inset-bottom))}
/* [v90] toc-hero ganha respiro maior abaixo (até a primeira tagbox iniciante) */
.toc-hero{text-align:center;width:100%;margin:18px auto 36px;overflow:hidden}
/* [v90] Título mega aumentado — destaque maior do "O caminho completo" */
.toc-mega-title{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(36px, 9vw, 72px);line-height:.98;letter-spacing:-1.8px;
  color:var(--text);
  border-left:none;padding-left:0;margin:0;
}
.toc-mega-title span{color:var(--primary)}
.toc-level-picker{
  display:flex;gap:8px;justify-content:center;flex-wrap:wrap;
  /* [v90] espaço extra acima → separa visualmente do título */
  margin:24px auto 18px;max-width:560px;
}
.toc-level{
  display:inline-flex;align-items:center;gap:8px;
  padding:8px 14px;border-radius:999px;
  background:var(--card2);border:1px solid var(--border2);
  color:var(--muted);
  font-family:var(--font-h);font-weight:700;font-size:11.5px;letter-spacing:.16em;
  text-transform:uppercase;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:all .15s;
}
.toc-level:hover{color:var(--text)}
.toc-level .lv-dots{display:inline-flex;align-items:center;color:var(--accent, var(--primary));width:24px;height:11px}
.toc-level .lv-dots svg{width:100%;height:100%}
.toc-level.active{
  background:var(--accent);border-color:var(--accent);color:#FFFFFF;
  box-shadow:0 4px 16px color-mix(in srgb, var(--accent) 45%, transparent);
}
.toc-level.active .lv-dots{color:#FFFFFF}
.toc-tabs{
  display:flex;gap:8px;flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;
  padding:8px 0 16px;margin:0 auto 18px;
  position:sticky;top:0;z-index:5;
  background:linear-gradient(180deg, var(--bg) 70%, transparent 100%);
  scrollbar-width:none;
}
.toc-tabs::-webkit-scrollbar{display:none}
.toc-tab{
  flex-shrink:0;
  padding:8px 16px;border-radius:999px;
  background:var(--card2);border:1px solid var(--border2);color:var(--muted);
  font-family:var(--font-h);font-weight:700;font-size:11.5px;letter-spacing:.12em;
  text-transform:uppercase;cursor:pointer;white-space:nowrap;
  -webkit-tap-highlight-color:transparent;
  transition:background .15s, border-color .15s, color .15s, transform .12s;
}
.toc-tab:hover{color:var(--text)}
.toc-tab.active{
  background:var(--accent, var(--primary));
  border-color:var(--accent, var(--primary));
  color:#FFFFFF;
  box-shadow:0 4px 14px color-mix(in srgb, var(--accent, var(--primary)) 40%, transparent);
}
.toc-tab:active{transform:scale(.97)}
.toc-list{max-width:760px;margin:0 auto}
.toc-section{margin-bottom:24px}
.toc-section.is-hidden{display:none}
.toc-section-head{
  display:flex;align-items:center;gap:10px;
  margin:0 4px 10px;
}
.toc-section-icon{
  width:30px;height:30px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:color-mix(in srgb, var(--accent) 18%, transparent);
  border:1px solid color-mix(in srgb, var(--accent) 50%, transparent);
  color:var(--accent);font-size:14px;line-height:1;
}
.toc-section-head h3{
  font-family:var(--font-h);font-weight:800;font-size:13px;letter-spacing:.18em;
  color:var(--text);text-transform:uppercase;
  flex:1;border-bottom:none;padding-bottom:0;margin-bottom:0;
}
.toc-section-count{
  font-family:var(--font-h);font-weight:800;font-size:11px;letter-spacing:.10em;
  color:var(--accent);
  background:color-mix(in srgb, var(--accent) 12%, transparent);
  padding:3px 9px;border-radius:999px;
}
.toc-cards{display:grid;gap:8px;grid-template-columns:1fr}
@media (min-width:680px){
  .toc-cards{grid-template-columns:1fr 1fr}
}
.toc-card{
  display:flex;align-items:center;gap:14px;
  padding:13px 14px;
  /* [v478] glass fosco — mesma identidade dos cards de fase */
  background:rgba(18,18,18,.34);
  border:1px solid var(--border2);
  border-left:3px solid var(--accent);border-radius:11px;
  text-decoration:none;color:var(--text);
  transition:background .12s, transform .12s, border-color .12s;
  -webkit-tap-highlight-color:transparent;
}
.toc-card:hover{background:rgba(26,26,26,.46);border-color:color-mix(in srgb, var(--accent) 60%, var(--border2))}
.toc-card:active{transform:scale(.98)}
/* [fix card] Blindagem do handoff welcome→Jornada: enquanto body.tt-welcome-handoff, os cards/fases ignoram QUALQUER
   ponteiro (toque, hit-test fantasma do iOS ao remover o iframe, click sintético) → o card da fase atual nunca
   "seleciona"/muda de cor sozinho no instante do handoff. A classe é tirada ~600ms após o handoff. */
body.tt-welcome-handoff .toc-phase, body.tt-welcome-handoff .toc-card{ pointer-events:none !important; }
.toc-card-num{
  flex-shrink:0;width:42px;
  font-family:var(--font-h);font-weight:900;font-size:13px;letter-spacing:.06em;
  color:var(--accent);
}
.toc-card-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.toc-card-title{
  font-family:var(--font-h);font-weight:800;font-size:14px;color:var(--text);
  letter-spacing:.02em;line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.toc-card-sym{color:var(--accent);margin-left:6px}
.toc-card-meta{
  font-size:11.5px;color:var(--muted);
  font-family:var(--font-h);font-weight:600;letter-spacing:.04em;
}
.toc-card-tier{font-weight:700}
.toc-card.is-locked{
  opacity:.55;cursor:not-allowed;background:var(--card2);
  border-color:var(--border2);border-left-color:var(--border2);
  filter:grayscale(.6);
}
.toc-card.is-locked .toc-card-num,
.toc-card.is-locked .toc-card-title{color:var(--muted)}
/* Barra de progresso por unidade — invisível quando 0% */
.toc-card-pbar{
  display:block;width:100%;height:3px;border-radius:3px;
  background:var(--card2);overflow:hidden;
  margin-top:6px;
}
.toc-card-pbar > span{
  display:block;height:100%;
  background:linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 70%, #FFF));
  border-radius:3px;
  transition:width .35s cubic-bezier(.2,.8,.2,1);
}
.toc-card-progress{
  flex-shrink:0;
  font-family:var(--font-h);font-weight:800;font-size:11px;letter-spacing:.06em;
  color:var(--accent);min-width:38px;text-align:right;
}
.toc-card.is-done{
  border-color:color-mix(in srgb, var(--accent) 75%, transparent);
  background:color-mix(in srgb, var(--accent) 5%, var(--card));
  box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent) inset;
}
.toc-card.is-done .toc-card-progress{color:#22C55E}
.toc-card.is-done::after{
  content:"";position:absolute;top:8px;right:34px;
  width:14px;height:14px;border-radius:50%;
  background:#22C55E;
  box-shadow:0 0 8px rgba(34,197,94,.65);
}
.toc-card{position:relative}
.toc-card-arrow{
  flex-shrink:0;font-family:var(--font-h);font-weight:300;font-size:24px;color:var(--accent);opacity:.6;
  transition:opacity .12s, transform .12s;
}
.toc-card:hover .toc-card-arrow{opacity:1;transform:translateX(2px)}

/* ============ UNIT INTRO ============ */
.unit-intro .u-tag{display:inline-block;background:rgba(232,105,10,.12);color:var(--primary);border:1px solid rgba(232,105,10,.30);padding:5px 12px;border-radius:14px;font-family:var(--font-h);font-weight:700;font-size:11px;letter-spacing:1.5px;text-transform:uppercase;margin-bottom:14px}
.unit-intro h1{font-family:var(--font-h);font-weight:900;font-size:54px;color:var(--text);line-height:1;letter-spacing:-1px}
.unit-intro h1 .sym{color:var(--primary);margin-left:14px;font-size:42px}
.unit-intro .formulae{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0 6px}
.badge{display:inline-block;padding:5px 12px;border-radius:14px;font-size:12px;font-weight:600;font-family:var(--font-h);letter-spacing:.5px}
.b-iv{background:rgba(232,105,10,.15);color:var(--primary);border:1px solid rgba(232,105,10,.35)}
.b-deg{background:rgba(100,200,255,.10);color:#64C8FF;border:1px solid rgba(100,200,255,.25)}
.b-form{background:rgba(255,255,255,.05);color:var(--text);border:1px solid var(--border2)}
/* [v518] textos explicativos DESCEM um pouquinho (margin-top +10) e o gap até o piano encolhe o mesmo
   tanto (margin-bottom -10) → os textos descem JUNTOS mas o piano de referência fica no MESMO lugar. */
/* [v539] o GRUPO subtítulo→texto→mini piano→botões (Ouvir referência + Praticar) desce como bloco pra preencher
   o vazio no rodapé: margin-top do .character 28→96 empurra ele E todos os irmãos abaixo (context/kbd-wrap/intro-stack)
   em fluxo; título + .formulae (Graus/Semitons/N notas) ficam no lugar; distâncias internas do grupo intactas. */
/* [v640] filler fluido (era 96px FIXO): clamp preserva ~96 em tela alta e encolhe até 28 em tela curta →
   o CTA cabe sem depender do breakpoint binário ≤780 (que ignorava a safe-area). A .page mantém scroll como
   rede (nunca clipa); no tutorial (experiencia.html) o mesmo sistema é hard-fit com flex+overflow:hidden. */
.unit-intro .character{font-family:var(--font-h);font-weight:600;font-size:20px;color:var(--text);margin:clamp(28px,11vh,96px) 0 8px;font-style:italic}
.unit-intro .context{color:var(--muted);font-size:14px;max-width:760px;margin-bottom:10px}
/* [v518] botão "Ouvir referência" DESCE um pouquinho (mais respiro abaixo do piano) e o CTA "Praticar"
   SOBE um pouquinho (gap antes do CTA encolhe de 52→34). Título/tags e piano não se mexem.
   [v527] o PAR (Ouvir referência + Praticar) desce mais um pouquinho: audio-btn-row 30→46 (empurra os dois
   pra baixo); intro-stack:34 (gap entre eles) NÃO muda → a distância entre os botões fica igual. */
.unit-intro .audio-btn-row{margin-top:clamp(12px,4.4vh,46px)}  /* [v640] vão fluido: 46→12 */
/* [v537] o CTA "Praticar" desce um pouco a MAIS (só ele): aumenta o gap acima da intro-stack (34→50).
   audio-btn-row (Ouvir referência) e o bloco de referência ficam onde estão; só o Praticar afasta. */
.unit-intro .intro-stack{margin-top:clamp(14px,4.8vh,50px)}  /* [v640] vão fluido: 50→14 */
/* [v528] o GRUPO de referência (piano + título "Referência em…" + "Ouvir referência") + CTA "Praticar" desce
   um pouquinho COMO UM BLOCO: o margin-top do .kbd-wrap (18→38) empurra o kbd-wrap E a intro-stack juntos pra
   baixo; as distâncias internas (audio-btn-row 46 / intro-stack 34) ficam intactas. Só na unit-intro. */
.unit-intro .kbd-wrap{margin-top:clamp(10px,3.6vh,38px)}  /* [v640] vão fluido: 38→10 */
/* [v640] breakpoint binário @media(max-height:780px) do v637 REMOVIDO: os clamp() acima são contínuos
   (encolhem suave em qualquer altura, sem o degrau que ignorava a safe-area). */
/* [v518] "Ouvir referência" agora FUNCIONA → fica LARANJA (contorno laranja + texto/ícone laranja; enche
   no hover/active/playing via as regras globais .audio-btn:hover/.playing). */
.unit-intro .audio-btn{background:rgba(232,105,10,.10);border-color:var(--primary);color:var(--primary)}
.tons-pill-row{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px}
/* Barra de progresso da unidade no intro */
.unit-progress-bar{display:flex;align-items:center;gap:12px;margin:0 0 14px}
.upb-track{
  flex:1;height:6px;border-radius:3px;
  background:var(--card2);border:1px solid var(--border2);
  overflow:hidden;
}
.upb-fill{
  height:100%;background:linear-gradient(90deg, var(--primary), var(--primary-2));
  transition:width .45s cubic-bezier(.2,.8,.2,1);box-shadow:0 0 6px rgba(232,105,10,.5);
}
.upb-meta{font-family:var(--font-h);font-weight:800;font-size:11px;letter-spacing:.10em;color:var(--primary);text-transform:uppercase;white-space:nowrap}
/* Pílulas de tom com estado */
.tpill-partial{border-color:rgba(232,105,10,.40)!important;background:rgba(232,105,10,.08)!important;color:var(--primary)!important}
.tpill-done{
  background:linear-gradient(135deg, #22C55E 0%, #16A34A 100%)!important;
  border-color:#16A34A!important;color:#FFFFFF!important;
}
.tpill-mark{display:inline-block;margin-left:3px;font-weight:900;font-size:10px}
.tpill{display:inline-block;background:var(--card2);border:1px solid var(--border2);color:var(--text);font-family:var(--font-h);font-weight:700;font-size:11px;padding:6px 12px;border-radius:14px;text-decoration:none;cursor:pointer;transition:background .15s,border-color .15s,color .15s,transform .1s;-webkit-tap-highlight-color:transparent}
.tpill:hover{background:var(--primary);border-color:var(--primary);color:#fff}
.tpill:active{transform:scale(.95)}
.tpill.first{background:var(--primary);color:#fff;border-color:var(--primary)}
.tpill.first:hover{background:var(--primary-dark);border-color:var(--primary-dark)}
.intro-stack{display:flex;flex-direction:column;gap:22px;margin-top:52px;max-width:720px;margin-left:auto;margin-right:auto}
.ordem-block h4{font-family:var(--font-h);font-weight:800;font-size:11px;color:var(--primary);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:10px}

/* ============ PRACTICE CTA (grande botão "Praticar nos 12 tons") ============ */
.practice-cta{
  display:flex;align-items:center;gap:16px;
  width:100%;margin-top:22px;padding:18px 22px;
  background:linear-gradient(135deg,var(--primary) 0%,#FF7B1A 50%,var(--primary-dark) 100%);
  border:none;border-radius:14px;text-decoration:none;color:#fff;
  box-shadow:0 10px 30px rgba(232,105,10,.30),0 2px 0 rgba(255,255,255,.10) inset,0 -2px 0 rgba(0,0,0,.20) inset;
  transition:transform .15s,box-shadow .15s,filter .15s;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  position:relative;overflow:hidden;
}
.practice-cta::before{
  content:"";position:absolute;inset:0;
  background:radial-gradient(circle at 30% 20%, rgba(255,255,255,.18) 0%, transparent 50%);
  pointer-events:none;
}
.practice-cta:hover{transform:translateY(-2px);box-shadow:0 14px 40px rgba(232,105,10,.45),0 2px 0 rgba(255,255,255,.10) inset,0 -2px 0 rgba(0,0,0,.20) inset}
.practice-cta:active{transform:translateY(0);filter:brightness(.95)}
.practice-cta-play{
  width:52px;height:52px;flex-shrink:0;border-radius:50%;
  background:rgba(0,0,0,.28);border:2px solid rgba(255,255,255,.45);
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 4px 14px rgba(0,0,0,.30);
}
.practice-cta-play svg{margin-left:2px}
.practice-cta-label{flex:1;display:flex;flex-direction:column;gap:2px;text-align:left;min-width:0}
/* 🔴 [30/08] A CLASSE QUE NÃO EXISTIA. `.practice-cta-txt` está em OITO lugares
   do index (a folha, os songs, o quiz, o jogo de inversões) e não tinha UMA
   regra aqui. Sem `flex:1` o rótulo não estica e a seta cola no texto em vez de
   ir para a borda; sem `flex-direction:column` o título e a legenda saem na
   MESMA linha. Era esta a diferença entre o Praticar da folha e o da unit-intro
   — que usa `.practice-cta-label`, logo acima, e por isso sempre esteve certo.
   Uma regra conserta os oito de uma vez. */
.practice-cta-txt{flex:1;display:flex;flex-direction:column;gap:2px;text-align:left;min-width:0}
.practice-cta-title{font-family:var(--font-h);font-weight:900;font-size:18px;letter-spacing:.3px;line-height:1.15}
.practice-cta-sub{font-family:var(--font-b);font-weight:500;font-size:12px;color:rgba(255,255,255,.85);letter-spacing:.2px}
.practice-cta-arrow{font-family:var(--font-h);font-weight:900;font-size:24px;opacity:.85;flex-shrink:0;transition:transform .2s}
.practice-cta:hover .practice-cta-arrow{transform:translateX(4px)}
@media(max-width:600px){
  .practice-cta{padding:14px 16px;gap:12px}
  .practice-cta-play{width:44px;height:44px}
  .practice-cta-title{font-size:16px}
  .practice-cta-sub{font-size:11.5px}
  .practice-cta-arrow{font-size:20px}
}

/* [v244] RHYTHM CTA — 4 botões de levada na unit-intro de Progressão */
.rhythm-cta-group{display:flex;flex-direction:column;gap:10px;margin-top:22px;width:100%}
.rhythm-cta-head{font-family:var(--font-h);font-weight:800;font-size:11px;color:var(--primary);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:4px;text-align:center}
.rhythm-cta{
  display:flex;align-items:center;gap:14px;
  width:100%;padding:14px 18px;
  /* [v254] Mesma paleta do .practice-cta (laranja brand) — antes era azul,
     destoava dos outros CTAs da unit-intro. */
  background:linear-gradient(135deg,var(--primary) 0%,#FF7B1A 50%,var(--primary-dark) 100%);
  border:none;border-radius:12px;text-decoration:none;color:#fff;
  box-shadow:0 8px 22px rgba(232,105,10,.28),0 2px 0 rgba(255,255,255,.10) inset,0 -2px 0 rgba(0,0,0,.18) inset;
  transition:transform .15s,box-shadow .15s,filter .15s;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  position:relative;overflow:hidden;
}
.rhythm-cta::before{content:"";position:absolute;inset:0;background:radial-gradient(circle at 30% 20%,rgba(255,255,255,.18) 0%,transparent 50%);pointer-events:none}
.rhythm-cta:hover{transform:translateY(-2px);box-shadow:0 12px 32px rgba(232,105,10,.42),0 2px 0 rgba(255,255,255,.10) inset,0 -2px 0 rgba(0,0,0,.18) inset}
.rhythm-cta:active{transform:translateY(0);filter:brightness(.95)}
.rhythm-cta-play{
  width:42px;height:42px;flex-shrink:0;border-radius:50%;
  background:rgba(0,0,0,.28);border:2px solid rgba(255,255,255,.45);
  display:flex;align-items:center;justify-content:center;
}
.rhythm-cta-play svg{margin-left:2px}
.rhythm-cta-body{flex:1;display:flex;flex-direction:column;gap:2px;text-align:left;min-width:0}
.rhythm-cta-name{font-family:var(--font-h);font-weight:900;font-size:16px;letter-spacing:.3px;line-height:1.15}
.rhythm-cta-sub{font-family:var(--font-b);font-weight:500;font-size:12px;color:rgba(255,255,255,.85);letter-spacing:.2px}
@media(max-width:600px){
  .rhythm-cta{padding:12px 14px;gap:11px}
  .rhythm-cta-play{width:38px;height:38px}
  .rhythm-cta-name{font-size:14.5px}
  .rhythm-cta-sub{font-size:11.5px}
}

/* ============ PRACTICE PAGE ============ */
.practice-head{display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:14px;margin-bottom:20px}
.practice-head .left .tag{display:inline-block;color:var(--primary);font-family:var(--font-h);font-weight:800;font-size:11px;letter-spacing:1.5px;text-transform:uppercase;margin-bottom:4px}
.practice-head .left h2{font-family:var(--font-h);font-weight:900;font-size:34px;color:var(--text);line-height:1}
.practice-head .right{text-align:right}
.tom-current{font-family:var(--font-h);font-weight:900;font-size:54px;color:var(--primary);line-height:.9}
.tom-current .pt{font-family:var(--font-h);font-weight:400;font-size:18px;color:var(--muted);margin-left:8px;letter-spacing:1px}
.tom-counter{font-family:var(--font-h);font-weight:700;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:4px}
.dot-row{display:flex;gap:5px;margin:6px 0 18px;justify-content:center}
.dot{width:10px;height:10px;border-radius:50%;background:var(--border)}
.dot.done{background:var(--primary-dark)}
.dot.cur{background:var(--primary);box-shadow:0 0 0 3px rgba(232,105,10,.2)}

/* ============ KEYBOARD ============ */
.kbd-wrap{margin:18px auto 14px;text-align:center;position:relative}
.kbd-svg{display:block;margin:0 auto;max-width:100%;height:auto}
/* [v2.28] Cifras nas teclas — DUAS estratégias:
   (a) Non-stretch (cover/intro): <text> SVG com .kbd-letter (sem distorção
       porque o SVG mantém aspect natural).
   (b) Stretch (practice page): <span class="kbd-label"> em HTML overlay,
       posicionado absoluto sobre o .kbd-wrap. Não sofre o squish horizontal
       do preserveAspectRatio="none". */
.kbd-letter{
  font-family:'Montserrat', sans-serif;
  font-weight:800;
  fill:#FFFFFF;
  letter-spacing:.2px;
  paint-order:stroke fill;
}
.kbd-letter-w{font-size:14px}
.kbd-letter-b{font-size:11px}
.kbd-labels{
  position:absolute;inset:0;
  pointer-events:none;
}
.kbd-label{
  position:absolute;
  transform:translateX(-50%);
  font-family:'Montserrat', sans-serif;
  font-weight:800;
  color:#FFFFFF;
  letter-spacing:.3px;
  line-height:1;
  text-shadow: 0 1px 3px rgba(0,0,0,.45);
  white-space:nowrap;
}
.kbd-label-w{
  /* tecla branca: bottom maior pra subir a posição da cifra (eleva sobre o
     centro óptico do trecho inferior visível da tecla). */
  bottom: clamp(16px, 3vh, 28px);
  font-size: clamp(13px, 1.7vw, 18px);
}
.kbd-label-b{
  /* tecla preta: ancorada na base da tecla preta (ratio 62.5% do viewBox);
     translate(-50%, -160%) sobe um pouco mais a label sobre a tecla preta. */
  transform: translate(-50%, -160%);
  font-size: clamp(10px, 1.3vw, 14px);
  color: #F0F0F0;
  text-shadow: 0 1px 2px rgba(0,0,0,.7);
}
.kbd-key{transition:fill .06s linear,stroke .06s linear,filter .22s ease-out;transform-origin:center bottom}
/* [v2.30] Teclas ativas (laranja default) ficam DIM por padrão. Quando tocadas
   (.kbd-playing), ACENDEM com brilho + glow — invertendo a antiga lógica em
   que tocar parecia perder brilho. */
/* [v292] Demais notas do exercício: ainda menos destacadas — mais fosco/opaco,
   sem brilho (só guia discreto; a tecla da vez pulsa neon por cima). */
.kbd-key.kbd-active{opacity:1} /* [v418] semi-aceso = brand #E8690A EXATO, opaco (era brightness(.4) saturate(.6) opacity:.62 = fosco/transparente) */
/* [v270] Estilo .kbd-key.kbd-scale REMOVIDO junto com a feature "teclas de
   improviso / campo harmônico" do exercício de Progressão. */
/* Tecla tocada: ACENDE — laranja vira vivo + glow. !important garante que
   a brightness aqui sobrescreva o filter dim de .kbd-active. */
.kbd-key.kbd-playing.kbd-w{fill:#FFB347!important;stroke:#000!important;filter:brightness(1.25) drop-shadow(0 0 9px rgba(255,170,60,.95))!important}
.kbd-key.kbd-playing.kbd-b{fill:#FFC066!important;stroke:#000!important;filter:brightness(1.3)  drop-shadow(0 0 8px rgba(255,180,80,1))!important}
/* Hint dim (etapa "Toque com Click"): só sinaliza a tecla certa, sem brilho.
   Tecla acende totalmente quando o usuário pressionar (vira kbd-playing). */
.kbd-key.kbd-hint.kbd-w{fill:#9A4A1C!important;stroke:#000!important;filter:none;opacity:.92} /* [v422] borda SEMPRE preta */
.kbd-key.kbd-hint.kbd-b{fill:#A55020!important;stroke:#000!important;filter:none;opacity:.95}
/* kbd-playing tem precedência sobre kbd-hint quando o usuário toca */
.kbd-key.kbd-hint.kbd-playing.kbd-w{fill:#FFB347!important;stroke:#000!important;filter:brightness(1.25) drop-shadow(0 0 9px rgba(255,170,60,.95))!important;opacity:1}
.kbd-key.kbd-hint.kbd-playing.kbd-b{fill:#FFC066!important;stroke:#000!important;filter:brightness(1.3) drop-shadow(0 0 8px rgba(255,180,80,1))!important;opacity:1}
/* [v360] Cue da etapa CLICK = MESMO OURO CINTILANTE da tecla acesa. Classe marcadora
   .kbd-clickcue adicionada SÓ pelo ClickCue (practice page) → NÃO afeta capa/demo (que usam
   kbd-playing direto). Mesma especificidade (3 classes) que .kbd-playing mas vem DEPOIS → vence
   o laranja; fica ANTES de guided-hit/miss → verde/vermelho continuam vencendo (e ao acertar o
   ClickCue já troca kbd-clickcue→kbd-guided-hit, então nem coexistem). */
.kbd-key.kbd-clickcue.kbd-w{fill:#FADE6E!important;stroke:#000!important;opacity:1; /* [v422] borda SEMPRE preta */
  filter:drop-shadow(0 0 6px rgba(250,225,120,.65)) drop-shadow(0 0 15px rgba(255,205,90,.40)) brightness(1.02);
  animation:keyShimmer 1.8s ease-in-out infinite}
.kbd-key.kbd-clickcue.kbd-b{fill:#FEFA95!important;stroke:#000!important;opacity:1; /* [v422] borda SEMPRE preta */
  filter:drop-shadow(0 0 7px rgba(254,250,150,.78)) drop-shadow(0 0 18px rgba(255,225,110,.50)) brightness(1.03);
  animation:keyShimmer 1.8s ease-in-out infinite}

/* [v283] No guided mode a tecla acende (kbd-playing) enquanto segurada. O flash
   de acerto/erro precisa VENCER a cor de "pressionada" — mesma especificidade
   (3 classes) que .kbd-playing, mas vindo DEPOIS na ordem ganha o desempate. */
.kbd-key.kbd-guided-hit.kbd-w,.kbd-key.kbd-guided-hit.kbd-b{fill:#4ADE80!important;stroke:#000!important;filter:drop-shadow(0 0 9px rgba(74,222,128,.95))!important;opacity:1} /* [v422] borda SEMPRE preta */
.kbd-key.kbd-guided-miss.kbd-w,.kbd-key.kbd-guided-miss.kbd-b{fill:#EF4444!important;stroke:#000!important;filter:drop-shadow(0 0 9px rgba(239,68,68,.95))!important;opacity:1}

/* Botões de áudio (Ouvir referência + Piano ON/OFF) */
.audio-btn-row{display:flex;justify-content:center;gap:10px;flex-wrap:wrap;margin-top:22px}
.audio-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  background:var(--card2);border:1px solid var(--border2);
  color:var(--text);font-family:var(--font-h);font-weight:700;
  font-size:12px;letter-spacing:1px;text-transform:uppercase;
  padding:10px 16px;border-radius:24px;cursor:pointer;
  transition:background .15s,border-color .15s,color .15s,transform .1s;
  -webkit-tap-highlight-color:transparent;line-height:1;min-width:170px;
}
.audio-btn:hover:not(:disabled){background:var(--primary);border-color:var(--primary);color:#fff}
.audio-btn:active:not(:disabled){transform:scale(.97)}
.audio-btn:disabled{opacity:.5;cursor:wait}
.audio-btn.playing{background:var(--primary);border-color:var(--primary);color:#fff;animation:audioPulse 1.2s ease-in-out infinite}
@keyframes audioPulse{0%,100%{box-shadow:0 0 0 0 rgba(232,105,10,.5)}50%{box-shadow:0 0 0 8px rgba(232,105,10,0)}}
.audio-btn-icon{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;flex-shrink:0}
.audio-btn-icon svg{display:block}
/* Toggle Piano ON/OFF */
.notes-row{display:flex;justify-content:center;gap:8px;flex-wrap:wrap;margin:14px 0 6px}
.note-chip{background:var(--card2);border:1px solid var(--primary);color:var(--primary);font-family:var(--font-h);font-weight:800;font-size:14px;padding:6px 12px;border-radius:8px;letter-spacing:.5px}
.note-chip.root{background:var(--primary);color:#fff}

/* ============ METRONOME + TIMER COMPONENT ============ */
.mt-card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:24px;margin:22px auto 8px;max-width:720px;transition:border-color .3s,box-shadow .3s}
.mt-card.mt-completed{border-color:#22C55E;box-shadow:0 0 0 1px rgba(34,197,94,.25)}

.mt-stages{display:flex;gap:6px;margin-bottom:18px}
.mt-stage{flex:1;background:var(--card2);border:1px solid var(--border);border-radius:8px;padding:12px 10px;text-align:center;transition:all .2s;position:relative;cursor:pointer;user-select:none;-webkit-user-select:none}
.mt-stage:hover{border-color:var(--border2);background:rgba(232,105,10,.05)}
.mt-stage:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
.mt-stage.mt-active{border-color:var(--primary);background:rgba(232,105,10,.12);box-shadow:0 0 0 1px var(--primary) inset}
.mt-stage.mt-done{opacity:.55}
.mt-stage .ic{font-size:18px;display:block;margin-bottom:4px}
.mt-stage .nm{font-family:var(--font-h);font-weight:800;font-size:10px;color:var(--text);text-transform:uppercase;letter-spacing:1px}
.mt-stage .dur{font-family:var(--font-h);font-weight:700;font-size:10px;color:var(--muted);margin-top:2px}

.mt-timer{margin-bottom:14px;text-align:center}
.mt-time{font-family:var(--font-h);font-weight:900;font-size:54px;color:var(--text);font-variant-numeric:tabular-nums;line-height:1}
.mt-progress{width:100%;height:4px;background:var(--border);border-radius:3px;margin-top:10px;overflow:hidden}
.mt-progress-bar{height:100%;background:var(--primary);width:100%;transition:width .25s linear;border-radius:3px}

.mt-pulse{display:flex;justify-content:center;align-items:center;gap:18px;margin:14px 0;height:24px;opacity:0;transition:opacity .25s}
.mt-pulse.mt-pulse-active{opacity:1}
.mt-beat{width:14px;height:14px;border-radius:50%;background:#555;transition:transform .08s ease-out,background-color .08s ease-out,box-shadow .08s ease-out}
.mt-beat[data-beat="0"]{width:18px;height:18px}
.mt-beat-flash-strong{transform:scale(1.4);background:var(--primary)!important;box-shadow:0 0 18px rgba(232,105,10,.7)}
.mt-beat-flash-weak{transform:scale(1.15);background:#888!important}

.mt-bpm-row{min-height:48px;margin:10px 0 4px;display:flex;align-items:center;justify-content:center}
.mt-bpm{display:none;align-items:center;justify-content:center;gap:14px}
.mt-bpm.mt-show{display:flex}
.mt-bpm-btn{width:38px;height:38px;border-radius:50%;background:var(--card2);border:1px solid var(--border2);color:var(--text);font-size:20px;font-weight:700;cursor:pointer;transition:all .15s;display:flex;align-items:center;justify-content:center;font-family:var(--font-h);user-select:none;-webkit-user-select:none;touch-action:manipulation}
.mt-bpm-btn:hover{background:var(--primary);border-color:var(--primary);color:#fff}
.mt-bpm-btn:active{transform:scale(.95)}
.mt-bpm-display{font-family:var(--font-h);font-weight:900;font-size:22px;color:var(--primary);min-width:120px;text-align:center;letter-spacing:.5px}
.mt-bpm-display .lbl{font-size:13px;color:var(--muted);font-weight:700;margin-left:4px;letter-spacing:1px}
.mt-bpm-fixed{display:none;font-family:var(--font-h);font-weight:800;font-size:15px;color:var(--primary);letter-spacing:1px;text-align:center}
.mt-bpm-fixed.mt-show{display:block}

.mt-controls{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:14px}
.mt-btn{background:var(--primary);color:#fff;border:none;border-radius:50px;padding:11px 26px;font-family:var(--font-h);font-weight:800;font-size:13px;letter-spacing:1.5px;text-transform:uppercase;cursor:pointer;transition:transform .15s,background .15s;min-width:160px}
.mt-btn:hover{background:var(--primary-2);transform:translateY(-1px)}
.mt-btn-sec{background:transparent;color:var(--text);border:1px solid var(--border2);border-radius:50px;padding:10px 20px;font-family:var(--font-h);font-weight:700;font-size:12px;letter-spacing:1.5px;text-transform:uppercase;cursor:pointer;transition:all .15s}
.mt-btn-sec:hover{background:var(--card2)}

.mt-complete{display:none;text-align:center;margin-top:14px;padding-top:14px;border-top:1px dashed var(--border)}
.mt-card.mt-completed .mt-complete{display:block}
.mt-complete-msg{font-family:var(--font-h);font-weight:800;font-size:14px;color:#22C55E;letter-spacing:.5px}
.mt-complete-msg span{color:var(--text)}

/* Print: static visual */
.mt-print{display:none;margin:22px auto 8px;max-width:720px}
.mt-print-stages{display:flex;gap:0;border-radius:10px;overflow:hidden;border:1px solid var(--border)}
.mt-print-stage{padding:14px 10px;text-align:center;color:#fff;font-family:var(--font-h);position:relative}
.mt-print-stage .ic{font-size:18px;display:block;margin-bottom:3px}
.mt-print-stage .nm{font-weight:800;font-size:10.5px;text-transform:uppercase;letter-spacing:1.2px}
.mt-print-stage .dur{font-weight:700;font-size:11px;margin-top:2px;opacity:.95}
.mt-print-stage .bpm{font-weight:700;font-size:9.5px;margin-top:3px;opacity:.85;letter-spacing:.5px}
.mt-print-stage.veja,.mt-print-stage.diga{background:#3A3A3A;color:#E0E0E0}
.mt-print-stage.lento{background:var(--primary-dark)}
.mt-print-stage.click{background:var(--primary)}
.mt-print-info{font-family:var(--font-h);font-weight:700;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:10px;text-align:center}
.mt-print-info strong{color:var(--primary);font-size:13px;margin-left:6px}

/* ============ TIP ============ */
.tip{background:rgba(232,105,10,.08);border:1px solid rgba(232,105,10,.28);border-left:3px solid var(--primary);border-radius:8px;padding:12px 16px;margin:18px auto 6px;max-width:780px;font-size:13px;color:var(--text)}
.tip strong{color:var(--primary);font-family:var(--font-h);font-size:11px;text-transform:uppercase;letter-spacing:1.2px;display:block;margin-bottom:4px}

/* ============ INTERACTIVE: show one page at a time ============ */
body.mode-interactive .page{display:none}
body.mode-interactive .page.visible{display:block}
/* [v2.8] Páginas com layout flex precisam preservar display:flex ao ficar
   visíveis. A regra .page.visible{display:block} acima venceria
   `.<page-class>{display:flex}` por ter mais especificidade (3 classes vs 1) —
   o resultado era conteúdo "vazio" porque os filhos dependiam de flex
   (margin:0 auto, flex:1 1 auto, justify-content:center) pra se posicionar.
   Estas regras têm 3 classes (igual à de cima) e vêm DEPOIS na cascata. */
body.mode-interactive .cover.visible,
body.mode-interactive .howto-page.visible,
body.mode-interactive .practice-intro-page.visible,
body.mode-interactive .why-minutes-page.visible,
body.mode-interactive .practice-page.visible,
body.mode-interactive .mode-select-page.visible,
body.mode-interactive .tons-page.visible{display:flex}
/* [v88] practice-intro fixa ao viewport p/ não sofrer shrink-to-fit do body flex. */
/* [v130] display:flex column REIMPOSTO aqui — a regra anterior (linha
   de cima, mesma especificidade) já define display:flex, mas SEM
   flex-direction nem justify-content. Esta regra vem depois na cascata
   e fixa as props necessárias pro layout centralizado da v130.
   [v144] why-minutes-page replicada com mesmo tratamento. */
body.mode-interactive .practice-intro-page.visible,
body.mode-interactive .why-minutes-page.visible{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  position:fixed; inset:0; top:0; left:0; right:0; bottom:0; z-index:5;
}

/* ============ PRINT ============ */
@page{size:A4;margin:15mm}
@media print, body.mode-print{
  *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
}
body.mode-print{display:block}
body.mode-print .sidebar,body.mode-print .global-progress{display:none}
body.mode-print .page{padding:14mm 12mm;min-height:auto;page-break-after:always;break-after:page}
body.mode-print .cover{padding:50px 60px;min-height:267mm}
body.mode-print .cover h1{font-size:74px}
body.mode-print .mt-card{display:none}
body.mode-print .mt-print{display:block}
body.mode-interactive .mt-print{display:none}
@media print{
  .sidebar,.global-progress{display:none!important}
  .page{padding:0!important;min-height:auto!important;page-break-after:always;break-after:page;display:block!important}
  .main{margin-left:0!important}
  body{display:block!important}
  .mt-card{display:none!important}
  .mt-print{display:block!important}
}

/* ============ PWA: safe-area (iPhone notch / Dynamic Island) ============ */
@supports(padding:max(0px)){
  body{
    padding-top:env(safe-area-inset-top);
    padding-bottom:env(safe-area-inset-bottom);
    padding-left:max(var(--sal), env(safe-area-inset-left, 0px));
    padding-right:max(var(--sar), env(safe-area-inset-right, 0px));
  }
  .sidebar{
    padding-top:calc(20px + env(safe-area-inset-top));
    padding-bottom:calc(20px + env(safe-area-inset-bottom));
    padding-left:max(var(--sal), env(safe-area-inset-left, 0px));
  }
  .global-progress{top:env(safe-area-inset-top)}
}

/* ============ PWA: Install Banner ============ */
.pwa-banner{
  position:fixed;left:12px;right:12px;
  bottom:calc(12px + env(safe-area-inset-bottom));
  background:linear-gradient(135deg,#1A1A1A 0%,#0F0F0F 100%);
  border:1px solid #2E2E2E;border-left:3px solid var(--primary);
  border-radius:12px;padding:14px 16px;
  display:none;align-items:center;gap:12px;
  box-shadow:0 12px 40px rgba(0,0,0,.6),0 0 0 1px rgba(232,105,10,.10);
  z-index:9999;font-family:var(--font-b);
  animation:pwaBannerIn .35s ease-out;
}
.pwa-banner.show{display:flex}
@keyframes pwaBannerIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
.pwa-banner-icon{
  width:38px;height:38px;flex-shrink:0;border-radius:8px;
  background:linear-gradient(135deg,var(--primary) 0%,var(--primary-dark) 100%);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-h);font-weight:900;font-size:14px;color:#fff;letter-spacing:.5px;
}
.pwa-banner-text{flex:1;min-width:0}
.pwa-banner-title{font-family:var(--font-h);font-weight:800;font-size:12px;color:#fff;letter-spacing:.5px;margin-bottom:3px}
.pwa-banner-desc{font-size:11.5px;color:#999;line-height:1.45}
.pwa-banner-desc b{color:var(--primary);font-weight:700}
.pwa-banner-btn{
  background:var(--primary);color:#fff;border:none;
  padding:9px 14px;border-radius:8px;
  font-family:var(--font-h);font-weight:800;font-size:11px;
  letter-spacing:.5px;text-transform:uppercase;cursor:pointer;flex-shrink:0;
}
.pwa-banner-btn:active{background:var(--primary-dark)}
.pwa-banner-close{
  background:transparent;border:none;color:#666;font-size:22px;line-height:1;
  cursor:pointer;padding:4px 8px;flex-shrink:0;
}
.pwa-banner-close:active{color:#fff}
@media(max-width:480px){
  .pwa-banner{flex-wrap:wrap}
  .pwa-banner-text{flex-basis:calc(100% - 60px)}
  .pwa-banner-btn{margin-left:50px}
}

/* ============ [v546] INSTALL GATE — bloqueia a entrada no app pelo navegador ============ */
html.gate-on body > *:not(#install-gate){ visibility:hidden; }   /* sem flash do app atrás do gate */
#install-gate{ display:none; position:fixed; inset:0; z-index:2147483600;
  flex-direction:column; align-items:center; justify-content:center;
  background:radial-gradient(125% 90% at 50% 0%, #1a1a1a 0%, var(--bg) 55%, #050505 100%);
  padding:34px 26px calc(96px + env(safe-area-inset-bottom)); text-align:center;
  -webkit-user-select:none; user-select:none; }
html.gate-on #install-gate{ display:flex; }
.gate-card{ width:100%; max-width:380px; }
.gate-logo{ width:66px; height:66px; margin:0 auto 16px; border-radius:18px;
  background:linear-gradient(160deg,var(--primary),var(--primary-dark));
  display:flex; align-items:center; justify-content:center; box-shadow:0 14px 32px rgba(232,105,10,.38); }
.gate-brand{ font-family:var(--font-h); font-weight:900; font-size:19px; letter-spacing:2px; color:#fff; margin-bottom:14px; }  /* [v748] 28→14: abre espaço p/ path+piano sem estourar telas baixas */
.gate-brand span{ color:var(--primary); }
.gate-panel{ display:none; flex-direction:column; align-items:center; }
html.gate-ios #gate-ios{ display:flex; }
html.gate-android #gate-android{ display:flex; }
.gate-title{ font-family:var(--font-h); font-weight:800; font-size:23px; color:#fff; line-height:1.22; margin-bottom:10px; }
.gate-sub{ color:var(--muted); font-size:14.5px; line-height:1.5; margin-bottom:24px; max-width:330px; }
.gate-cta{ background:var(--primary); color:#fff; font-family:var(--font-h); font-weight:800; font-size:16px; letter-spacing:.4px;
  border:none; border-radius:14px; padding:16px 44px; cursor:pointer; box-shadow:0 12px 28px rgba(232,105,10,.42); }
.gate-cta:active{ background:var(--primary-dark); transform:translateY(1px); }
.gate-hint{ display:none; color:var(--dim); font-size:12.5px; line-height:1.5; margin-top:20px; max-width:300px; }
.gate-hint b{ color:var(--muted); font-weight:700; }
/* [v765] clique no "Instalar agora" SEM prompt nativo (incógnito/já instalado): hint pulsa =
   feedback visível a cada toque (antes o 2º clique em diante não fazia nada — botão "morto"). */
@keyframes gateHintPulse{ 0%{transform:scale(1)} 35%{transform:scale(1.07);color:var(--muted)} 100%{transform:scale(1)} }
.gate-hint.gh-pulse{ animation:gateHintPulse .5s ease; }
.gate-steps{ list-style:none; text-align:left; display:flex; flex-direction:column; gap:18px; margin:0 auto; padding:0; max-width:330px; width:-webkit-fit-content; width:fit-content; }
.gate-steps li{ display:flex; align-items:center; gap:13px; color:#ececec; font-size:15px; line-height:1.4; }
.gate-steps b{ color:var(--primary); font-weight:700; }
.gate-step-n{ flex:0 0 auto; width:30px; height:30px; border-radius:50%; background:var(--primary); color:#fff;
  font-family:var(--font-h); font-weight:800; font-size:15px; display:flex; align-items:center; justify-content:center; }
.gate-ic{ display:inline-flex; vertical-align:middle; }
/* [09/09 · SETA-1] A SETA DO COMPARTILHAR APONTA PARA ONDE O BOTAO ESTA DE VERDADE.
   Ate aqui a seta era UMA so: sempre em baixo, sempre no centro. Isso e certo em iPhone
   EM PE — e so ai. O Safari muda de sitio o grupo `Compartilhar / Marcadores / Abas`:
     · iPhone em pe .......... barra de BAIXO, 5 icones repartidos pela largura. O
                              Compartilhar e o 3o de 5, ou seja, o CENTRO. (o que ja havia)
     · iPhone deitado ........ a barra de baixo DESAPARECE e o grupo sobe para a barra de
                              cima, encostado a direita. A seta apontava para o vazio.
     · iPad (qualquer lado) .. barra unica em cima; o mesmo grupo a direita. Idem.
   A posicao vertical nao precisa de conta por modelo (Dynamic Island x botao Home): quem
   reserva o espaco da barra e o proprio Safari, e o fundo da viewport ja cai exatamente
   em cima dela. O que PRECISA de compensacao e o eixo X quando o alvo esta em cima e
   deitado — ai a ilha/entalhe come `env(safe-area-inset-right)` e empurra os icones para
   dentro. Por isso o X e uma variavel escrita por JS (--seta-x), medida, e nao um 50%.
   `seta-nenhuma` = navegador iOS que nao e o Safari: seta ESCONDIDA em vez de apontar
   errado (la o caminho nem e o Compartilhar). Sem JS nenhuma classe casa e vale o
   fallback de baixo/centro = o comportamento antigo, intocado. */
#gate-arrow{ display:none; position:fixed; left:50%; bottom:calc(6px + env(safe-area-inset-bottom)); transform:translateX(-50%);
  z-index:2147483601; pointer-events:none; animation:gateArrowB 1.15s ease-in-out infinite;
  flex-direction:column; align-items:center; gap:7px; }
html.gate-ios #gate-arrow{ display:flex; }
@keyframes gateArrowB{ 0%,100%{ transform:translateX(-50%) translateY(0); } 50%{ transform:translateX(-50%) translateY(11px); } }

/* alvo EM BAIXO (iPhone em pe): igual ao de sempre, mas com o X medido */
html.gate-ios.seta-baixo #gate-arrow{ display:flex; flex-direction:column; left:var(--seta-x,50%); top:auto;
  bottom:calc(6px + env(safe-area-inset-bottom)); animation:gateArrowB 1.15s ease-in-out infinite; }
/* alvo EM CIMA (iPhone deitado e iPad): a seta vira e sobe */
html.gate-ios.seta-topo #gate-arrow{ display:flex; flex-direction:column-reverse; left:var(--seta-x,50%); bottom:auto;
  top:calc(6px + env(safe-area-inset-top)); animation:gateArrowT 1.15s ease-in-out infinite; }
@keyframes gateArrowT{ 0%,100%{ transform:translateX(-50%) translateY(0); } 50%{ transform:translateX(-50%) translateY(-11px); } }
/* navegador iOS que nao e o Safari: nao apontar e melhor que apontar errado */
html.gate-ios.seta-nenhuma #gate-arrow{ display:none; }
/* so um dos dois desenhos vive de cada vez */
#gate-arrow .sa-topo{ display:none; }
html.gate-ios.seta-topo #gate-arrow .sa-topo{ display:block; }
html.gate-ios.seta-topo #gate-arrow .sa-baixo{ display:none; }
/* [09/09 · GATE-LIMPO] A PILULA "CLIQUE AQUI" NA SETA. A seta sozinha aponta mas nao
   MANDA fazer nada; a pilula e a mesma peca que o webview-gate ja usa (.wv-arrow-tap),
   aqui presa ao mesmo conjunto que faz o bounce, para nunca desalinhar da ponta. */
.ga-tap{ background:var(--primary); color:#fff; font-family:var(--font-h); font-weight:800;
  font-size:14.5px; letter-spacing:.2px; line-height:1; white-space:nowrap; padding:10px 17px; border-radius:999px;
  box-shadow:0 4px 16px rgba(0,0,0,.55), 0 0 0 3px rgba(232,105,10,.18); }

/* o aviso de "abra no Safari" so existe quando o navegador nao e o Safari */
#gate-ios-safari{ display:none; margin-top:18px; }
html.gate-ios.seta-nenhuma #gate-ios-safari{ display:block; }

/* ============ [v631] WEBVIEW GATE — gêmeo visual do install-gate, camada ACIMA (sai da IAB da Meta) ============ */
html.wv-on body > *:not(#webview-gate){ visibility:hidden; }   /* cobre TUDO, inclusive o install-gate — sem flash */
html.wv-on #install-gate{ display:none !important; }           /* não empilha os dois gates */
#webview-gate{ display:none; position:fixed; inset:0; z-index:2147483647;   /* teto de 32 bits: acima do install-gate (…600) */
  flex-direction:column; align-items:center; justify-content:center;
  background:radial-gradient(125% 90% at 50% 0%, #1a1a1a 0%, var(--bg) 55%, #050505 100%);
  padding:34px 26px calc(40px + env(safe-area-inset-bottom)); text-align:center;
  -webkit-user-select:none; user-select:none; }
/* visibility:visible !important vence a regra do install-gate (html.gate-on body > *:not(#install-gate){visibility:hidden}),
   que — com gate-on sempre coexistindo — ocultaria o próprio webview-gate por ele "não ser" o install-gate. */
html.wv-on #webview-gate{ display:flex; visibility:visible !important; }
.wv-panel{ display:none; flex-direction:column; align-items:center; }
html.wv-ios #wv-ios{ display:flex; }
html.wv-android #wv-android{ display:flex; }
.wv-cta{ background:var(--primary); color:#fff; font-family:var(--font-h); font-weight:800; font-size:16px; letter-spacing:.4px;
  border:none; border-radius:14px; padding:16px 44px; cursor:pointer; box-shadow:0 12px 28px rgba(232,105,10,.42); }
.wv-cta:active{ background:var(--primary-dark); transform:translateY(1px); }
.wv-skip{ display:inline-block; margin-top:26px; color:var(--dim); font-size:12.5px; text-decoration:underline;
  cursor:pointer; background:none; border:none; font-family:var(--font-b); padding:6px 10px; }
.wv-skip:active{ color:var(--muted); }
.wv-shot{ max-width:230px; width:72%; margin:6px auto 0; border-radius:12px; display:none; }  /* hook p/ print real do menu ⋯ (Luis manda depois → basta setar src) */
/* [v766] iOS: callout "clique aqui" → ⋯ REFORMULADO — pílula + seta CURVA num conjunto único (substitui os
   patches v749–v761: svg e texto rotacionados SEPARADOS nunca fechavam alinhamento). A curva SAI do centro-topo
   da pílula (x=66 do viewBox 132) e chega em diagonal (~38°) com a ponta a 22px da borda direita = pontinho
   CENTRAL do ⋯. Bounce na DIREÇÃO da ponta (↗), no conjunto inteiro — nada de peças soltas.
   [v768] right 6→13px (pedido Luis no device): conjunto inteiro 7px pra esquerda → ponta a 29px da borda,
   cravada no MEIO do ícone ⋯; geometria interna intocada. */
#wv-ios-arrow{ display:none; position:fixed; top:calc(8px + env(safe-area-inset-top)); right:13px; width:132px;
  z-index:2147483701; pointer-events:none; flex-direction:column; align-items:center;
  animation:wvArrowB 1.15s ease-in-out infinite; }
html.wv-ios #wv-ios-arrow{ display:flex; }
@keyframes wvArrowB{ 0%,100%{ transform:translate(0,0); } 50%{ transform:translate(4px,-7px); } }
#wv-ios-arrow svg{ display:block; }
.wv-arrow-tap{ margin-top:2px; background:var(--primary); color:#fff; font-family:var(--font-h); font-weight:800;
  font-size:13.5px; letter-spacing:.2px; line-height:1; white-space:nowrap; padding:9px 15px; border-radius:999px;
  box-shadow:0 4px 16px rgba(0,0,0,.55), 0 0 0 3px rgba(232,105,10,.18); }

/* ============ [v748·F1+F2] GATES CHAMATIVOS — path de progresso, piano vivo, mock do menu iOS, cascata ============ */
/* [v749] webview-gate: headline no lugar do logo+marca + "clique aqui" sob a seta */
.wv-headline{ font-family:var(--font-h); font-weight:900; font-size:27px; line-height:1.18; color:#fff;
  letter-spacing:-.3px; margin:0 0 20px; }
/* [v754] install-gate ANDROID espelha o wv-gate: headline no lugar de logo/marca, sem sub, respiro 36px.
   iOS mantém logo/marca/sub/passos (fluxo de instrução manual). Desktop cai no painel android = mesmo visual. */
#install-gate .gate-headline{ display:none; }
html.gate-android #install-gate .gate-logo, html.gate-android #install-gate .gate-brand{ display:none; }
html.gate-android #install-gate .gate-headline{ display:block; margin-bottom:36px; }
html.gate-android #install-gate .gate-path{ margin-bottom:36px; }
html.gate-android #install-gate .gate-piano{ margin:0 0 36px; }
html.gate-android #install-gate .gate-piano svg{ max-width:280px; }
html.gate-android #gate-install-btn{ margin-top:30px; }
/* [v756] install-android: pílula única (par do wv-android) — fonte do título, branco, sem "Instalar grátis" */
html.gate-android #gate-install-btn{ font-weight:900; font-size:21px; letter-spacing:-.2px; padding:18px 48px; }
/* [v751] sem sub/tópicos embaixo → redistribui o respiro vertical (não sobra vazio no rodapé) */
#webview-gate .wv-headline{ margin-bottom:36px; }
#webview-gate .gate-path{ margin-bottom:36px; }
#webview-gate .gate-piano{ margin:0 0 36px; }
#webview-gate .gate-piano svg{ max-width:280px; }
#webview-gate .wv-action{ line-height:1.28; }
html.wv-android #wv-open-btn{ margin-top:30px; }
/* [v755] wv-android: sem título — a pílula carrega a ação com a fonte do título (900), texto branco */
html.wv-android #wv-open-btn{ font-weight:900; font-size:21px; letter-spacing:-.2px; padding:18px 48px; }
/* [v750] título dos painéis wv na MESMA fonte da headline (900/27px), <b> laranja via .gate-title b */
.wv-action{ font-weight:900; font-size:27px; letter-spacing:-.3px; line-height:1.18; }
/* [v750] sub do wv-gate: maior e LARANJA (os <b> ficam brancos pra contraste); só no webview-gate */
#webview-gate .gate-sub{ font-size:16.5px; color:var(--primary); }
#webview-gate .gate-sub b{ color:#fff; }
/* [v750] tópicos 1–2 do wv-ios: fonte maior + mais respiro vertical */
#wv-ios .gate-steps{ gap:22px; }
#wv-ios .gate-steps li{ font-size:16.5px; line-height:1.55; }
/* Ênfase nos títulos/subs (copy nova usa <b> via data-i18n-html) */
.gate-title b{ color:var(--primary); font-weight:800; }
.gate-sub b{ color:#fff; font-weight:700; }

/* Caminho de 3 passos (Instagram → Navegador → App) */
.gate-path{ display:flex; align-items:flex-start; justify-content:center; margin:0 0 18px; }
.gp-step{ display:flex; flex-direction:column; align-items:center; gap:5px; min-width:62px; }
.gp-dot{ width:26px; height:26px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  font-family:var(--font-h); font-weight:800; font-size:13px; color:var(--dim); background:#1d1d1d; border:1.5px solid #333; }
.gp-lbl{ font-size:10.5px; letter-spacing:.4px; color:var(--dim); font-weight:600; text-transform:uppercase; }
.gp-done .gp-dot{ background:rgba(232,105,10,.16); border-color:var(--primary); color:var(--primary); }
.gp-done .gp-lbl{ color:var(--muted); }
.gp-now .gp-dot{ background:var(--primary); border-color:var(--primary); color:#fff; animation:gpPulse 1.6s ease-out infinite; }
.gp-now .gp-lbl{ color:#fff; }
.gp-line{ flex:0 0 24px; height:1.5px; background:#333; margin-top:12px; }
.gp-line.gp-on{ background:var(--primary); opacity:.55; }
@keyframes gpPulse{ 0%{ box-shadow:0 0 0 0 rgba(232,105,10,.55); } 70%{ box-shadow:0 0 0 11px rgba(232,105,10,0); } 100%{ box-shadow:0 0 0 0 rgba(232,105,10,0); } }

/* Piano vivo: varredura L→R das teclas brancas (CSS puro, GPU-barato; brancas = filhos 1–8 do svg) */
.gate-piano{ margin:0 0 20px; }
.gate-piano svg{ display:block; width:100%; max-width:240px; margin:0 auto; }
.gate-piano rect.gpk{ fill:#f2f2f2; animation:gpkLit 2.6s linear infinite; }
.gate-piano rect.gpb{ fill:#101010; }
@keyframes gpkLit{ 0%{ fill:#f2f2f2; } 3%,9%{ fill:var(--primary); } 15%,100%{ fill:#f2f2f2; } }
.gate-piano rect.gpk:nth-child(1){ animation-delay:0s; }
.gate-piano rect.gpk:nth-child(2){ animation-delay:.16s; }
.gate-piano rect.gpk:nth-child(3){ animation-delay:.32s; }
.gate-piano rect.gpk:nth-child(4){ animation-delay:.48s; }
.gate-piano rect.gpk:nth-child(5){ animation-delay:.64s; }
.gate-piano rect.gpk:nth-child(6){ animation-delay:.8s; }
.gate-piano rect.gpk:nth-child(7){ animation-delay:.96s; }
.gate-piano rect.gpk:nth-child(8){ animation-delay:1.12s; }

/* Mock do menu ⋯ do IG (iOS): sheet escura, linha-alvo destacada com anel pulsante */
.wv-menu-mock{ width:100%; max-width:270px; margin:16px auto 0; background:#232326; border-radius:14px; overflow:hidden;
  text-align:left; box-shadow:0 18px 40px rgba(0,0,0,.5); }
.wvm-row{ display:flex; align-items:center; gap:12px; padding:13px 16px; border-top:1px solid rgba(255,255,255,.07); }
.wvm-row:first-child{ border-top:none; }
.wvm-ln{ height:9px; border-radius:5px; background:#3a3a3e; }
.wvm-ln.w60{ width:60%; }
.wvm-ln.w45{ width:45%; }
.wvm-hot{ background:rgba(232,105,10,.14); position:relative; }
.wvm-hot .wvm-open{ color:#fff; font-size:14.5px; font-weight:600; }
.wvm-compass{ font-size:16px; line-height:1; }
.wvm-hot::after{ content:''; position:absolute; inset:3px; border:2px solid var(--primary); border-radius:11px;
  animation:wvmRing 1.6s ease-out infinite; pointer-events:none; }
@keyframes wvmRing{ 0%{ opacity:1; } 60%{ opacity:.25; } 100%{ opacity:1; } }

/* Cascata de entrada (opacity/transform APENAS — visibility fica fora de animação por regra iOS) */
/* [v753] :not(#gate-arrow): a seta do Compartilhar é filha FIXED do card — o fill:both da cascata
   terminava em transform:none e APAGAVA o translateX(-50%) da centralização (e o bounce próprio dela) */
html.gate-on #install-gate .gate-card > *:not(#gate-arrow), html.wv-on #webview-gate .gate-card > *{
  animation:gateIn .55s cubic-bezier(.22,.7,.3,1) both; }
#install-gate .gate-card > :nth-child(2), #webview-gate .gate-card > :nth-child(2){ animation-delay:.06s; }
#install-gate .gate-card > :nth-child(3), #webview-gate .gate-card > :nth-child(3){ animation-delay:.12s; }
#install-gate .gate-card > :nth-child(4), #webview-gate .gate-card > :nth-child(4){ animation-delay:.18s; }
#install-gate .gate-card > :nth-child(5), #webview-gate .gate-card > :nth-child(5){ animation-delay:.24s; }
#install-gate .gate-card > :nth-child(6), #webview-gate .gate-card > :nth-child(6){ animation-delay:.3s; }
#install-gate .gate-card > :nth-child(7), #webview-gate .gate-card > :nth-child(7){ animation-delay:.36s; }
@keyframes gateIn{ from{ opacity:0; transform:translateY(16px); } to{ opacity:1; transform:none; } }

/* Telas baixas (iPhone SE etc.): o piano cede primeiro, margens apertam — nada estoura */
@media(max-height:700px){
  .gate-piano{ display:none; }
  .gate-path{ margin-bottom:14px; }
  .gate-logo{ width:54px; height:54px; margin-bottom:10px; }
}
@media (prefers-reduced-motion: reduce){
  .gate-piano rect.gpk, .gp-now .gp-dot, .wvm-hot::after,
  html.gate-on #install-gate .gate-card > *, html.wv-on #webview-gate .gate-card > *{ animation:none; }
}

/* [v767] ANDROID (wv + install): headline ↔ botão TROCADOS de lugar (pedido Luis — mesmas posições,
   invertidas). CSS-only via flex order (iOS INTOCADO: painéis iOS nem entram nas regras .wv-android/
   .gate-android). Card vira flex column (stretch = larguras de block preservadas; #gate-arrow é FIXED,
   fora do fluxo). Margens ESPELHAM os slots: o botão no topo herda o mb:36 da headline (via margin do
   painel; mt:30 do botão zerado) e a headline embaixo herda o gap extra de 30 que o botão tinha.
   Cascata de entrada re-mapeada (delays eram por nth-child = ordem de DOM): topo anima primeiro. */
html.wv-android #webview-gate .gate-card{ display:flex; flex-direction:column; }
html.wv-android #webview-gate #wv-android{ order:-1; margin-bottom:36px; animation-delay:0s; }
html.wv-android #webview-gate .wv-headline{ order:1; margin:30px 0 0; animation-delay:.18s; }
html.wv-android #wv-open-btn{ margin-top:0; }
html.gate-android #install-gate .gate-card{ display:flex; flex-direction:column; }
html.gate-android #install-gate #gate-android{ order:-1; margin-bottom:36px; animation-delay:.12s; }
html.gate-android #install-gate .gate-headline{ order:1; margin:30px 0 0; animation-delay:.3s; }
html.gate-android #gate-install-btn{ margin-top:0; }

/* [v792] iOS (wv): as DUAS frases grandes TROCADAS de lugar (pedido Luis — "Instale agora" em cima,
   "Seu professor de IA está aqui" embaixo). Mesmo mecanismo do v767 no Android: CSS-only via flex
   order, markup intocado. As margens espelham os slots (o painel no topo herda o mb:36 da headline;
   a headline embaixo herda o respiro de 30) e a cascata de entrada é re-mapeada, porque os delays
   originais são por nth-child = ordem de DOM. Android INTOCADO (regras separadas por classe). */
html.wv-ios #webview-gate .gate-card{ display:flex; flex-direction:column; }
html.wv-ios #webview-gate #wv-ios{ order:-1; margin-bottom:36px; animation-delay:0s; }
html.wv-ios #webview-gate .wv-headline{ order:1; margin:30px 0 0; animation-delay:.24s; }

/* ============ [09/09 · GATE-LIMPO] O INSTALL-GATE DO iOS FICA COM DUAS COISAS ============
 * Ordem do Luis: *"pagina poluida visualmente. deixe apenas o teclado animado e 2 passos
 * (remova o terceiro). bem grande e limpo. coloque um clique aqui na seta animada"*.
 *
 * O QUE SAI DA TELA (por CSS, nao do DOM — o markup continua a servir os outros dois
 * painis do MESMO gate): o logo, a marca, o caminho Instagram→Navegador→App, o titulo e
 * o sub. Eram seis blocos a competir pela atencao numa tela cujo unico trabalho e fazer
 * a pessoa tocar no Compartilhar. O 3o passo ("feche o navegador") saiu do DOM.
 *
 * 🔑 POR QUE POR CLASSE E NAO POR APAGAR: o `#install-gate` serve TRES painis conforme a
 * classe do <html> — `gate-ios` (este), `gate-android` (headline + botao) e
 * `gate-installed` ("Instalado!", que usa o logo e a marca). Apagar o logo/marca do markup
 * limparia esta tela e esvaziaria a de quem ja instalou. Por isso tudo aqui esta preso a
 * `html.gate-ios`, e os outros dois caminhos ficam byte a byte como estavam.
 *
 * O TECLADO PASSA A SER OBRIGATORIO. A regra antiga `@media(max-height:700px){ .gate-piano
 * { display:none } }` fazia sentido quando o piano era o 5o item de uma pilha que estourava
 * em iPhone SE; agora ele e METADE do que existe na tela, e some-lo deixava a tela vazia.
 * Nas telas baixas ele encolhe, nao desaparece. */
html.gate-ios #install-gate .gate-logo,
html.gate-ios #install-gate .gate-brand,
html.gate-ios #install-gate .gate-path,
html.gate-ios #install-gate #gate-ios > .gate-title,
html.gate-ios #install-gate #gate-ios > .gate-sub{ display:none !important; }

/* o teclado, grande e sozinho no topo */
html.gate-ios #install-gate .gate-piano{ margin:0 0 44px; }
html.gate-ios #install-gate .gate-piano svg{ max-width:300px; }

/* os 2 passos, bem grandes */
html.gate-ios #install-gate .gate-steps{ gap:30px; max-width:100%; width:100%; }
html.gate-ios #install-gate .gate-steps li{ font-size:22px; line-height:1.32; gap:16px; align-items:flex-start; }
html.gate-ios #install-gate .gate-step-n{ width:42px; height:42px; font-size:20px; margin-top:-2px; }
html.gate-ios #install-gate .gate-ic svg{ width:21px; height:25px; margin-bottom:-5px !important; }

/* telas baixas (iPhone SE): o teclado ENCOLHE, nunca some — e agora metade da tela */
@media(max-height:700px){
  html.gate-ios #install-gate .gate-piano{ display:block; margin:0 0 30px; }
  html.gate-ios #install-gate .gate-piano svg{ max-width:230px; }
  html.gate-ios #install-gate .gate-steps{ gap:22px; }
  html.gate-ios #install-gate .gate-steps li{ font-size:19px; }
  html.gate-ios #install-gate .gate-step-n{ width:36px; height:36px; font-size:17px; }
}

/* ============ RESPONSIVE MOBILE (vertical optimization) ============ */
@media(max-width:900px){
  html{font-size:15px}
  .page{padding:64px 18px calc(28px + env(safe-area-inset-bottom))}   /* 64 top deixa espaço pro botão hamburguer; [v633] +safe-area no bottom (era 28px puro → barra preta/corte no standalone iOS) */
  .page-header{margin-bottom:20px;padding-bottom:10px;flex-wrap:wrap;gap:6px}
  .page-header .brand{font-size:10.5px;letter-spacing:1.5px}
  .page-header .pn{font-size:9.5px}

  /* Cover — flow vertical natural; tagline + título + prose + tags + piano */
  .cover{padding:64px 18px calc(86px + env(safe-area-inset-bottom));min-height:100vh;height:100dvh;max-height:100dvh;justify-content:flex-start;gap:18px;overflow:hidden}
  .cover-top{flex:0 0 auto;padding-top:6px;padding-bottom:6px}
  .cover-mid{flex:0 0 auto;gap:18px;padding-bottom:8px}
  .cover h1{font-size:52px;letter-spacing:-1.5px;line-height:1}
  .cover-tagline{font-size:15px;max-width:100%;margin:14px auto 0}
  .cover-prose{gap:12px;max-width:100%}
  .cover-prose p{font-size:14px;line-height:1.6}

  /* [v97] Tags em mobile — full-width, mas mantém o tamanho premium das tags
     (já que ocupam o espaço antes do piano virtual). */
  .cover-tags{flex:0 0 auto;gap:14px;max-width:100%}
  .cover-tag{padding:18px 18px;gap:16px}
  .ct-icon{font-size:26px;width:46px;height:46px}
  .ct-num{font-size:34px}
  .ct-lbl{font-size:14px}

  /* Special pages */
  .special h1{font-size:28px;padding-left:14px;border-left-width:3px}
  .special .lead{font-size:14px;margin-left:18px}
  .special h3{font-size:16px;margin:18px 0 8px}
  .special p,.special li{font-size:13.5px}
  .callout{padding:14px 16px;margin:14px 0}
  .callout p{font-size:12.5px}

  /* Sidebar mais larga em mobile (até 78% da tela, máx 320px) */
  .sidebar{width:min(78vw,320px)}

  /* Nav arrows menores e mais perto da borda */

  /* Tabelas: scroll horizontal pra não espremer */
  table{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;white-space:nowrap;max-width:100%}

  /* Grids genéricos: stacking em mobile */
  [class*="grid"]:not(.no-stack){grid-template-columns:1fr!important}

  /* SVG do teclado: ocupa toda largura */
  svg{max-width:100%;height:auto}
}

@media(max-width:480px){
  html{font-size:14.5px}
  .page{padding:60px 14px calc(24px + env(safe-area-inset-bottom))}   /* [v633] +safe-area no bottom (idem @900px) */
  .cover{padding:76px 18px calc(82px + env(safe-area-inset-bottom))}
  .cover h1{font-size:40px;letter-spacing:-1px}
  .cover-tagline{font-size:13.5px}
  .cover-prose p{font-size:13.5px;line-height:1.55}
  .cover-tag{padding:10px 12px}
  .ct-num{font-size:18px}
  .ct-lbl{font-size:12.5px}
  .special h1{font-size:24px}
}

/* ============================================================
   [v72] SISTEMA DE ANIMAÇÕES DE ENTRADA — premium e elegante
   - Toda página visível ganha fade+lift sutil
   - Elementos-chave entram em stagger (cascata)
   - Curva: cubic-bezier(.2,.8,.2,1) — ease-out pesado, refinado
   - prefers-reduced-motion respeitado
   ============================================================ */
@keyframes ttPageEnter{
  from{ opacity:0; transform:translateY(10px) }
  to  { opacity:1; transform:translateY(0) }
}
@keyframes ttElemUp{
  from{ opacity:0; transform:translateY(14px) }
  to  { opacity:1; transform:translateY(0) }
}
@keyframes ttElemFade{
  from{ opacity:0 }
  to  { opacity:1 }
}
@keyframes ttElemBarDown{
  from{ opacity:0; transform:translateY(-12px) }
  to  { opacity:1; transform:translateY(0) }
}
@keyframes ttElemScale{
  from{ opacity:0; transform:scale(.96) }
  to  { opacity:1; transform:scale(1) }
}

body.mode-interactive .page.visible{
  animation: ttPageEnter .42s cubic-bezier(.2,.8,.2,1);
}
/* [06/09 · A VIRADA É UMA COISA SÓ] companheira da regra de cima, no mesmo lugar e
   com a mesma força: a página que chega por uma VIRADA do aparelho não anima a
   entrada. Quem faz a transição ali é o véu (`#tt-giro-veu`) — a página por baixo
   dele já está pronta e cheia, e uma entrada de 420 ms por cima disso só devolvia
   a tela apagada que se está a curar. A marca sai só quando a página deixa de estar
   visível (showPage): tirá-la com a página no ar DISPARARIA a animação. */
body.mode-interactive .page.visible.tt-sem-entrada{ animation:none!important; }

/* A CORTINA DA VIRADA. [06/09b] Era um quadrado do lado maior do aparelho, em px de
   `screen`, CENTRADO no quadro. O tamanho estava certo; a ÂNCORA não: durante a
   rotação o quadro já é o novo e o `left:50%` ainda é lido no velho — num 390×844
   que vira 844×390 o centro erra 227 px e sobra faixa por cobrir do lado direito.
   Agora são os quatro lados a `calc(-100% - 1200px)` do PRÓPRIO quadro: o quadro
   inteiro mais 1200 px de folga de cada lado, sempre ancorada nele, e sem uma medida
   escrita pelo JS. (Só `-100%` não bastava: se a percentagem ainda for lida na caixa
   velha, 390 dá borda a 780 e o quadro novo já pede 844.)
   Continua sem `vw/vh/vmax` — as unidades de viewport são exatamente o que mente
   durante a rotação (os quadros 844×303 e 390×590 medidos na bancada). O JS repete
   estas declarações INLINE, para que uma folha em cache não devolva a cortina velha.
   z-index 99995: acima das molduras do click/erro (99990/99991), da tela "vire o
   celular" (9998) e do splash (9999); MUITO abaixo dos portões/paywall (2147483…),
   que ela nunca pode tapar. */
#tt-giro-veu{
  position:fixed; transform:none;
  left:calc(-100% - 1200px); top:calc(-100% - 1200px);
  right:calc(-100% - 1200px); bottom:calc(-100% - 1200px);
  z-index:99995; pointer-events:none; opacity:0; background:#070707;
}
@media (prefers-reduced-motion: reduce){
  #tt-giro-veu{ transition:none!important; }
}

/* [v473] SAÍDA de página = fade-out por cima (crossfade com a entrada da próxima).
   A página que sai vira position:fixed (fora do fluxo) p/ não empilhar com a nova; a
   classe .visible é removida na hora (seletores '.page.visible' continuam vendo só 1
   página) e o display é preservado por estas regras. showPage tira .page-leaving ao fim. */
@keyframes ttPageExit{
  from{ opacity:1 }
  to  { opacity:0 }
}
body.mode-interactive .page.page-leaving{
  /* [v583] A página que SAI fica EM FLUXO (position:relative do .page) e SÓ faz fade NO LUGAR — NÃO vira mais
     position:fixed. A página VISÍVEL nunca é reposicionada → acaba o "1 frame deslocando pra cima" do iOS
     (criar a camada fixed + reposicionar o pino nunca era atômico: black/see-through/top-jump/transform-jump
     eram todos o mesmo desync). Quem fica FIXED agora é a que ENTRA, mas só ENQUANTO INVISÍVEL (.tt-enter-defer
     abaixo) — fixed nela é imperceptível (opacity:0) e a tira do fluxo p/ não empurrar a que sai. No reveal a
     que entra volta a relative + scrollTo(0,0) e nasce no topo. */
  display:block; pointer-events:none;
  animation: ttPageExit .26s cubic-bezier(.2,.8,.2,1) both;
}
body.mode-interactive .cover.page-leaving,
body.mode-interactive .howto-page.page-leaving,
body.mode-interactive .practice-intro-page.page-leaving,
body.mode-interactive .why-minutes-page.page-leaving,
body.mode-interactive .practice-page.page-leaving,
body.mode-interactive .mode-select-page.page-leaving,
/* 🔴 [06/08 · L9 item 09] o EDITOR faltava nesta lista: com display:block no fade
   de saída a coluna flex dele colapsava e a figura armada da RODA pintava solta no
   meio da tela por ~0,26s — era o "bug visual de uma figura rítmica" na virada
   Finalizar→folha. Mesma classe do gotcha de 29/07 (".page.special mata o flex"). */
body.mode-interactive .mp-edit-page.page-leaving,
body.mode-interactive .tons-page.page-leaving{ display:flex }
@media (prefers-reduced-motion: reduce){
  body.mode-interactive .page.page-leaving{ display:none!important; animation:none!important }
}

/* Stagger pras páginas especiais — elementos-chave em cascata.
   Não toca em .cover-tag (já tem coverTagIn) nem .pi-cta (já tem piCtaPulse). */
body.mode-interactive .cover.visible .cover-top,
body.mode-interactive .cover.visible .cover-mid,
body.mode-interactive .cover.visible .cover-tags,
body.mode-interactive .cover.visible .cover-piano-wrap{
  animation: ttElemUp .60s cubic-bezier(.2,.8,.2,1) backwards;
}
body.mode-interactive .cover.visible .cover-top      { animation-delay:.06s; }
body.mode-interactive .cover.visible .cover-mid      { animation-delay:.16s; }
body.mode-interactive .cover.visible .cover-tags     { animation-delay:.24s; }
body.mode-interactive .cover.visible .cover-piano-wrap{ animation-delay:.34s; }

body.mode-interactive .practice-intro-page.visible .pi-title,
body.mode-interactive .practice-intro-page.visible .pi-tags,
body.mode-interactive .why-minutes-page.visible .pi-title,
body.mode-interactive .why-minutes-page.visible .pi-tags{
  animation: ttElemUp .58s cubic-bezier(.2,.8,.2,1) backwards;
}
body.mode-interactive .practice-intro-page.visible .pi-title,
body.mode-interactive .why-minutes-page.visible .pi-title { animation-delay:.06s; }
body.mode-interactive .practice-intro-page.visible .pi-tags,
body.mode-interactive .why-minutes-page.visible .pi-tags  { animation-delay:.20s; }
/* CTA: fade-in mas mantém piCtaPulse — combinamos na shorthand. */
body.mode-interactive .practice-intro-page.visible .pi-cta{
  animation: ttElemFade .55s ease-out backwards .50s, piCtaPulse 2.4s ease-in-out infinite 1.20s;
}

body.mode-interactive .tons-page.visible h1,
body.mode-interactive .tons-page.visible .lead,
body.mode-interactive .tons-page.visible .tn-card{
  animation: ttElemUp .55s cubic-bezier(.2,.8,.2,1) backwards;
}
body.mode-interactive .tons-page.visible h1   { animation-delay:.06s; }
body.mode-interactive .tons-page.visible .lead{ animation-delay:.16s; }
body.mode-interactive .tons-page.visible .tn-card{ animation-delay: calc(.20s + var(--idx, 0) * .05s); }

body.mode-interactive .toc-page.visible h1,
body.mode-interactive .toc-page.visible .lead,
body.mode-interactive .toc-page.visible .toc-tier-card,
body.mode-interactive .toc-page.visible .toc-grid{
  animation: ttElemUp .55s cubic-bezier(.2,.8,.2,1) backwards;
}
body.mode-interactive .toc-page.visible h1            { animation-delay:.06s; }
body.mode-interactive .toc-page.visible .lead         { animation-delay:.14s; }
body.mode-interactive .toc-page.visible .toc-tier-card{ animation-delay:.22s; }
body.mode-interactive .toc-page.visible .toc-grid     { animation-delay:.30s; }

body.mode-interactive .unit-intro.visible h1,
body.mode-interactive .unit-intro.visible .lead,
body.mode-interactive .unit-intro.visible > *:not(h1):not(.lead){
  animation: ttElemUp .55s cubic-bezier(.2,.8,.2,1) backwards;
}
body.mode-interactive .unit-intro.visible h1   { animation-delay:.06s; }
body.mode-interactive .unit-intro.visible .lead{ animation-delay:.16s; }
body.mode-interactive .unit-intro.visible > *:not(h1):not(.lead){ animation-delay:.26s; }

/* Practice page: barra desce do topo, piano fade-in atrás. */
body.mode-interactive .practice-page.visible header.practice-bar{
  animation: ttElemBarDown .50s cubic-bezier(.2,.8,.2,1) backwards;
  animation-delay:.05s;
}
body.mode-interactive .practice-page.visible .kbd-wrap{
  animation: ttElemFade .60s ease-out backwards;
  animation-delay:.18s;
}

/* [v514] TRANSIÇÃO SEQUENCIAL (Luis): a página inteira SAI (cada elemento no seu tempo) → INSTANTE só com
   o FUNDO ESTELAR → a nova ENTRA (cada elemento no seu tempo). NÃO é crossfade: a nova entra .visible mas
   fica INVISÍVEL via .tt-enter-defer ENQUANTO a anterior sai; o showPage tira o defer (~600ms) só DEPOIS da
   saída terminar → nunca há conteúdo das 2 páginas sobreposto. Páginas não listadas mantêm o ttPageExit. */
@keyframes ttElemUpOut{ from{opacity:1;transform:translateY(0) scale(1)} to{opacity:0;transform:translateY(-16px) scale(.985)} }
@keyframes ttElemFadeOut{ from{opacity:1} to{opacity:0} }
@keyframes ttElemBarUpOut{ from{opacity:1;transform:translateY(0)} to{opacity:0;transform:translateY(-14px)} }

/* [v583] nova página ADIADA: invisível, SEM animar E `position:fixed` (cobre o viewport mas FORA do fluxo →
   não empurra/desloca a página que está saindo, que segue em fluxo). É ESTA que carrega o custo de virar
   fixed — mas como está em opacity:0, o eventual "1 frame na posição errada" do iOS é invisível. No reveal
   o showPage tira o defer → volta a relative (em fluxo, no topo, scroll já zerado) e roda o ttPageEnter. */
body.mode-interactive .page.visible.tt-enter-defer{ position:fixed!important; inset:0!important; opacity:0!important; pointer-events:none!important; animation:none!important; overflow:hidden; }
body.mode-interactive .page.visible.tt-enter-defer *{ animation:none!important; }

/* [v581] SAÍDA = fade-out da PÁGINA INTEIRA (ttPageExit em .page.page-leaving, lá em cima). Removido o
   esquema por-elemento (ttElemUpOut + os overrides `animation:none`): ele só animava h1/.lead e deixava os
   cards/conteúdo parados em opacity:1 até a remoção seca aos 600ms → a nova página entrava POR TRÁS e
   aparecia "através" da que saía. Agora a página inteira dissolve uniforme, confiável, e a nova só entra
   depois (timer fixo _LEAVE_MS no showPage). Mantida a ENTRADA escalonada (ttElemUp, .visible, acima). */

@media (prefers-reduced-motion: reduce){
  body.mode-interactive .page.visible.tt-enter-defer{ opacity:1!important; }   /* sem respiro longo p/ reduced-motion */
  body.mode-interactive .page.page-leaving [class]{ animation:none!important; }
}

/* Howto: cada slide já tem howtoSlideIn ao virar — mantemos só a entrada da página. */

@media (prefers-reduced-motion: reduce){
  body.mode-interactive .page.visible,
  body.mode-interactive .cover.visible .cover-top,
  body.mode-interactive .cover.visible .cover-mid,
  body.mode-interactive .cover.visible .cover-tags,
  body.mode-interactive .cover.visible .cover-piano-wrap,
  body.mode-interactive .practice-intro-page.visible .pi-title,
  body.mode-interactive .practice-intro-page.visible .pi-tags,
  body.mode-interactive .practice-intro-page.visible .pi-cta,
  body.mode-interactive .why-minutes-page.visible .pi-title,
  body.mode-interactive .why-minutes-page.visible .pi-tags,
  body.mode-interactive .tons-page.visible h1,
  body.mode-interactive .tons-page.visible .lead,
  body.mode-interactive .tons-page.visible .tn-card,
  body.mode-interactive .toc-page.visible h1,
  body.mode-interactive .toc-page.visible .lead,
  body.mode-interactive .toc-page.visible .toc-tier-card,
  body.mode-interactive .toc-page.visible .toc-grid,
  body.mode-interactive .unit-intro.visible h1,
  body.mode-interactive .unit-intro.visible .lead,
  body.mode-interactive .unit-intro.visible > *:not(h1):not(.lead),
  body.mode-interactive .practice-page.visible header.practice-bar,
  body.mode-interactive .practice-page.visible .kbd-wrap{
    animation: none!important;
  }
}

/* ============================================================
   SPLASH DE ABERTURA — animação cinematográfica de 2.5s
   [v2.16] RESTAURADO INTEGRALMENTE da v1 PWA (commit local 7 Mai 00:15).
   Substitui a tentativa minimalista da v2.14.
   ============================================================ */
.splash{
  position:fixed;inset:0;z-index:9999;
  /* [v2.31] HIDDEN por default. Splash.init() adiciona .is-active pra mostrar.
     Sem isso, quando o usuário já completou o tutorial (HOWTO_SEEN_KEY=1) e
     reabre o app, o splash ficava visível atrás (sem piano renderizado, pois
     Splash.init() não roda) — travando o usuário numa tela morta. */
  display:none;flex-direction:column;align-items:center;justify-content:center;
  gap:0;padding:48px 32px;text-align:center;
  background:
    radial-gradient(ellipse 60% 50% at 50% 20%, rgba(232,105,10,.28) 0%, transparent 60%),
    radial-gradient(ellipse 70% 60% at 50% 95%, rgba(232,105,10,.18) 0%, transparent 60%),
    #0A0A0A;
  overflow:hidden;
  -webkit-tap-highlight-color:transparent;
  transition:opacity .42s cubic-bezier(.4,0,.2,1);
  will-change:opacity;
}
/* [v2.31] Só mostra quando JS ativa explicitamente (Splash.init). */
.splash.is-active{display:flex}
/* [v80] Lock de viewport durante splash — elimina shift visual do URL bar do iOS */
body.splash-active{ overflow:hidden!important; height:100vh; height:100dvh; touch-action:none; overscroll-behavior:none; }  /* [v535] vh fallback */
/* Brilho sutil orbitando atrás do conteúdo */
.splash::before{
  content:"";position:absolute;inset:-20%;
  background:radial-gradient(circle at 50% 50%, rgba(232,105,10,.10) 0%, transparent 45%);
  filter:blur(40px);
  animation: splashGlow 6s ease-in-out infinite alternate;
  pointer-events:none;
}
@keyframes splashGlow{
  0%  {transform:translate(-3%,-1%) scale(1)}
  100%{transform:translate( 3%, 1%) scale(1.06)}
}
/* Linha decorativa horizontal acima do bloco — "régua de marca" */
.splash-rule{
  position:relative;width:0;height:1px;
  background:linear-gradient(90deg, transparent, rgba(232,105,10,.55), transparent);
  margin-bottom:34px;opacity:0;
}
.splash.running .splash-rule{
  animation: splashRule .60s cubic-bezier(.2,.8,.2,1) 0s forwards;
}
@keyframes splashRule{
  0%  {opacity:0;width:0}
  100%{opacity:1;width:min(280px,70vw)}
}
/* Bloco texto */
.splash-stack{
  position:relative;display:flex;flex-direction:column;align-items:center;
}
.splash-method{
  font-family:var(--font-h);font-weight:300;
  font-size:clamp(28px,7vw,52px);
  color:#FFFFFF;letter-spacing:.32em;text-transform:uppercase;
  opacity:0;transform:translateY(14px);
  text-shadow:0 0 24px rgba(232,105,10,.10);
  margin-right:-.32em;
}
.splash.running .splash-method{
  animation: splashFadeUp .65s cubic-bezier(.2,.8,.2,1) .05s forwards;
}
.splash-brand{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(46px,12vw,108px);
  line-height:.95;letter-spacing:-2px;
  margin-top:6px;
  opacity:0;transform:translateY(18px) scale(.98);
  background:linear-gradient(180deg, #FFFFFF 0%, #FFFFFF 55%, var(--primary-2) 95%, var(--primary) 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  filter: drop-shadow(0 6px 32px rgba(232,105,10,.22));
}
.splash.running .splash-brand{
  animation: splashBrandIn .70s cubic-bezier(.2,.8,.2,1) .15s forwards;
}
.splash-brand .tt-orange{
  -webkit-text-fill-color: var(--primary);
  background:none;
  text-shadow:0 0 28px rgba(232,105,10,.45);
}
/* Subtítulo elegante */
.splash-tag{
  font-family:var(--font-h);font-weight:400;font-style:italic;
  font-size:clamp(11px,2.6vw,15px);
  color:var(--muted);letter-spacing:.20em;text-transform:uppercase;
  margin-top:16px;opacity:0;
}
.splash.running .splash-tag{
  animation: splashFadeUp .60s cubic-bezier(.2,.8,.2,1) .30s forwards;
}
/* Logomarca */
.splash-logo-wrap{
  margin-top:clamp(40px,7vh,72px);
  position:relative;display:flex;flex-direction:column;align-items:center;gap:14px;
  opacity:0;transform:translateY(10px) scale(.92);
}
.splash.running .splash-logo-wrap{
  animation: splashLogoIn .70s cubic-bezier(.2,.8,.2,1) .45s forwards;
}
.splash-logo{
  width:clamp(74px,12vw,108px);height:auto;
  border-radius:22%;
  box-shadow:
    0 0 0 1px rgba(232,105,10,.30),
    0 12px 36px rgba(232,105,10,.30),
    0 0 60px rgba(232,105,10,.18);
  background:#0A0A0A;
}
.splash-logo-bar{
  width:0;height:2px;border-radius:2px;
  background:linear-gradient(90deg, transparent, var(--primary) 30%, var(--primary-2) 50%, var(--primary) 70%, transparent);
  box-shadow:0 0 10px rgba(232,105,10,.55);
}
.splash.running .splash-logo-bar{
  animation: splashBar .70s cubic-bezier(.2,.8,.2,1) .60s forwards;
}
@keyframes splashBar{
  0%  {width:0;opacity:0}
  100%{width:min(160px,40vw);opacity:1}
}
.splash-logo-name{
  font-family:var(--font-h);font-weight:800;font-size:clamp(10px,2.4vw,12.5px);
  color:#FFFFFF;letter-spacing:.32em;text-transform:uppercase;
  opacity:0;
}
.splash.running .splash-logo-name{
  animation: splashFadeUp .55s cubic-bezier(.2,.8,.2,1) .70s forwards;
}
.splash-logo-name b{color:var(--primary);font-weight:900;letter-spacing:.32em}
@keyframes splashFadeUp{
  to{opacity:1;transform:translateY(0)}
}
@keyframes splashBrandIn{
  to{opacity:1;transform:translateY(0) scale(1)}
}
@keyframes splashLogoIn{
  to{opacity:1;transform:translateY(0) scale(1)}
}
/* === FASES (piano → marca) === */
.splash-phase{
  position:absolute;inset:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:48px 16px;
  transition:opacity .55s cubic-bezier(.4,0,.2,1), transform .55s cubic-bezier(.4,0,.2,1);
}
.splash-phase-piano{opacity:1;transform:scale(1)}
.splash-phase-brand{opacity:0;transform:scale(.985);pointer-events:none}
.splash.chord-played .splash-phase-piano{opacity:0;transform:scale(.97);pointer-events:none}
.splash.running       .splash-phase-brand{opacity:1;transform:scale(1)}

/* === CTA "Toque qualquer tecla para começar" === */
.splash-cta-text{
  font-family:var(--font-h);font-weight:800;
  font-size:clamp(13px,2.6vw,16px);letter-spacing:.28em;
  color:var(--primary);text-transform:uppercase;
  margin-bottom:clamp(28px,5vh,52px);
  text-align:center;
  text-shadow:0 0 18px rgba(232,105,10,.45);
  animation: splashCtaTextPulse 2.2s ease-in-out infinite;
}
.splash-cta-text strong{color:#FFFFFF;font-weight:900;letter-spacing:.18em}
@keyframes splashCtaTextPulse{
  0%,100%{opacity:.85;transform:translateY(0)}
  50%   {opacity:1;   transform:translateY(-2px)}
}

/* === PIANO SPLASH (estilo idêntico ao kbd-svg das páginas de exercício) === */
.splash-piano-wrap{
  position:relative;
  width:100%;
  padding:0 clamp(28px, 7vw, 64px);
  display:flex;justify-content:center;
  filter:drop-shadow(0 18px 38px rgba(0,0,0,.55)) drop-shadow(0 0 32px rgba(232,105,10,.20));
}
.splash-piano{
  position:relative;
  width:100%;
  max-width:680px;
  /* [v429] aspect-ratio AQUI (não só no .kbd-svg) reserva a altura do piano já no
     1º paint, ANTES de Splash.init() injetar o <svg>. Sem isso o container ficava com
     altura 0 até a injeção; como o .splash é flex-column + justify-content:center, a
     coluna crescia quando o SVG entrava e RE-CENTRALIZAVA tudo → o piano "se deslocava
     e depois se encaixava". Com a altura reservada desde o início, injetar o SVG não
     muda o layout (zero shift). Casa com o .kbd-svg (também 25/7, viewBox 750/210). */
  aspect-ratio:25/7;
  border-radius:6px;
  overflow:visible;
  user-select:none;-webkit-user-select:none;
  -webkit-tap-highlight-color:transparent;
}
.splash-piano .kbd-svg{cursor:pointer;width:100%;height:auto;aspect-ratio:25/7;display:block;border-radius:6px;overflow:hidden} /* [v421] aspect-ratio (750/210 do viewBox de 2 oitavas) reserva a altura no 1º paint → mata o "zoom gigante" intermitente do SVG inline no iOS */
.splash-piano .kbd-key{cursor:pointer;transition:fill .06s linear, stroke .06s linear, filter .12s ease-out}
.splash-piano .kbd-key.kbd-w:hover{fill:#D6D6D6}
.splash-piano .kbd-key.kbd-b:hover{fill:#000000}
/* [v2.16] cobertura pra v2 que usa data-kind ao invés de classe .kbd-w/.kbd-b */
.splash-piano .kbd-svg rect[data-kind="w"]:hover{fill:#D6D6D6}
.splash-piano .kbd-svg rect[data-kind="b"]:hover{fill:#000000}
/* Anel de glow sutil indicando que o piano todo é interativo */
.splash-piano-wrap::after{
  content:"";position:absolute;left:50%;bottom:-14px;transform:translateX(-50%);
  width:38%;max-width:240px;height:2px;border-radius:2px;
  background:linear-gradient(90deg, transparent, rgba(232,105,10,.6), transparent);
  filter:blur(.5px);
  animation: spGlowSweep 2.6s ease-in-out infinite;
}
@keyframes spGlowSweep{
  0%,100%{opacity:.35;transform:translateX(-65%) scaleX(.6)}
  50%   {opacity:1;  transform:translateX(-35%) scaleX(1.1)}
}

/* Flash radial ao tocar uma tecla */
.splash.chord-played .splash-piano-wrap::before{
  content:"";position:absolute;inset:-20% 0;
  background:radial-gradient(ellipse 60% 80% at 50% 50%, rgba(232,105,10,.55) 0%, transparent 60%);
  animation: spPianoFlash .55s ease-out forwards;
  pointer-events:none;
}
@keyframes spPianoFlash{
  0%  {opacity:0}
  20% {opacity:1}
  100%{opacity:0}
}

/* === Saída === */
.splash.fade-out{opacity:0;pointer-events:none}
.splash.removed{display:none}
@media (prefers-reduced-motion: reduce){
  .splash-rule,.splash-method,.splash-brand,.splash-tag,
  .splash-logo-wrap,.splash-logo-bar,.splash-logo-name{
    animation-duration:.001s !important;
  }
  .splash::before,.splash-cta-text{animation:none}
}
body.mode-print .splash{display:none}

/* ============ v2.21 — reenquadramento dos slides A, B, C, D ============
 * Títulos GRANDES (clamp ~30-72px), em 1 linha onde couber, com white-space:
 * nowrap. Espaçamento vertical IGUALMENTE distribuído pra cobrir toda a
 * viewport sem scroll: justify-content:space-between (A, B, C) ou
 * space-around (D). Reverte os overrides v2.15 que ENCOLHIAM padding/gap. */

/* [v265] Regras do slide "Navegação / Interface Interativa" REMOVIDAS
   junto com o próprio slide. */

/* B — Slide "Protocolo" → "Cada série, 2 minutos" (1 linha) */
.howto-slide[data-step="protocolo"] .howto-title{
  font-size: clamp(28px, 8vw, 60px);
  white-space: nowrap;
  letter-spacing: -1.4px;
}
.howto-slide[data-step="protocolo"] .howto-lead{
  padding: clamp(10px, 2.5vh, 22px) 0;
  margin-bottom: 0;
}
/* [v2.22] Slide tem 3 caixas (.howto-protocol + .howto-tip + .howto-extend).
   gap proporcional + padding-bottom evita que a última (.howto-extend) cole
   no bottom da viewport. */
.howto-slide[data-step="protocolo"] .howto-content{
  justify-content: flex-start;
  gap: clamp(8px, 1.5vh, 14px);
  padding: 4px 0 clamp(12px, 2.5vh, 24px);
}
.howto-slide[data-step="protocolo"] .howto-extend{
  margin-bottom: 0;
}

/* C — Slide "Tocando" → "O piano responde a você" (2 linhas, fonte maior) */
.howto-slide[data-step="tocando"] .howto-title{
  font-size: clamp(36px, 8vw, 60px);
  letter-spacing: -1.4px;
}
.howto-slide[data-step="tocando"] .howto-lead{
  padding: clamp(10px, 2.5vh, 22px) 0;
  margin-bottom: 0;
}
.howto-slide[data-step="tocando"] .howto-content{
  /* [v109] flex-start (era space-between) — empilha tiles + frase + CTA no topo
     com gaps controlados, sem distribuição que afastava o CTA. */
  justify-content: flex-start;
  gap: clamp(10px, 2vh, 16px);
  padding: clamp(8px, 2vh, 14px) 0;
}
.howto-slide[data-step="tocando"] .howto-task-instruction{
  margin-bottom: 4px;
}
.howto-slide[data-step="tocando"] .howto-fs-cta{
  padding: 11px 22px;
  font-size: 12px;
  letter-spacing: .14em;
  gap: 8px;
  margin-top: 0;
}
/* [v233] Native: CTA elevado, ancorado logo abaixo da 3ª tile (verde),
   com todo o espaço livre EMBAIXO dele (longe dos nav-dots). v231 tentou
   centralizar mas ficou muito perto dos dots; agora margin-top pequeno
   garante proximidade com a tile verde. */
body[data-practice-mode="native"] .howto-slide[data-step="tocando"] .howto-fs-cta{
  margin-top: 0;
  margin-bottom: 0;
}
.howto-slide[data-step="tocando"] .howto-fs-cta svg{
  width: 12px; height: 12px;
}
/* [v227] Variante companion do slide tocando: o CTA estava quase saindo
   do viewport e os nav-dots tampavam parte dele. Solução:
   1) Ocultar os dots só nesse slide (carrossel ainda funciona via swipe/teclado).
   2) Encurtar o padding top/bottom pra mover conteúdo pra cima e dar
      respiro ao CTA.
   IMPORTANTE: a media query mobile (≤480px) já reduz `.howto-slide`
   padding-top pra 48px FLAT (sem env). Então o override pra mobile não
   pode adicionar env(), senão acaba empurrando pra baixo (bug v226). */
body[data-practice-mode="companion"] .howto-page:has(.howto-slide.active[data-step="tocando"]) .howto-dots{
  display: none;
}
body[data-practice-mode="companion"] .howto-slide[data-step="tocando"]{
  padding-top: calc(40px + env(safe-area-inset-top));
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
}
@media (max-width:480px){
  body[data-practice-mode="companion"] .howto-slide[data-step="tocando"]{
    padding-top: 32px;
    padding-bottom: calc(14px + env(safe-area-inset-bottom));
  }
}
/* [v221] Variante companion: nas duas tags inferiores (Toque no teclado)
   o texto era left-align e ficava desalinhado com o
   visual da 1ª tag. Centralizamos texto + ícone só nessas duas. */
body[data-practice-mode="companion"] .howto-slide[data-step="tocando"] .howto-tile:nth-child(n+2){
  text-align: center;
  align-items: center;
}
body[data-practice-mode="companion"] .howto-slide[data-step="tocando"] .howto-tile:nth-child(n+2) .ic{
  align-self: center;
}
@media (max-width:520px){
  body[data-practice-mode="companion"] .howto-slide[data-step="tocando"] .howto-tile:nth-child(n+2){
    text-align: center;
  }
  body[data-practice-mode="companion"] .howto-slide[data-step="tocando"] .howto-tile:nth-child(n+2) .ic{
    align-self: center;
  }
}

/* [remoção timbres] CSS do slide "Experimente" (3 timbres) removido. */

/* F — [v92] Slide "Caminho" → "A ordem inteligente" (2 linhas, título grande,
   12 cards 4×3 ocupando 100% da largura horizontal, legenda explicativa) */
.howto-slide[data-step="caminho"]{
  /* Expande além do max-width:680px padrão pra cards ocuparem largura total */
  max-width:none;
  padding-left: clamp(12px, 4vw, 32px);
  padding-right: clamp(12px, 4vw, 32px);
}
.howto-slide[data-step="caminho"] .howto-title{
  font-size: clamp(40px, 9vw, 68px);
  letter-spacing: -1.6px;
  /* line-height < 1 cola as 2 linhas: "A ordem" + "inteligente" sem gap vazio */
  line-height: .92;
  margin-bottom: 0;
}
.howto-slide[data-step="caminho"] .howto-lead{
  padding: clamp(8px, 1.8vh, 14px) 0;
  margin-bottom: 0;
  max-width:680px;
}
.howto-slide[data-step="caminho"] .howto-content{
  /* [v98] flex-start (era space-between) → legenda fica logo abaixo da última fileira
     de cards em vez de ser empurrada pro fundo da viewport. Sem gap pendurado. */
  justify-content: flex-start;
  gap: clamp(10px, 1.5vh, 16px);
  padding: clamp(6px, 1.5vh, 14px) 0;
  width: 100%;
}

/* [v147] Regra removida — .timbre-host já é display:none por padrão. */

/* E — TOC "A jornada completa"
   [v107] Título aumentado mais — 44-80 → 56-104. Letter-spacing apertado.
   [v128] FIX trava de tamanho — `.special h1` (0,1,1) vencia o
   `.toc-mega-title` (0,1,0) que tentava resetar `border-left:none` +
   `padding-left:0` e setar font-size via clamp. Resultado: título ficava
   travado em 42px (do .special h1) com a barra laranja imposta.
   Solução: `.toc-page .toc-mega-title` tem specificidade (0,2,0), vence
   `.special h1` sem `!important`. Border laranja eliminada + tamanho
   aumentado pra clamp(68-124px). */
.toc-page .toc-mega-title{
  /* [v223] título um pouco menor pra dar mais respiro com a tag INICIANTE. */
  font-size:clamp(58px, 13vw, 104px);
  letter-spacing:-2px;
  border-left:none;
  padding-left:0;
  margin:0;
}
/* [i18n] título do TOC em EN/ES é mais longo → no piso de 58px estoura p/ 3 linhas em telas estreitas.
   Reduz O MÍNIMO p/ caber em 2 linhas (quebra forçada no i18n.js); responsivo por largura (clamp), maior
   nos iPhones grandes. Verificado 360→430px (margem ~15-20px). ES só reduz abaixo de ~390px. */
.lang-en .toc-page .toc-mega-title{ font-size:clamp(44px, 12.5vw, 54px); }
.lang-es .toc-page .toc-mega-title{ font-size:clamp(48px, 15vw, 58px); }

/* ====================================================================
   [v136] DESKTOP / Mac / PC — adaptação para mouse-driven devices
   em monitores ≥ 1024px de largura.

   Por que `pointer:fine` + `min-width:1024px`:
   - `pointer:fine` MATCHES: mouse, trackpad. EXCLUI: touch screens.
   - `min-width:1024px` exclui qualquer iPhone em qualquer orientação
     (iPhone 12 Pro Max em landscape = 844px), iPad portrait (768-810px),
     iPad mini landscape (1024 — borderline, mas pointer:coarse o exclui).

   Isto GARANTE zero impacto no iPhone 12 Pro Max — o device validado
   até v135 — preservando 100% do design mobile que o usuário aprovou.

   Estratégia: full-screen com max-width nos elementos. Páginas portrait
   (cover, intro, howto, toc, unit-intro, tons) confiam nos max-widths
   INTERNOS já existentes (cover-prose 600px, pi-tagboxes 620px, etc.) +
   um background gradient discreto que preenche o "respiro" do desktop.
   Practice-page é o único que precisa cap explícito de proporção pra não
   virar um piano absurdamente largo em monitores grandes.
   ==================================================================== */
@media (pointer: fine) and (min-width: 1024px) {

  /* Background ambient — preenche a área desktop com gradiente sutil
     laranja/preto. Sem alterar bg das páginas internas (cover.visible,
     etc. têm seus próprios gradientes locais). */
  body.mode-interactive{
    background:
      radial-gradient(ellipse 80% 60% at 50% 0%,   rgba(232,105,10,.06) 0%, transparent 60%),
      radial-gradient(ellipse 60% 60% at 50% 100%, rgba(232,105,10,.04) 0%, transparent 60%),
      #060606;
  }

  /* Practice page: cap em proporção "celular landscape" (~1100×620)
     pra evitar piano gigante em monitores 1920×1080+. Truque de
     centralizar fixed: `inset:0 + margin:auto + max-w/h`. Bordas
     suaves + sombra elevam o "frame" sem virar moldura de simulador. */
  body.mode-interactive .practice-page.visible{
    inset: 0;
    margin: auto;
    max-width:  1100px;
    max-height:  620px;
    border: 1px solid rgba(232,105,10,.18);
    border-radius: 16px;
    overflow: hidden;
    box-shadow:
      0 24px 64px rgba(0,0,0,.55),
      0 0 0 1px rgba(232,105,10,.06) inset;
  }

  /* Cap suave nas demais páginas portrait — segura layout legível em
     monitores ultra-wide. Páginas continuam full-viewport, mas o conteúdo
     interno mantém max-widths já configurados; o gradient ambient do
     body preenche o resto. */

  /* Wrapper opcional pra max-width "respiro" — se quiser limitar bg
     gradient da página interna, adicionar aqui. Por padrão, deixamos
     full-bleed pra aproveitar o bg ambient. */
}

/* ============ [v146] MODE SELECT PAGE ============ */
.mode-select-page{
  display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
  width:100vw;height:100vh;height:100dvh;min-height:100vh;min-height:100dvh;box-sizing:border-box;overflow-y:auto;-webkit-overflow-scrolling:touch;
  padding:
    calc(env(safe-area-inset-top) + clamp(76px,15vh,160px))
    clamp(20px,4vw,52px)
    calc(40px + env(safe-area-inset-bottom));
  gap:clamp(30px,6vh,64px);
  /* [22/07 · Luis] "fundo estrelado INTACTO e persistente, igual à experiência."
     A PMS foi portada do experiencia.html no [v573] quando o app-pai AINDA NÃO
     tinha a constelação global (#c) — por isso ela pintava o PRÓPRIO fundo (glows
     laranja + um linear-gradient terminando em var(--bg) SÓLIDO). Agora o #c é
     persistente e fica ATRÁS de todas as telas: aquele véu COBRIA/apagava as
     estrelas no reveal (o "embaça" do Luis). A .ms-page da experiência não tem
     fundo nenhum — vive sobre o #c. Igualado 1:1: transparente, #c intocado.
     (A ambientação laranja continua vindo dos próprios cards — .ms-card--kbd/screen.) */
  background:transparent;
}
.ms-like .ms-title{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(36px,9vw,72px);letter-spacing:-2px;line-height:1.06;
  color:var(--text);text-align:center;margin:0;flex-shrink:0;
}
.ms-like .ms-title span{ color:var(--primary); }
/* 🔴 [29/07] QUEBRA DE LINHA EQUILIBRADA, não à mão.
   O título virou uma PERGUNTA (mais longa que o "Selecione o modo de prática"
   que estava ali), e o <br> que eu tinha cravado deixava "seu" sozinho numa
   linha — visto no print, não na sonda. Com 8 idiomas de comprimentos
   diferentes, cravar quebra é errar em sete. `text-wrap:balance` distribui as
   linhas sozinho; onde não houver suporte, cai na quebra normal, que é o que
   já acontecia antes de eu pôr o <br>. */
.ms-like .ms-title{ text-wrap:balance; }
.ms-like .ms-cards{
  display:flex;flex-direction:column;gap:clamp(14px,2.6vh,22px);
  width:100%;max-width:480px;align-items:stretch;
}
.ms-like .ms-card{
  width:100%;display:flex;flex-direction:row;align-items:center;gap:clamp(16px,4vw,24px);
  padding:clamp(20px,4.5vw,28px) clamp(20px,5vw,30px);border-radius:18px;cursor:pointer;text-align:left;color:#fff;
  position:relative;overflow:hidden;border:1.5px solid var(--border);margin:0;
  -webkit-appearance:none;appearance:none;
  transition:transform .2s cubic-bezier(.2,.8,.2,1),box-shadow .2s,border-color .2s;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation;font:inherit;
}
.ms-like .ms-card-text{display:flex;flex-direction:column;gap:5px;min-width:0;flex:1 1 auto}
.ms-like .ms-card--kbd{
  border-color:rgba(232,105,10,.50);
  background:radial-gradient(ellipse 100% 130% at 18% 18%,rgba(232,105,10,.22) 0%,rgba(232,105,10,.06) 50%,transparent 74%),rgba(16,10,6,.55);
  box-shadow:0 16px 44px rgba(0,0,0,.50);
}
.ms-like .ms-card--screen{
  border-color:rgba(100,200,255,.42);
  background:radial-gradient(ellipse 100% 130% at 18% 18%,rgba(100,200,255,.16) 0%,rgba(100,200,255,.05) 50%,transparent 74%),rgba(8,14,20,.55);
  box-shadow:0 16px 44px rgba(0,0,0,.50);
}
@media (hover:hover) and (pointer:fine){
  .ms-like .ms-card--kbd:hover{ transform:translateY(-3px); border-color:rgba(232,105,10,.75); box-shadow:0 22px 54px rgba(0,0,0,.6),0 0 34px rgba(232,105,10,.20); }
  .ms-like .ms-card--screen:hover{ transform:translateY(-3px); border-color:rgba(100,200,255,.70); box-shadow:0 22px 54px rgba(0,0,0,.6),0 0 34px rgba(100,200,255,.18); }
}
.ms-like .ms-card:active{ transform:scale(.975); }
.ms-like .ms-ic{flex-shrink:0;width:clamp(46px,11vw,60px);height:clamp(46px,11vw,60px);display:flex;align-items:center;justify-content:center}
.ms-like .ms-ic svg{width:100%;height:100%}
.ms-like .ms-card--kbd .ms-ic{color:var(--primary-2)}
.ms-like .ms-card--screen .ms-ic{color:#64C8FF}
.ms-like .ms-card-title{font-family:var(--font-h);font-weight:900;font-size:clamp(22px,5.5vw,32px);line-height:1.05;letter-spacing:-.6px;margin:0}
.ms-like .ms-card--kbd .ms-card-title{color:var(--primary-2)}
.ms-like .ms-card--screen .ms-card-title{color:#64C8FF}
.ms-like .ms-card-body{font-family:var(--font-h);font-weight:700;font-size:clamp(13px,3.4vw,16px);line-height:1.4;color:rgba(255,255,255,.80);margin:0}

/* ============ [v146] COMPANION INSTRUCTION PAGE ============ */
.companion-instruction-page{
  display:flex;flex-direction:column;align-items:center;
  justify-content:center;
  text-align:center;
  width:100vw;height:100vh;height:100dvh;min-height:100vh;min-height:100dvh;  /* [v535] vh fallback */
  box-sizing:border-box;overflow:hidden;
  padding:
    calc(env(safe-area-inset-top) + clamp(28px,4.5vh,52px))
    clamp(28px,6vw,88px)
    calc(env(safe-area-inset-bottom) + clamp(28px,4.5vh,52px));
  background:
    radial-gradient(ellipse 65% 50% at 80% 0%,  rgba(34,197,94,.08) 0%, transparent 60%),
    radial-gradient(ellipse 55% 45% at 20% 100%, rgba(34,197,94,.06) 0%, transparent 60%),
    var(--bg);
}
/* Wrapper central — distribui título, tags e botão com gap */
.ci-body{
  display:flex;flex-direction:column;align-items:center;
  gap:clamp(20px,3.8vh,36px);
  width:100%;max-width:520px;
}
/* Título 2 linhas — mesmo tratamento tipográfico do splash-brand */
.ci-title{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(36px,8vw,60px);line-height:1.08;
  margin:0;letter-spacing:-1.4px;
  text-align:center;
}
.ci-title-main{
  display:block;
  background:linear-gradient(180deg,#FFFFFF 0%,rgba(255,255,255,.75) 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
}
.ci-title-em{
  display:block;
  background:linear-gradient(180deg,#6EE7A0 0%,#4ADE80 55%,#22C55E 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  filter:drop-shadow(0 4px 18px rgba(34,197,94,.50));
}
/* Container das tags */
.ci-tags{
  display:flex;flex-direction:column;
  gap:clamp(14px,2.2vh,22px);
  width:100%;text-align:left;
}
/* Tag base — visual inspirado na .cover-tag, adaptado ao tema verde */
.ci-tag{
  position:relative;
  display:flex;align-items:center;gap:clamp(14px,2vw,18px);
  padding:clamp(20px,3vh,28px) clamp(20px,3.2vw,30px);
  border-radius:16px;
  background:
    linear-gradient(180deg,rgba(255,255,255,.04) 0%,rgba(255,255,255,0) 30%),
    radial-gradient(ellipse 95% 140% at 0% 0%,rgba(34,197,94,.18) 0%,rgba(34,197,94,.06) 40%,transparent 72%),
    linear-gradient(135deg,rgba(20,34,26,.94) 0%,rgba(14,20,16,.94) 100%);
  border:1.5px solid rgba(34,197,94,.38);
  box-shadow:
    0 10px 28px rgba(0,0,0,.45),
    0 0 0 1px rgba(34,197,94,.10) inset,
    0 1px 0 rgba(255,255,255,.08) inset;
  overflow:hidden;
  font-family:var(--font-h);
  font-weight:600;
  font-size:clamp(13px,1.8vw,16px);
  line-height:1.5;
  color:rgba(255,255,255,.88);
  transition:border-color .22s, box-shadow .22s, transform .22s;
}
.ci-tag::before{
  content:"";position:absolute;inset:-1px;border-radius:inherit;
  background:linear-gradient(120deg,transparent 35%,rgba(34,197,94,.16) 50%,transparent 65%);
  opacity:0;pointer-events:none;
  transition:opacity .35s;
}
.ci-tag > *{ position:relative;z-index:1; }
@media(hover:hover) and (pointer:fine){
  .ci-tag:not(.ci-tag--link):hover{
    border-color:rgba(34,197,94,.70);
    box-shadow:0 14px 40px rgba(34,197,94,.18),0 0 0 1px rgba(34,197,94,.14) inset,0 1px 0 rgba(255,255,255,.08) inset;
    transform:translateY(-2px);
  }
  .ci-tag:not(.ci-tag--link):hover::before{ opacity:1; }
}
/* Ícone badge — estilo .ct-icon adaptado ao tema verde */
.tag-icon-box{
  flex-shrink:0;
  width:clamp(42px,5.5vw,50px);height:clamp(42px,5.5vw,50px);
  border-radius:11px;
  background:rgba(34,197,94,.12);
  border:1px solid rgba(34,197,94,.32);
  box-shadow:0 0 14px rgba(34,197,94,.22) inset;
  display:flex;align-items:center;justify-content:center;
  color:#4ADE80;
}
/* Tag clicável (catálogo) — [v222] herda layout flex de .ci-tag (ícone à
   esquerda em .tag-icon-box, texto ao lado). justify-content removido
   porque a seta diagonal agora vive dentro de .tag-icon-box junto aos
   outros ícones, não mais como ícone solto à direita. */
.ci-tag--link{
  cursor:pointer;width:100%;box-sizing:border-box;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation;
  transition:border-color .15s,transform .15s;
}
.ci-tag--link:active{ opacity:.7; }
@media (hover:hover) and (pointer:fine){
  .ci-tag--link:hover{ border-color:rgba(34,197,94,.65);transform:translateY(-1px); }
}
/* CTA — uma linha, botão mais fino */
.ci-btn{
  flex-shrink:0;
  font-family:var(--font-h);font-weight:800;
  font-size:clamp(11px,1.5vw,14px);letter-spacing:.04em;
  text-transform:uppercase;white-space:nowrap;
  color:rgba(0,0,0,.82);background:#4ADE80;border:none;border-radius:999px;
  padding:clamp(14px,2vh,18px) clamp(28px,4.5vw,48px);
  width:100%;max-width:480px;
  cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;
  box-shadow:0 8px 28px rgba(34,197,94,.40), 0 0 0 1px rgba(34,197,94,.20) inset;
  transition:transform .15s,box-shadow .15s;text-align:center;
}
@media (hover:hover) and (pointer:fine){
  .ci-btn:hover{ transform:translateY(-2px); box-shadow:0 12px 36px rgba(34,197,94,.55); }
}
.ci-btn:active{ transform:scale(.97); }

/* [v151] Modal de confirmação — sair pro catálogo */
.catalog-confirm-modal{
  position:fixed;inset:0;z-index:800;
  display:none;align-items:center;justify-content:center;
  padding:20px;
  padding-top:calc(20px + env(safe-area-inset-top,0px));
  padding-bottom:calc(20px + env(safe-area-inset-bottom,0px));
}
.catalog-confirm-modal.is-open{
  display:flex;
  animation:ccmFadeIn .2s ease-out;
}
@keyframes ccmFadeIn{from{opacity:0}to{opacity:1}}
.ccm-backdrop{
  position:absolute;inset:0;
  background:rgba(0,0,0,.7);
  backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
}
.ccm-card{
  position:relative;
  background:var(--card);
  border:1px solid var(--border2);
  border-radius:16px;
  padding:32px 24px;
  max-width:360px;width:100%;
  text-align:center;
  box-shadow:0 20px 60px rgba(0,0,0,.6);
  animation:ccmSlideUp .25s cubic-bezier(.2,.8,.2,1);
}
@keyframes ccmSlideUp{from{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}
/* [v508] SAÍDA animada do modal (espelho de ccmFadeIn/ccmSlideUp) — antes sumia seco no display:none */
/* [21/07 · item 9] popup de escolha da levada */
.lvm-lista{display:flex;flex-direction:column;gap:8px;margin:14px 0 4px}
.lvm-opt{display:flex;flex-direction:column;gap:2px;text-align:left;cursor:pointer;
  padding:12px 14px;border-radius:12px;background:rgba(255,255,255,.04);
  border:1px solid var(--border2);border-left:3px solid var(--accent,var(--primary));
  transition:background .14s var(--e-out,ease),border-color .14s var(--e-out,ease)}
.lvm-opt strong{font-family:var(--font-h);font-weight:800;font-size:15px;color:var(--text)}
.lvm-opt em{font-style:normal;font-family:var(--font-b);font-size:12.5px;color:var(--muted)}
.lvm-opt:active{transform:scale(.99)}
@media (hover:hover) and (pointer:fine){ .lvm-opt:hover{background:rgba(255,255,255,.07)} }
.lvm-fonte{font-size:12.5px;color:var(--muted);margin-top:8px}
/* ============================================================
 * [21/07 · Luis] MODO MINHA MÚSICA — card por acorde, 1 por linha
 * ⚠️ A textarea das cifras JÁ usa .se-chords. Este container é .se-cards de
 * propósito: colidir a classe aplicava o flex do container NA CAIXA DE TEXTO.
 * ========================================================= */
.se-sub{ font-family:var(--font-h);font-weight:800;font-size:12px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);margin:16px 0 10px; }

/* [21/07 · Luis] 3 SUB-MODOS. Cada um tem cor própria — não são variações do
 * mesmo caminho, são caminhos diferentes, e a cor é o que comunica isso antes
 * da leitura. Verde = pronto pra tocar · âmbar = o professor decide por você ·
 * azul = você decide (é a cor que o "monte sua harmonia" já tinha). */
.se-modos{ display:flex;flex-direction:column;gap:10px;margin:16px 0 4px; }
.se-modo{
  --m: var(--muted);
  display:flex;align-items:center;gap:12px;width:100%;text-align:left;cursor:pointer;
  padding:14px 16px;border-radius:12px;background:rgba(18,18,18,.34);
  border:1px solid var(--border2);border-left:3px solid var(--m);
  transition:background .14s var(--e-out,ease),border-color .14s var(--e-out,ease);
}
.se-modo[data-song-modo="simples"]  { --m:#22C55E; }
.se-modo[data-song-modo="professor"]{ --m:#F59E0B; }
.se-modo[data-song-modo="montar"]   { --m:#3B82F6; }
.se-modo-txt{ display:flex;flex-direction:column;gap:3px;min-width:0; }
.se-modo-txt strong{ font-family:var(--font-h);font-weight:900;font-size:15.5px;
  letter-spacing:-.2px;color:var(--text); }
.se-modo-txt em{ font-style:normal;font-family:var(--font-b);font-size:12.5px;
  color:var(--muted);line-height:1.35; }
.se-modo.is-on{ background:color-mix(in srgb, var(--m) 14%, rgba(18,18,18,.34));
  border-color:var(--m); }
.se-modo.is-on .se-modo-txt strong{ color:var(--m); }
.se-modo:active{ transform:scale(.995); }
@media (hover:hover) and (pointer:fine){ .se-modo:hover{ background:rgba(255,255,255,.06) } }

/* nos modos automáticos o card vira LEITURA: sem chevron, sem cursor de clique */
.se-cards.is-readonly .se-cc-head{ cursor:default; }
.se-cards.is-readonly .se-cc-chev{ display:none; }
.se-cards.is-readonly .se-cc-head:active{ transform:none; }
/* a cor do card acompanha o sub-modo ativo */
.song-edit-page[data-modo-ativo="simples"]   .se-cc-head{ border-left-color:#22C55E; }
.song-edit-page[data-modo-ativo="simples"]   .se-cc-atual em{ color:#22C55E; }
.song-edit-page[data-modo-ativo="professor"] .se-cc-head{ border-left-color:#F59E0B; }
.song-edit-page[data-modo-ativo="professor"] .se-cc-atual em{ color:#F59E0B; }
.song-edit-page[data-modo-ativo="montar"]    .se-cc-head{ border-left-color:#3B82F6; }
.song-edit-page[data-modo-ativo="montar"]    .se-cc-atual em{ color:#3B82F6; }
.se-cards{ display:flex;flex-direction:column;gap:10px;margin:0 0 6px; }
.se-cc{ position:relative;width:100%; }
.se-cc-head{
  width:100%;display:flex;align-items:center;gap:12px;text-align:left;cursor:pointer;
  padding:14px 16px;border-radius:12px;background:rgba(18,18,18,.34);
  backdrop-filter:blur(9px) saturate(1.1);-webkit-backdrop-filter:blur(9px) saturate(1.1);
  border:1px solid var(--border2);border-left:3px solid var(--accent,var(--primary));
  transition:background .14s var(--e-out,ease),border-color .14s var(--e-out,ease);
}
.se-cc-head:active{ transform:scale(.995); }
@media (hover:hover) and (pointer:fine){ .se-cc-head:hover{ background:rgba(255,255,255,.06) } }
.se-cc-cifra{ flex:0 0 auto;min-width:62px;font-family:var(--font-h);font-weight:900;
  font-size:24px;letter-spacing:-.5px;color:var(--text); }
.se-cc-atual{ flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px; }
.se-cc-atual em{ font-style:normal;font-family:var(--font-h);font-weight:800;font-size:11px;
  letter-spacing:.10em;text-transform:uppercase;color:var(--accent,var(--primary)); }
.se-cc-notas{ font-family:var(--font-b);font-size:13px;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
.se-cc-chev{ flex:0 0 auto;color:var(--muted);font-size:13px;
  transition:transform .18s var(--e-out,ease); }
.se-cc-head[aria-expanded="true"] .se-cc-chev{ transform:rotate(180deg); }

/* menu IN-APP (não o nativo): cabe nome + cifra + notas de cada opção */
.se-cc-menu{ margin-top:6px;display:flex;flex-direction:column;gap:6px;
  padding:8px;border-radius:12px;background:rgba(10,10,10,.72);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  border:1px solid var(--border2); }
.se-cc-opt{ display:grid;grid-template-columns:1fr auto;gap:2px 10px;text-align:left;cursor:pointer;
  padding:10px 12px;border-radius:9px;background:transparent;border:1px solid transparent; }
.se-cc-opt-nome{ font-family:var(--font-h);font-weight:800;font-size:12.5px;
  letter-spacing:.06em;text-transform:uppercase;color:var(--text); }
.se-cc-opt-cifra{ grid-row:1;grid-column:2;font-family:var(--font-h);font-weight:900;
  font-size:15px;color:var(--accent,var(--primary)); }
.se-cc-opt-notas{ grid-column:1 / -1;font-family:var(--font-b);font-size:12.5px;color:var(--muted); }
.se-cc-opt.is-on{ background:rgba(255,255,255,.06);border-color:var(--accent,var(--primary)); }
@media (hover:hover) and (pointer:fine){ .se-cc-opt:hover{ background:rgba(255,255,255,.08) } }
/* [21/07 · item 7] popup de nome da música */
.snm-input{width:100%;margin:14px 0 0;padding:12px 14px;border-radius:12px;
  background:rgba(255,255,255,.05);border:1px solid var(--border2);color:var(--text);
  font-family:var(--font-b);font-size:16px;   /* 16px: abaixo disso o iOS dá zoom no foco */
  -webkit-appearance:none;appearance:none}
.snm-input:focus{outline:none;border-color:var(--accent,var(--primary))}
.snm-erro{margin:8px 0 0;font-family:var(--font-b);font-size:13px;color:#EF4444;text-align:left}
/* [21/07 · Luis] popup de troca de modo. As 2 opções empilham (os rótulos são
 * frases — "Tocar no meu teclado" — e lado a lado quebram feio em DE/FR), e o
 * "sair do exercício" é deliberadamente discreto: é escape, não ação principal. */
.msw-actions{flex-direction:column;gap:10px}
.msw-actions .ccm-btn{width:100%}
.msw-exit{display:block;margin:14px auto 0;background:none;border:0;cursor:pointer;
  font-family:var(--font-b);font-size:13px;color:var(--muted);text-decoration:underline;
  text-underline-offset:3px;padding:6px 10px;-webkit-tap-highlight-color:transparent}
@media (hover:hover) and (pointer:fine){ .msw-exit:hover{color:var(--text)} }
/* [07/09] o mesmo pop-up passou a ser a PORTA do PRATICAR na fita e na partitura,
   que vivem deitadas — e ali o cartão não pode passar da tela (mesma lição do
   .sc-fim-modal). Só encolhe o que é decoração: o círculo do ícone e o respiro. */
@media (orientation:landscape) and (max-height:520px){
  #mode-switch-modal .ccm-card{ max-height:92vh; overflow-y:auto; padding:18px 20px; }
  #mode-switch-modal .ccm-icon{ width:44px;height:44px;margin-bottom:10px; }
  #mode-switch-modal .ccm-icon svg{ width:26px;height:26px; }
  #mode-switch-modal .ccm-title{ font-size:17px;margin-bottom:14px; }
}
.catalog-confirm-modal.closing{display:flex;pointer-events:none;animation:ccmFadeOut .2s ease-in both}
.catalog-confirm-modal.closing .ccm-card{animation:ccmSlideDown .2s cubic-bezier(.4,0,1,1) both}
@keyframes ccmFadeOut{from{opacity:1}to{opacity:0}}
@keyframes ccmSlideDown{from{transform:translateY(0);opacity:1}to{transform:translateY(16px);opacity:0}}
.ccm-icon{
  width:64px;height:64px;border-radius:50%;
  background:rgba(232,105,10,.15);color:var(--primary);
  display:inline-flex;align-items:center;justify-content:center;
  margin-bottom:20px;
}
.ccm-title{
  font-family:var(--font-h);font-weight:800;font-size:18px;
  color:var(--text);margin:0 0 12px;letter-spacing:.3px;
}
.ccm-body{
  font-family:var(--font-b);font-size:14px;line-height:1.5;
  color:var(--muted);margin:0 0 28px;
}
.ccm-actions{
  display:flex;gap:10px;flex-direction:column;
}
@media(min-width:480px){.ccm-actions{flex-direction:row;}}
.ccm-btn{
  flex:1;padding:14px 20px;border:none;border-radius:10px;
  font-family:var(--font-h);font-weight:700;font-size:13px;
  letter-spacing:.5px;text-transform:uppercase;cursor:pointer;
  transition:transform .15s,background .15s;
  -webkit-tap-highlight-color:transparent;
}
.ccm-btn:active{transform:scale(.97);}
.ccm-btn-primary{background:var(--primary);color:#fff;}
.ccm-btn-primary:hover{background:var(--primary-2);}
.ccm-btn-secondary{background:var(--card2);color:var(--text);border:1px solid var(--border2);}
.ccm-btn-secondary:hover{background:var(--border);}

/* [v149] Botão de dedilhado — laranja quando ativo */
body[data-show-fingerings="true"] .pb-fingerings-btn{
  background:var(--primary);
  color:#fff;
}

/* ============ [v771·F1-desktop] GATES EM DESKTOP — desfaz o embaralhado visto na auditoria F0 ============ */
/* O order do v767 (CTA no topo, headline embaixo) foi desenhado pro THUMB no celular; em desktop lê
   embaralhado (auditoria F0: 1280×650). Aqui (≥1024px + mouse) a ordem natural volta: headline → path →
   piano → CTA. Mobile 100% INTOCADO (media não pega touch). */
@media (min-width:900px) and (pointer:fine){
  html.gate-android #install-gate #gate-android{ order:0; margin-bottom:0; }
  html.gate-android #install-gate .gate-headline{ order:0; margin:0 0 36px; }
  html.wv-android #webview-gate #wv-android{ order:0; margin-bottom:0; }
  html.wv-android #webview-gate .wv-headline{ order:0; margin:0 0 36px; }
}
/* O hide do piano em max-height:700 mira iPhone SE, mas engole quase todo laptop (auditoria F0:
   1280×650 sem piano + metade da tela vazia). Desktop com altura razoável mostra o piano de novo.
   (Última regra do arquivo → vence o display:none da media de 700px quando ambas ativas.) */
@media (min-width:900px) and (pointer:fine) and (min-height:560px){
  .gate-piano{ display:block; }
}

/* ============ [v773·F2-desktop] FUNDAÇÃO DESKTOP (html.dt = ≥1024px + pointer:fine) ============ */
html.dt{ --dt-max:1240px; --dt-pad:40px; scrollbar-color:#2a2a2a #0a0a0a; scrollbar-width:thin; }
/* scrollbar escura (WebKit) — a barra clara padrão gritava no tema preto */
html.dt ::-webkit-scrollbar{ width:12px; height:12px; }
html.dt ::-webkit-scrollbar-track{ background:#0a0a0a; }
html.dt ::-webkit-scrollbar-thumb{ background:#2a2a2a; border-radius:8px; border:3px solid #0a0a0a; }
html.dt ::-webkit-scrollbar-thumb:hover{ background:#3a3a3a; }
/* teclado físico navega: anel de foco na cor da marca */
html.dt :focus-visible{ outline:2px solid var(--primary); outline-offset:2px; }
/* mouse: tudo que age parece clicável */
html.dt button, html.dt a, html.dt [role="button"], html.dt .toc-phase-link, html.dt .three-card{ cursor:pointer; }

/* ============ [v774·F3-desktop] JORNADA: sidebar sticky + grid de fases ============
   Antes (auditoria F0): hero centrado ocupava ~1 tela inteira e as 62 fases desciam numa
   coluna de 760px com o resto do monitor deserto. Agora: identidade/progresso fixos à
   esquerda (acompanham o scroll) e as fases em 2 colunas (3 em telas largas). */
@media (min-width:600px){
  html.dt body.mode-interactive .toc-page.visible{
    display:grid; grid-template-columns:minmax(288px,332px) minmax(0,1fr);
    gap:clamp(30px,3.6vw,58px); align-items:start;
    max-width:var(--dt-max); margin:0 auto;
    padding:52px var(--dt-pad) 90px;
  }
  /* coluna 1 — hero vira painel lateral fixo, alinhado à esquerda */
  html.dt .toc-page .toc-hero{ position:sticky; top:52px; text-align:left; width:auto; margin:0; overflow:visible; }
  html.dt .toc-page .toc-mega-title{ font-size:clamp(34px,3.1vw,46px); letter-spacing:-1.2px; line-height:1.02; }
  html.dt .toc-page .toc-tier-row{ max-width:none; margin:26px 0 0; }
  html.dt .toc-page .toc-progress-line{ text-align:left; margin:22px 0 0; }
  /* coluna 2 — fases em grid */
  /* [v780] auto-fit: o nº de colunas se ajusta sozinho à largura REAL (janela de PWA no desktop pode
     nascer com qualquer tamanho — não dá pra assumir 1440). ~290px é o piso legível de um card. */
  html.dt .toc-page .toc-phases{ max-width:none; margin:0; display:grid;
    grid-template-columns:repeat(auto-fit,minmax(290px,1fr)); gap:13px; align-content:start; }
  /* mouse: o card responde ao hover (levanta + destaca) */
  html.dt .toc-phase{ transition:transform .16s ease, box-shadow .16s ease, background-color .16s ease; }
  html.dt a.toc-phase-link:hover{ transform:translateY(-3px);
    background:rgba(30,30,30,.52); box-shadow:0 12px 30px rgba(0,0,0,.55); }
  html.dt a.toc-phase-link:active{ transform:translateY(-1px); }
}
/* monitor largo: o container respira mais (o auto-fit acima converte isso em +1 coluna de fases) */
@media (min-width:1600px){
  html.dt{ --dt-max:1440px; }
}
/* janela estreita de desktop (900–1080, típico de PWA instalado): sidebar encolhe pra sobrar
   largura de card; abaixo de 900 o app volta ao layout mobile, que já é bom nessa medida. */
@media (min-width:600px) and (max-width:1080px){
  html.dt body.mode-interactive .toc-page.visible{ grid-template-columns:minmax(232px,262px) minmax(0,1fr); padding:40px 28px 72px; }
  html.dt .toc-page .toc-mega-title{ font-size:32px; }
  html.dt body.mode-interactive .unit-intro.visible{ padding:36px 28px 64px; }
}

/* ============ [v775·F4-desktop] PÁGINA DA FASE (unit-intro): 2 colunas ============
   Antes (F0): título no canto sup-esq, chips 11px, piano de referência tímido no meio e um mar
   de vazio embaixo. Agora: identidade+descrição à esquerda, piano de referência + "ouvir" à
   direita (centrado verticalmente), CTA Praticar fechando a coluna de texto.
   row-gap:0 é OBRIGATÓRIO — os blocos já trazem suas próprias margens verticais. */
@media (min-width:600px){
  html.dt body.mode-interactive .unit-intro.visible{
    display:grid; grid-template-columns:minmax(0,.94fr) minmax(0,1.06fr);
    column-gap:clamp(36px,4.2vw,76px); row-gap:0; align-content:center;
    max-width:var(--dt-max); margin:0 auto; padding:48px var(--dt-pad) 88px;
  }
  html.dt .unit-intro.visible > *{ grid-column:1; }
  /* piano de referência: coluna 2, cobrindo as 5 linhas da coluna de texto, centrado */
  html.dt .unit-intro.visible > .kbd-wrap{ grid-column:2; grid-row:1 / span 5; align-self:center; margin:0!important; }

  /* 🔴🔑 [18/09 · LOTE 4i · Luis] *"a unit intro page no tablet em pe deve ter o
     layout igual a em celulares em pe"*.

     A grelha de duas colunas acima é do lote F4-desktop e é boa no MONITOR (e no
     tablet DEITADO, que também tem largura de sobra). Em PÉ ela não cabe: o
     tablet ganhou o `dt` pelo lado curto >= 600 (index.html:1380) e levou junto o
     desenho de uma tela larga — texto espremido numa coluna de .94fr com o piano
     ao lado, onde o telemóvel empilha e respira.
     🔑 O PORTÃO É `tb` + RETRATO: o monitor não tem `tb`, o tablet deitado não é
     retrato, e o telemóvel nunca entrou aqui. Só a caixa que ele nomeou muda.
     ⚠️ `display:block` e não `flex`: a `.page` da casa é bloco em fluxo, e um
     `flex` aqui mudava a margem de todos os filhos (as margens verticais deixam
     de colapsar) — era mudar o layout a pretexto de o devolver. */
  @media (orientation:portrait){
    html.tb body.mode-interactive .unit-intro.visible{
      display:block; max-width:none; margin:0;
      padding:64px 18px calc(28px + env(safe-area-inset-bottom));
    }
    html.tb .unit-intro.visible > *{ grid-column:auto; }
    html.tb .unit-intro.visible > .kbd-wrap{
      grid-column:auto; grid-row:auto; align-self:auto;
      margin-top:clamp(10px,3.6vh,38px)!important;   /* o valor base, app.css:3555 */
    }
  }
  html.dt .unit-intro h1{ font-size:clamp(54px,4.6vw,68px); }
  html.dt .unit-intro .u-tag{ font-size:12px; }
  /* o vão de 96px (11vh) fazia sentido no empilhamento mobile; em 2 colunas vira buraco */
  html.dt .unit-intro .character{ margin:26px 0 10px; font-size:21px; }
  html.dt .unit-intro .context{ font-size:15px; line-height:1.6; max-width:52ch; }
  html.dt .unit-intro .intro-stack{ margin-top:34px; }
  html.dt .unit-intro .audio-btn-row{ margin-top:26px; }
  /* mouse: CTA e "ouvir" reagem */
  html.dt .unit-intro .audio-btn{ transition:background .15s, transform .12s; }
  html.dt .unit-intro .audio-btn:hover:not([disabled]){ background:rgba(232,105,10,.20); transform:translateY(-1px); }
}

/* ============ [v776·F5-desktop] PRÁTICA: hints QWERTY + hover + limite em telas ultra-largas ============
   A prática já era a melhor tela do desktop (auditoria F0). Faltava: (a) revelar que o teclado do
   COMPUTADOR toca o piano (o mapa z s x d c… sempre existiu, mas invisível), (b) resposta ao mouse,
   (c) um teto de largura pra não virar teclado de circo em 2560px. */
/* [21/07 · Luis] hints QWERTY REMOVIDOS: as teclas da tela não exibem mais a
   letra do teclado do computador. A camada nem é gerada (ver renderKeyboard);
   o PHYSICAL_KEY_MAP segue ativo — sumiu a legenda, não a funcionalidade. */
/* mouse sobre a tecla: clareia (feedback que faltava no desktop) */
/* 🔴 [19/09 · TECNICA] SO COM RATO DE VERDADE. Esta regra nasceu em julho para o
   computador, quando `dt` era `(min-width:900px) and (pointer:fine)`. Em 16/09 o portao
   passou a dar `dt` ao tablet e ao telemovel deitado (`dc`) — e num aparelho de TOQUE o
   `:hover` COLA na ultima tecla tocada ate ao toque seguinte: era a "tecla branca" que o
   Luis via ao passar de um acorde para o outro na harmonia (medido: fill rgb(237,237,237)
   sem classe nenhuma, 3/3 caixas, todos os exercicios, ate no caminho do mic). A cor da
   tecla na pratica e ESTADO (ouro/verde/vermelho); so um rato real ganha o clareamento.
   Escape `?hov=1` (html.tt-hov1) devolve o comportamento do ar. */
@media (hover:hover) and (pointer:fine){
  html.dt .practice-page .kbd-key.kbd-w:hover{ fill:#EDEDED; }
  html.dt .practice-page .kbd-key.kbd-b:hover{ fill:#242424; }
}
html.dt.tt-hov1 .practice-page .kbd-key.kbd-w:hover{ fill:#EDEDED; }
html.dt.tt-hov1 .practice-page .kbd-key.kbd-b:hover{ fill:#242424; }
html.dt .practice-page .kbd-key{ transition:fill .1s ease; }
/* botões da barra de prática respondem ao mouse */
html.dt .practice-bar .pb-icon-btn:hover, html.dt .practice-bar .pb-pill:hover{ filter:brightness(1.18); }
/* telas ultra-largas: o conjunto para de esticar e centraliza */
@media (min-width:1760px){
  html.dt .practice-page .practice-bar, html.dt .practice-page .kbd-wrap{
    max-width:1680px; margin-left:auto; margin-right:auto; width:100%;
  }
}

/* ============ [v778·F6-desktop] FUNIL DE COMPRA + TUTORIAL ============ */
/* (b) [v781·REVERTIDO] O "palco retrato" de 660px do v778 FOI UM ERRO: no Mac o app instalado abria
   mostrando só uma faixa vertical no meio da tela — "igualzinho no celular" (relato Luis). O iframe
   volta a ocupar a tela inteira; quem foi adaptado pro desktop agora é o CONTEÚDO do tutorial
   (experiencia.html, bloco DESKTOP próprio). Nada a sobrescrever aqui — o inline style já é 100vw. */
/* (c) decisão: cartões respondem ao mouse (o clique é a venda — precisa parecer clicável) */
html.dt .three-card{ transition:transform .16s ease, box-shadow .16s ease, border-color .16s ease; }
html.dt .three-card:hover{ transform:translateY(-3px); box-shadow:0 16px 40px rgba(0,0,0,.55); }
/* (d) desktop: a decisão respira um pouco mais sem tocar na coreografia (só largura do container) */
@media (min-width:600px){
  html.dt body:not(.dv2-on) .three-cards{ max-width:680px; }
}

/* ============ [v779·F7-desktop] POLISH ============ */
@media (min-width:600px){
  /* mode-select: os 2 cards empilhados viravam uma coluna estreita perdida no monitor → lado a lado */
  html.dt .ms-like .ms-cards{ flex-direction:row; max-width:900px; gap:22px; }
  html.dt .ms-like .ms-card{ flex:1 1 0; }
  html.dt .ms-like .ms-title{ font-size:clamp(44px,4.4vw,64px); }
  /* login: respiro proporcional à tela */
  html.dt .login-gate-inner{ max-width:520px; }
}
/* mouse: acionáveis globais reagem (botão de conta, navegação, itens da sidebar) */
html.dt .account-btn{ transition:transform .14s ease, background .14s ease; }
html.dt .account-btn:hover{ transform:translateY(-1px); background:rgba(32,32,32,.96); }
html.dt .nav-tag{ transition:transform .14s ease, filter .14s ease; }
html.dt .nav-tag:hover{ transform:translateY(-1px); filter:brightness(1.15); }
html.dt .sidebar a:hover, html.dt .sidebar button:hover{ background:rgba(255,255,255,.05); }
/* CTAs principais ganham um lift sutil no hover */
html.dt .audio-btn:hover, html.dt .wv-cta:hover, html.dt #gate-install-btn:hover{ filter:brightness(1.08); }

/* ============ [v784·ESCALA DESKTOP] Elementos em tamanho de monitor ============
   Relato Luis 20/07 (print): "todos os elementos do menu jornada completa ficaram pequenos, com
   exceção do título, olhe quanto espaço vazio tem... vale para os cards de níveis no topo. Refaça
   sob o critério de ícones grandes / sem deixar sobrar espaço vazio; aplique o mesmo pra intro page."
   Causa: o layout desktop (F3/F4) reorganizou as CAIXAS mas manteve tipografia/paddings de celular,
   e o container travava em 1240px deixando ~450px vazios de cada lado num monitor. */
@media (min-width:600px){
  /* container: ocupa a tela de verdade (era 1240 fixo) */
  html.dt{ --dt-max:min(1880px, 94vw); }

  /* ---- CARDS DE FASE: maiores, com respiro e cadeado visível ---- */
  html.dt .toc-page .toc-phases{ grid-template-columns:repeat(auto-fit,minmax(330px,1fr)); gap:16px; }
  html.dt .toc-phase{ padding:20px 22px; border-radius:16px; border-left-width:5px; }
  html.dt .toc-phase-title{ font-size:21px; }
  html.dt .toc-phase-sub{ font-size:15px; margin-top:5px; }
  html.dt .toc-phase-pill{ font-size:11px; padding:4px 12px; }
  html.dt .toc-phase-lock{ width:27px; height:27px; right:20px; }
  html.dt body.pw-nonpayer .toc-phase{ padding-right:60px; }

  /* ---- CARDS DE NÍVEL (tiers): mesmo tratamento ---- */
  html.dt .toc-tier-tag{ padding:18px 22px; border-radius:15px; gap:16px; }
  html.dt .toc-tier-tag .ttag-dots{ width:38px; height:17px; }
  html.dt .toc-tier-tag strong{ font-size:16px; }
  html.dt .toc-tier-tag em{ font-size:14px; }
  html.dt .toc-progress-line{ font-size:17px; }
  html.dt .toc-progress-line strong{ font-size:19px; }
  html.dt .toc-page .toc-hero{ top:40px; }

  /* ---- INTRO PAGE (unit-intro): chips, citação e contexto na mesma régua ---- */
  html.dt .unit-intro .u-tag{ font-size:14px; padding:7px 16px; border-radius:16px; }
  html.dt .unit-intro .badge{ font-size:16px; padding:8px 18px; border-radius:18px; }
  html.dt .unit-intro .formulae{ gap:13px; margin:24px 0 8px; }
  html.dt .unit-intro .character{ font-size:26px; margin:30px 0 12px; }
  html.dt .unit-intro .context{ font-size:17px; line-height:1.62; max-width:56ch; }
  html.dt .unit-intro .kbd-ref-label{ font-size:14px!important; letter-spacing:2px!important; margin-bottom:14px!important; }
  html.dt .unit-intro .audio-btn{ font-size:15px; padding:14px 26px; }
}
/* monitor grande: mais uma marcha de escala (o vazio some de vez) */
@media (min-width:1500px){
  html.dt .toc-page .toc-phases{ grid-template-columns:repeat(auto-fit,minmax(360px,1fr)); }
  html.dt .toc-phase-title{ font-size:23px; }
  html.dt .toc-phase-sub{ font-size:16px; }
  html.dt .toc-phase{ padding:23px 25px; }
  html.dt .toc-page .toc-mega-title{ font-size:clamp(46px,3.4vw,60px); }
  /* a coluna da esquerda cresce junto — senão vira uma tira fina ao lado de cards grandes */
  html.dt body.mode-interactive .toc-page.visible{ grid-template-columns:minmax(340px,400px) minmax(0,1fr); }
}
/* [v785] telas grandes: o piano de referência estava preso ao max-width:720px INLINE do template
   (a F4 zerou só a margem) e o título podia crescer mais — o vazio da direita some. */
@media (min-width:1500px){
  html.dt .unit-intro.visible > .kbd-wrap{ max-width:900px!important; }
  html.dt .unit-intro h1{ font-size:clamp(64px,4.6vw,84px); }
  html.dt .unit-intro .character{ font-size:29px; }
}

/* ============ [v786·PAINEL DE PRÁTICA MAIOR] ============
   Pedido Luis 20/07: "aumente a tela de piano mantendo a proporção". O painel estava preso a um cap
   FIXO de 1100×620 (bloco desktop herdado, ~linha 4361) — num monitor sobrava tela por todos os lados.
   Agora o cap ESCALA com a janela mantendo exatamente a mesma razão (1100/620 = 1.7742):
   a largura é o menor entre 92vw e "a altura disponível convertida em largura", e a altura é derivada
   dela — - nunca distorce e nunca estoura. Em 1728×1080: 1590×896 (era 1100×620, ~+45%). */
@media (min-width:600px){
  html.dt body.mode-interactive .practice-page.visible{
    /* [v791] inset+margin:auto REPETIDOS aqui de propósito: o dimensionamento do v786 começa em 900px,
       mas a centralização ("truque de centralizar fixed") só existia no bloco de 1024px — entre 900 e
       1023 o painel ficava sobre-restringido e grudava no CANTO SUPERIOR ESQUERDO, com faixas pretas à
       direita e embaixo. É exatamente a largura em que a janela do PWA de desktop nasce. Acima de 1024
       é no-op (mesmos valores do outro bloco). */
    inset: 0;
    margin: auto;
    --pw: min(92vw, calc(86vh * 1.7742));
    max-width: var(--pw);
    max-height: calc(var(--pw) / 1.7742);
    border:1px solid rgba(232,105,10,.18); border-radius:18px; overflow:hidden;
  }
  /* a barra do exercício acompanha o painel — senão vira uma tira pequena num card grande */
  html.dt .practice-bar{ --pb-btn:46px; --pb-btn-fz:17px; }
}

/* 🔴🔑🔑 [18/09 · LOTE 4i · Luis] *"a tela de pratica nos tablets ganhou bordas
   igual na versao computador, completamente errado, nao deve haver bordas, tela
   de pratica completamente maximizada e cheia, seja fita, partitura, ou
   tecnica."*

   O TABLET HERDOU O PAINEL PORQUE HERDOU O `dt`. O portão (index.html:1380) liga
   `dt` a tudo o que tem toque e lado curto >= 600, e o bloco acima — que é o
   painel de monitor que ele próprio pediu a 20/07 ("aumente a tela de piano
   mantendo a proporção") — passou a valer no iPad. Medido no ar (staging-330),
   nas TRÊS práticas (`.practice-page` é a classe das três: técnica, `score-play`
   e `song-practice`):

       P1  390x844   útil 390 de 390   0 px de borda   ← o telemóvel, que ele diz
       B   810x1080  útil 743 de 810   33 + 33         ser perfeito
       D1 1024x1366  útil 940 de 1024  42 + 42
       C  1080x810   útil 992 de 1080  44 + 44
       M1 1440x900   útil 1323 de 1440 59 + 59         ← o MONITOR fica como está

   🔑 O PORTÃO É `tb`, NÃO `dt`. `tb` = `dt && dedo` (o mesmo index.html:1380), ou
   seja exactamente "tablet": não toca no monitor, onde o painel é o desenho
   pedido, e não toca no telemóvel, que nunca entrou no `dt`. E a cura devolve a
   prática ao layout BASE (`.practice-page{height:100dvh;width:100vw}` +
   `position:fixed;inset:0`, app.css:1783/1799) — que é o mesmo layout, ao píxel,
   que o telemóvel usa.
   ⚠️ MESMA ESPECIFICIDADE do bloco acima (0,4,2), logo ganha por VIR DEPOIS — e
   fica aqui, encostado a quem corrige, não no fim do ficheiro. */
@media (min-width:600px){
  html.tb body.mode-interactive .practice-page.visible{
    /* o `--pw` fica DEFINIDO (a barra deriva a altura dele, logo abaixo) e passa
       a valer a tela inteira, em vez dos 92 % que reservavam a moldura. */
    --pw:100vw;
    max-width:none; max-height:none;
    border:0; border-radius:0;
  }
  /* deitado a prática gira por conta do `body.tt-deitado` (app.css:10050), onde a
     largura é a ALTURA da janela — o `--pw` tem de seguir a mesma troca de eixo,
     senão a barra dimensiona-se pelo lado errado. */
  body.tt-deitado html.tb .practice-page.visible,
  html.tb body.tt-deitado .practice-page.visible{ --pw:100dvh; }
}
@media (min-width:1400px){
  html.dt .practice-bar{ --pb-btn:52px; --pb-btn-fz:19px; }
}
/* [v787] A barra tinha altura FIXA (220px), então todo o ganho de altura ia pro teclado e as teclas
   ficavam mais compridas que no design original. Agora a barra cresce na MESMA razão do painel
   (220/620 = .355 da altura total) → o teclado mantém exatamente a proporção de origem (~2.76:1). */
@media (min-width:600px){
  html.dt .practice-page.visible .practice-bar{ min-height:calc((var(--pw) / 1.7742) * .355); }
}

/* ============================================================================
 * [F2] SHELL DE NAVEGAÇÃO — tab bar (retrato) / rail (desktop)
 *
 * A superfície de navegação persistente que o app nunca teve. Só aparece com
 * body.tabbar-on, que o JS liga conforme o data-mode da página: some inteira na
 * prática (paisagem imersiva) e sob os gates de alta faixa.
 *
 * MONOCROMÁTICA de propósito. O app já sobrecarrega o verde (#22C55E = Escala
 * E tier iniciante E concluído) e o vermelho (#DC2626 = Modo E avançado); dar
 * cor por aba criaria um terceiro significado pros mesmos hexes. --accent fica
 * reservado ao conteúdo, a navegação é laranja/cinza.
 * ========================================================================= */

/* [22/07 · Luis] NÃO mexer na reserva de safe area daqui: o Luis conferiu no device
   e "a visualização já estava perfeita no index principal". A barra cortada do
   relato é só a da experiencia.html (iframe), que tem causa própria — ver o bloco
   .tut-tabbar lá. Aqui o env() resolve normal, porque este é o documento de TOPO. */
.tabbar{
  position:fixed;left:0;right:0;bottom:0;z-index:200;
  display:none;                                  /* só com body.tabbar-on */
  align-items:stretch;
  /* [22/07 · ERRO MEU, corrigido] Isto chegou a usar var(--tt-sab), uma "reserva efetiva"
     calculada em JS como env − (screen.height − innerHeight). A subtração estava ERRADA:
     naquele iPhone 926−879 = 47 é o safe-area-inset-TOP (barra de status), não folga
     embaixo. Subtraí um inset de cima de um inset de baixo, deu 0, e o indicador de home
     passou a cobrir os rótulos (medido no print: barra 869→926 = 57px, reserva zero).
     No documento de TOPO o env() é a resposta certa e VIVA — recalculada a cada layout,
     sem snapshot e sem depender de JS. ⚠️ NÃO trocar de novo por valor vindo de JS. */
  height:calc(58px + env(safe-area-inset-bottom));
  padding-bottom:env(safe-area-inset-bottom);
  background:transparent;
  border-top:1px solid var(--border);
}
/* [22/07] O backdrop-filter NÃO pode morar no MESMO elemento que é position:fixed:
   no iOS/WebKit, fixed + backdrop-filter sobre documento rolando faz a barra ser
   pintada numa posição CONGELADA e ela "rola junto" com a página, aparecendo no meio
   do conteúdo (reproduzido no device: barra flutuando ~70% da tela). O fix é desacoplar
   — o vidro vai pra um ::before ABSOLUTE (inset:0), que composita junto com o pai fixed
   sem stale-paint. O .tabbar volta a ser fixed PURO e gruda na viewport.
   ⚠️ NÃO devolver o backdrop-filter pro .tabbar. Blur 14px (mais forte que .toc-phase)
   porque sobrepõe conteúdo rolando; o #c de constelação segue aparecendo através. */
.tabbar::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:rgba(12,12,12,.82);
  backdrop-filter:blur(14px) saturate(1.1);
  -webkit-backdrop-filter:blur(14px) saturate(1.1);
}
/* [08/09 · ANDROID_BLUR lote 2] A BARRA DE ABAS E A UNICA QUE SE VE, e por isso e a
   unica com ramo de plataforma. Ela desfoca conteudo que ROLA por baixo — o pior caso
   do Chrome, uma superficie FIXA cujo fundo suja a cada quadro de rolagem — mas
   tira-la NAO e neutro: medido (ceu congelado, sumario rolado, recorte do rodape)
   sem blur com o fundo de hoje da media 3,146 e p99 30; a .92 da 2,047; a .96, o
   melhor de todos, ainda da 1,704 com p99 10 e 2,75% dos pixels da barra. Nem
   opacidade 1,0 o reproduz. Entao paga-se o pixel SO onde ha ganho: no Android.
   `html.gate-android-ua` e posto no <head> para toda UA de Android, ANTES do ramo do
   gate e independente do bypass (index.html:222) — no iPhone esta barra fica
   exatamente como estava. Reverter e apagar este bloco. */
html.gate-android-ua .tabbar::before{
  background:rgba(12,12,12,.96);
  backdrop-filter:none; -webkit-backdrop-filter:none;
}
body.tabbar-on .tabbar{display:flex}

/* [22/07 · v3] A reserva das barras do documento de topo é CALCULADA (--tt-sab), porque
   o viewport muda de altura entre telas: na decisão ele termina 47pt acima do indicador
   de home (reserva = 0) e na Jornada ele chega no fundo da tela (reserva = 34). Medido
   no device nas duas telas.
   A regra base acima segue com env() CRU de propósito: `html.tt-sab-js` só é adicionada
   quando o JS mediu de verdade. Se o JS falhar, o CSS degrada pro env() — reserva a MAIS
   (feio) em vez de reserva a MENOS (indicador cobrindo os rótulos). ⚠️ Nunca inverter isso. */
html.tt-sab-js .tabbar{
  height:calc(58px + var(--tt-sab, 0px));
  padding-bottom:var(--tt-sab, 0px);
}
html.tt-sab-js .gate-tabbar{
  height:calc(58px + var(--tt-sab, 0px));
  padding-bottom:var(--tt-sab, 0px);
}
html.tt-sab-js .three-options{ padding-bottom:calc(var(--tt-sab, 0px) + 24px + 58px); }
html.dt.tt-sab-js .three-options{ padding-bottom:calc(var(--tt-sab, 0px) + 24px); }

.tabbar-item{
  flex:1 1 0;min-width:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  background:none;border:0;border-top:2px solid transparent;
  color:var(--muted);cursor:pointer;
  padding:0 2px;   /* [F1-Partitura] 4px→2px: pacote tipográfico da barra de 5 itens (aprovado 25/07) */
  -webkit-tap-highlight-color:transparent;
  transition:color .16s cubic-bezier(.2,.8,.2,1), border-color .16s cubic-bezier(.2,.8,.2,1), transform .14s cubic-bezier(.2,.8,.2,1);
}
.tabbar-item svg{width:22px;height:22px;flex:none}
.tabbar-label{
  /* [F1-Partitura] 9.5px/.10em → 9px/.04em: com 5 itens o slot em 320px cai de
     ~72px pra ~56px e os rótulos latinos de 8-9 chars (CANCIONES, PARTITURA,
     HARMONIA…) truncavam. O ellipsis continua como última defesa. */
  font-family:var(--font-h);font-weight:800;font-size:9px;
  letter-spacing:.04em;text-transform:uppercase;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* Indicador de ativo = barra SUPERIOR de 2px. É a rotação natural da
   assinatura do app (a borda-esquerda de 3px dos cards) pra uma barra
   inferior — não um preenchimento sólido, que aqui viraria botão. */
.tabbar-item[aria-selected="true"]{
  color:var(--primary);
  border-top-color:var(--primary);
}
.tabbar-item:active{transform:scale(.96)}
@media (hover:hover) and (pointer:fine){
  .tabbar-item:hover{color:var(--text)}
  .tabbar-item[aria-selected="true"]:hover{color:var(--primary)}
}
.tabbar-item:focus-visible{outline:2px solid var(--primary);outline-offset:-3px}

/* As pílulas flutuantes (#nextBtn/#prevBtn) ficam fixed em bottom:14px — em
   cima da barra. Sobem quando a barra está visível. */
body.tabbar-on .nav-tags{ bottom:calc(14px + 58px + env(safe-area-inset-bottom)); }

/* .page já reserva 82px no rodapé e .toc-page 96px — a barra tem 58px, então
   cabe sem somar padding. Só as raízes novas precisam de folga própria. */

/* ---- raízes "em breve" das abas novas (F3/F4/F5 preenchem) ---- */
.tabroot-page{ padding:32px clamp(16px,4vw,40px) calc(96px + env(safe-area-inset-bottom)); }
/* Mesma armadilha de specificidade documentada no bloco E do TOC: `.special h1`
   (0,1,1) vence `.toc-mega-title` (0,1,0) e impõe font-size:42px + a barra
   laranja à esquerda. `.tabroot-page .toc-mega-title` é (0,2,0) e vence sem
   !important — exatamente como `.toc-page .toc-mega-title` faz. */
.tabroot-page .toc-mega-title{
  font-size:clamp(34px, 8.5vw, 62px);
  line-height:1.02;letter-spacing:-1.6px;
  border-left:none;padding-left:0;margin:0 0 12px;
}
.tabroot-page .toc-hero{ margin-bottom:28px;text-align:left; }
.tabroot-page .lead{ max-width:680px;color:var(--muted);font-size:15px;margin:0; }
.tabroot-soon{ display:flex;justify-content:center; }
.tabroot-soon-card{
  max-width:520px;width:100%;
  padding:22px 24px;border-radius:14px;
  background:rgba(18,18,18,.34);
  border:1px solid var(--border2);
  border-left:3px solid var(--accent,var(--primary));
}
.tabroot-soon-card .u-tag{ display:inline-block;margin-bottom:12px;
  padding:5px 12px;border-radius:14px;
  font-family:var(--font-h);font-weight:700;font-size:11px;
  letter-spacing:1.5px;text-transform:uppercase;line-height:1;
  background:color-mix(in srgb, var(--accent,var(--primary)) 14%, transparent);
  border:1px solid color-mix(in srgb, var(--accent,var(--primary)) 40%, transparent);
  color:var(--accent,var(--primary)); }
.tabroot-soon-card p{ margin:0;color:var(--muted);font-size:14px;line-height:1.55; }

/* ---- DESKTOP: barra inferior é errada aqui; vira rail vertical à esquerda ----
   Compõe com o grid de 2 colunas que a Jornada já tem (hero sticky + cards):
   o rail é uma coluna externa, ANTES do container --dt-max. */
html.dt body.tabbar-on .tabbar{
  top:0;bottom:0;right:auto;
  /* 🔴🔑 [16/09 · Luis, no aparelho] *"no iphone, o trilho de navegacao lateral
     quando a tela esta horizontal, esta sendo tampado pelo notch."*
     O trilho nasceu para MONITOR, onde `left:0` é a borda do ecrã e não há nada
     por cima dela. Em 15/09 o portão do `dt` passou a acender no telemóvel
     DEITADO (a classe `dc`) — e aí `left:0` é exactamente onde o entalhe mora.
     Os ícones do trilho ficavam por baixo dele.
     A faixa CONTINUA a chegar à borda (o vidro dela corre por baixo do entalhe,
     que é o que faz a tela parecer inteira); o que recua é o CONTEÚDO, pelo
     padding. Mesma conta do resto da casa — `max(var(--sal), env(...))`, com o
     `--sal` a servir de costura para a bancada e o `env()` a mandar no device.
     Virado para o outro lado o entalhe vai para a direita, `inset-left` dá 0 e
     esta conta desaparece sozinha: não há orientação a decidir nada. */
  left:0;
  width:calc(88px + max(var(--sal), env(safe-area-inset-left, 0px)));
  padding-left:max(var(--sal), env(safe-area-inset-left, 0px));
  height:auto;padding-bottom:0;
  flex-direction:column;justify-content:flex-start;gap:2px;
  padding-top:18px;
  border-top:0;border-right:1px solid var(--border);
}
html.dt .tabbar-item{
  flex:none;height:74px;gap:6px;
  border-top:0;border-left:3px solid transparent;
}
html.dt .tabbar-item[aria-selected="true"]{ border-left-color:var(--primary); }
html.dt .tabbar-item svg{width:24px;height:24px}
html.dt .tabbar-label{font-size:10px}
html.dt body.tabbar-on #app{ padding-left:calc(88px + max(var(--sal), env(safe-area-inset-left, 0px))); }
html.dt body.tabbar-on .nav-tags{ left:calc(88px + max(var(--sal), env(safe-area-inset-left, 0px)));bottom:calc(14px + env(safe-area-inset-bottom)); }

@media (prefers-reduced-motion:reduce){
  .tabbar-item{transition:none}
  .tabbar-item:active{transform:none}
}

/* ============================================================================
 * [F3] MODO MINHA MÚSICA — lista, editor e o comparativo "mão parada"
 * ========================================================================= */
.songs-page .songs-list{ display:flex;flex-direction:column;gap:10px;max-width:760px;margin:0 0 20px; }
.song-card{ cursor:pointer; }
.songs-new{ max-width:760px;margin-top:4px; }
.songs-new-plus{
  width:44px;height:44px;flex:none;display:flex;align-items:center;justify-content:center;
  border-radius:50%;background:rgba(0,0,0,.28);border:1.5px solid rgba(255,255,255,.45);
  font-family:var(--font-h);font-weight:900;font-size:26px;line-height:1;color:var(--text);
}

/* ---- editor ---- */
.song-edit-page{ max-width:860px; }
.se-head{ display:flex;align-items:center;gap:12px;margin-bottom:18px; }
.se-name{
  flex:1;min-width:0;background:transparent;border:0;border-bottom:2px solid var(--border2);
  color:var(--text);font-family:var(--font-h);font-weight:900;
  font-size:clamp(22px,5vw,32px);letter-spacing:-.6px;padding:6px 2px;
}
.se-name:focus{ outline:none;border-bottom-color:var(--accent,var(--primary)); }
.se-name::placeholder{ color:var(--dim); }
.se-label{
  display:block;margin-bottom:8px;
  font-family:var(--font-h);font-weight:800;font-size:10.5px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
}
/* [21/07 · Luis] campo de UMA LINHA. Era um textarea de 3 linhas que ocupava um
 * terço da tela sem precisar — uma harmonia inteira cabe numa linha. Continua
 * <textarea> (colar de outro lugar traz quebras e o textarea absorve), mas com
 * altura de linha única e rolagem horizontal; nada de esticar na vertical. */
.se-chords{
  width:100%;box-sizing:border-box;resize:none;
  height:52px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;
  background:rgba(18,18,18,.34);backdrop-filter:blur(9px) saturate(1.1);-webkit-backdrop-filter:blur(9px) saturate(1.1);
  border:1px solid var(--border2);border-left:3px solid var(--accent,var(--primary));
  border-radius:12px;padding:14px 16px;color:var(--text);
  font-family:var(--font-h);font-weight:700;font-size:16px;letter-spacing:.06em;line-height:1.5;
}
.se-chords::-webkit-scrollbar{ height:0; }

/* ============================================================
 * [21/07 · Luis] PÁGINA DA HARMONIA PRONTA — um mini piano por acorde
 * ========================================================= */
.sv-title{ font-size:clamp(22px,6vw,34px); }
.sv-lista{ display:flex;flex-direction:column;gap:18px;margin:18px 0 8px; }
.sv-item{
  display:flex;flex-direction:column;gap:8px;align-items:center;
  padding:14px 14px 16px;border-radius:14px;background:rgba(18,18,18,.34);
  border:1px solid var(--border2);border-left:3px solid var(--accent,var(--primary));
}
/* o acorde temperado é o ponto alto da sugestão — merece se distinguir */
.sv-item.is-tempero{ border-left-color:#F59E0B;background:color-mix(in srgb,#F59E0B 8%,rgba(18,18,18,.34)); }
.sv-kbd{ width:100%;max-width:520px; }
.sv-kbd svg{ width:100%;height:auto;display:block; }
.sv-cap{ display:flex;flex-direction:column;gap:2px;align-items:center;text-align:center; }
.sv-cap strong{ font-family:var(--font-h);font-weight:900;font-size:19px;letter-spacing:-.2px;color:var(--text); }
.sv-cap span{ font-family:var(--font-b);font-size:13px;color:var(--muted); }
.sv-ganho{
  margin:18px 0 10px;text-align:center;
  font-family:var(--font-h);font-weight:700;font-size:15px;color:var(--muted);
}
.sv-ganho strong{ color:var(--accent,var(--primary));font-weight:900;font-size:19px; }

/* check no card decidido + menu com rolagem suave */
.se-cc-check{ flex:0 0 auto;font-size:16px;font-weight:900;color:#22C55E;line-height:1; }
.se-cc.is-done .se-cc-head{ border-left-color:#22C55E; }
.se-cc.is-done .se-cc-atual em{ color:#22C55E; }
/* [Luis] "animação rolante suave": o menu desce em vez de aparecer seco */
.se-cc-menu{ overflow:hidden; }
.se-cc-menu:not([hidden]){ animation:ccRolar .22s cubic-bezier(.2,.8,.2,1) both; }
@keyframes ccRolar{ from{ opacity:0; transform:translateY(-6px); max-height:0 }
                    to  { opacity:1; transform:none;             max-height:420px } }
@media (prefers-reduced-motion:reduce){ .se-cc-menu:not([hidden]){ animation:none } }

/* MONITOR AO VIVO — o recibo do que o app entendeu */
.se-monitor{
  display:flex;flex-wrap:wrap;justify-content:center;align-items:baseline;
  gap:6px clamp(14px,4vw,30px);
  min-height:56px;margin:14px 0 2px;text-align:center;
}
.se-monitor.is-vazio{ min-height:0;margin:0; }
.se-mon-ch{
  font-family:var(--font-h);font-weight:900;
  font-size:clamp(30px,8.4vw,46px);line-height:1.05;letter-spacing:-.5px;
  color:var(--primary);
  animation:seMonIn .18s var(--e-out,ease) both;
}
/* o que o parser NÃO entendeu não some — some daria a impressão de aceito */
.se-mon-ch.is-bad{ color:var(--muted);opacity:.5;text-decoration:line-through; }
@keyframes seMonIn{ from{opacity:0;transform:translateY(4px)} to{opacity:1;transform:none} }
@media (prefers-reduced-motion:reduce){ .se-mon-ch{ animation:none } }
.se-chords:focus{ outline:none;border-color:var(--accent,var(--primary)); }
.se-errors{ min-height:20px;margin:8px 0 4px;font-size:13px;color:var(--muted); }
.se-err{
  display:inline-block;padding:2px 8px;border-radius:999px;
  background:rgba(239,68,68,.14);border:1px solid rgba(239,68,68,.45);color:#EF4444;
  font-family:var(--font-h);font-weight:700;font-size:12px;
}
/* [AUDITORIA] (0,2,0): `.special p` (2623) vencia e isto renderizava BRANCO 13.5px */
.song-edit-page .se-hint{ color:var(--dim);font-size:14px;margin:0; }

/* ---- comparativo: o momento "aha" ---- */
.se-compare{ display:grid;grid-template-columns:1fr;gap:14px;margin:14px 0 6px; }
@media (min-width:720px){ .se-compare{ grid-template-columns:1fr 1fr;gap:18px } }
.se-side{
  padding:14px;border-radius:12px;
  background:rgba(18,18,18,.34);backdrop-filter:blur(9px) saturate(1.1);-webkit-backdrop-filter:blur(9px) saturate(1.1);
  border:1px solid var(--border2);
}
.se-tag-old{ background:rgba(255,255,255,.05);border:1px solid var(--border2);color:var(--muted); }
.se-tag-new{ background:rgba(34,197,94,.14);border:1px solid rgba(34,197,94,.45);color:#22C55E; }
.se-side .u-tag{ display:inline-block;margin-bottom:12px;padding:5px 12px;border-radius:14px;
  font-family:var(--font-h);font-weight:700;font-size:11px;letter-spacing:1.5px;
  text-transform:uppercase;line-height:1; }
.se-row{ display:flex;flex-wrap:wrap;gap:8px; }
.se-chip{
  display:flex;flex-direction:column;gap:2px;min-width:62px;
  padding:8px 10px;border-radius:10px;background:var(--card2);border:1px solid var(--border);
}
.se-chip strong{ font-family:var(--font-h);font-weight:800;font-size:14px;color:var(--text); }
.se-chip span{ font-family:var(--font-h);font-weight:600;font-size:11px;color:var(--muted);letter-spacing:.04em; }
.se-row.is-new .se-chip{ border-color:rgba(34,197,94,.35); }
.se-row.is-new .se-chip strong{ color:#22C55E; }
.song-edit-page .se-gain{   /* [AUDITORIA] idem: era branco 13.5px */
  margin:12px 0 0;font-family:var(--font-h);font-weight:700;font-size:15px;color:var(--muted);
}
.se-gain strong{ color:#22C55E;font-weight:900;font-size:17px; }

.se-actions{ display:flex;flex-direction:column;gap:12px;margin-top:22px; }
@media (min-width:600px){ .se-actions{ flex-direction:row-reverse;align-items:center } }
.se-practice{ flex:1; }
.se-practice[disabled]{ opacity:.45;pointer-events:none;filter:grayscale(1); }
.se-actions .audio-btn{ min-width:0; }

@media (prefers-reduced-motion:reduce){ .song-card{ transition:none } }

/* ============================================================================
 * [F4] CAMPO HARMÔNICO — controles, grade de graus e detalhe do acorde
 * ========================================================================= */
.harmony-page{ max-width:1000px; }
.hm-controls{ display:flex;flex-direction:column;gap:16px;margin-bottom:24px; }
.hm-group .se-label{ margin-bottom:8px; }
.hm-chips{ display:flex;flex-wrap:wrap;gap:8px; }
.hm-chips-scale{ max-width:820px; }

/* ============================================================
 * [21/07 · item 12 · Luis] DOCK DA HARMONIA
 * Os 3 grupos (tonalidade · escala · complexidade) deixam de ser uma grade que
 * quebra linha e viram uma faixa com SCROLL LATERAL: pílulas QUADRADAS, só
 * algumas na tela, e a do centro em zoom — a gramática do dock do Mac.
 *
 * Por que o zoom é dirigido pelo CENTRO DO SCROLL e não por :hover: no celular
 * não existe hover. O hover entra só como reforço em pointer:fine. O scale vem
 * de JS (hmDock) porque a distância até o centro não é expressável em CSS.
 * ========================================================= */
/* [22/07 · Luis] CARDS GRANDES, UMA COR POR LINHA, CONTORNO NEON.
   Eram 68px com overflow:hidden — e o texto que não coubesse era CORTADO
   ("XTENSÕE", "MELÓDICA" decepado). Agora:
     · 112px  → cabem ~3 na tela do celular, e é essa a troca que o Luis aceitou
     · --dock → cor própria de cada linha (tom · escala · complexidade)
     · o corte acabou: quem garante o encaixe é o hmFitChips (index.html), que
       acha o maior corpo de letra que cabe. O overflow:hidden fica só como rede.
   O tamanho tinha que subir junto com a cor: sem espaço, nenhum ajuste de tipo
   deixaria "MENOR MELÓDICA" legível. */
/* O WRAPPER é quem tem a largura; a faixa ocupa 100% dele.
   Por quê: o padding lateral da faixa é `50% - card/2`, e porcentagem de padding
   resolve contra o PAI. No desktop isso dava 396px de cada lado — e uma caixa
   nunca encolhe abaixo do próprio padding, então o max-width de 452px era
   simplesmente ignorado e o 4º card aparecia fatiado. Com o wrapper, o 50%
   passa a ser metade da FAIXA, e aí a conta fecha. */
/* [22/07 · Luis] "aumente bastante o tamanho dessa frase". O .se-label é o rótulo
   miúdo padrão (11px, caixa alta, cinza); aqui ele vira instrução de verdade. */
.se-chords-label{
  font-size:clamp(19px, 5.2vw, 26px);line-height:1.2;
  letter-spacing:-.2px;text-transform:none;color:var(--text);
  font-family:var(--font-h);font-weight:800;margin-bottom:12px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   [15/09 · Luis] OS SLOTS GIRAM NA VERTICAL, e o campo harmónico cabe na tela.
   Pedido dele: *"campo harmonico os slots giraveis devem ser verticais ao inves
   de horizontais agora, e o campo harmonico inteiro caber na mesma tela seria
   bem interessante"*.

   🔑 AS DUAS COISAS SÃO A MESMA COISA. Três fileiras HORIZONTAIS empilhadas
   gastam três alturas de card mais três rótulos — medido na caixa B (tablet em
   pé): o campo harmónico só começava aos 790 px e ficava quase todo fora da
   tela. Três colunas VERTICAIS lado a lado gastam UMA altura de card. É o que
   liberta o espaço para o campo caber inteiro.

   🔑 E O EIXO PASSA A SER DECISÃO DO CSS. O JS (`__hmEixo`, no index.html)
   pergunta o `flex-direction` ao dock e mede nesse eixo — o zoom, o `is-center`,
   a centragem do escolhido, o clique na borda e a varredura do tour. Estavam os
   cinco cravados em `scrollLeft`; agora há uma peça só para as duas formas.

   ⚠️ `.hm-controls` e `.hm-group` são PARTILHADOS com a tela dos jogos
   (index.html:5889). Por isso tudo o que é layout de coluna leva o prefixo
   `.harmony-page` — o `.hm-dock` e o `.hm-dock-wrap`, esses, só existem aqui.
   ═══════════════════════════════════════════════════════════════════════════ */
.hm-dock-wrap{
  /* --cw = o lado do card (ele é quadrado). Em pé numa tela estreita o que
     manda é a largura da coluna (1/3 da faixa); numa larga, o teto.
     --dh = a altura da janela do dock: 3 cards + 2 vãos. TEM que ser ímpar pela
     mesma razão de sempre — o snap é no centro, e num número par não há como
     centrar um e caber o outro inteiro. */
  --gap:clamp(8px, 1.1vw, 13px);
  --halo:clamp(10px, 2.4vw, 14px);
  /* 🔑 O CARD DO SLOT DEIXA DE SER QUADRADO. Ele era quadrado porque num
     carrossel HORIZONTAL a largura é o recurso escasso. Num vertical o escasso
     é a ALTURA, e a largura é a da coluna inteira — então o card vira uma LINHA
     larga e baixa, que é a forma que um seletor vertical tem em qualquer sistema
     operativo, e não por acaso: dá ao rótulo a largura toda.
     🔴 MEDIDO, e foi o que me obrigou a mudar: com o card quadrado a 90 px, o
     `hmFitChips` encolhia "MENOR HARMÔNICA" até **8,5 px** para caber — abaixo
     de qualquer piso (o do tablet é 14). Na linha larga o mesmo rótulo cabe com
     folga. E a coluna ainda fica MAIS BAIXA: 3 linhas de 64 px contra 3
     quadrados de 90. As duas pontas do pedido do Luis ganham juntas. */
  /* 🔑 [16/09] O PISO SUBIU DE 46 PARA 50, e é uma conta, não um palpite: o
     rótulo do slot quebra em DUAS linhas ("MENOR / HARMÔNICA") e duas linhas a
     14 px com `line-height:1.14` são 32 px, mais 16 de padding = 48. Com 46 não
     cabiam, e o `hmFitChips` encolhia até **11,6 px** — no iPhone SE, onde o ar
     dava 14. Medido: no SE o que prende é a ALTURA (o padding lateral não move
     o número um décimo em quatro valores), e no iPhone 13/14 é a LARGURA. Dois
     telemóveis, dois constrangimentos, e por isso as duas curas.
     Só o SE muda: o piso só morde abaixo de 758 px de altura (6,6vh < 50), e o
     13/14 já estava em 55,7. Os tablets em pé estão no teto (68). Custo medido
     no SE: a página passa de 1212 para 1224 px — e ela já rolava. */
  --ch:clamp(50px, 6.6vh, 68px);
  --cw:100%;
  --dh:calc(var(--ch) * 3 + var(--gap) * 2);
  width:100%; height:var(--dh);
  display:flex; justify-content:center;
}
/* ⚠️ Continuam a ser 3 cards à vista, e continua a ter de ser ÍMPAR: o snap é
   no CENTRO, e num número par não há como centrar um e caber o outro inteiro.
   O que mudou foi o eixo. */
.hm-dock{
  display:flex;flex-direction:column;flex-wrap:nowrap;gap:var(--gap);
  overflow-y:auto;overflow-x:hidden;
  scroll-snap-type:y mandatory;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
  height:var(--dh);
  /* 🔴 O DOCK TEM DE ESTICAR ATÉ À COLUNA. Ele é item de um flex em LINHA (o
     `.hm-dock-wrap`, que centra), e na direcção principal um item dimensiona-se
     ao CONTEÚDO — então cada coluna ficava com a largura do seu rótulo mais
     comprido: 103 px em TONALIDADE contra 237 em ESCALA, com os três grupos a
     medirem 287 iguais. A régua não vê (as caixas continuam simétricas e nada
     se sobrepõe); o olho vê logo, porque os três eixos deixam de ler como um
     conjunto. É o tipo de coisa que só a FOTO denuncia. */
  flex:1 1 auto; min-width:0;
  /* [22/07 · Luis: "linhas invisíveis cortam a propagação do neon"]
     A causa é da spec: com um eixo a rolar, o OUTRO não pode ser `visible` — a
     faixa vira uma caixa que corta em linha reta o que passar das bordas. Não se
     desliga o corte; deixa-se de encostar nele.
     🔑 AO VIRAR O EIXO, O HALO TROCA DE LADO: era o padding VERTICAL que o
     protegia (porque o corte era em cima e em baixo); agora quem corta são as
     laterais, logo é o padding HORIZONTAL que o reserva — e o `margin-inline`
     negativo devolve esse espaço ao layout, como a margem negativa do wrapper
     fazia antes no eixo vertical.
     E o `padding-block` deixa de poder ser `calc(50% - cw/2)`: percentagem de
     padding resolve-se SEMPRE contra a LARGURA, em qualquer um dos quatro lados.
     Num dock horizontal isso calhava certo; num vertical daria um número que não
     tem nada a ver com a altura. Por isso o `--dh` é explícito e a conta usa-o. */
  padding:calc(var(--dh) / 2 - var(--ch) / 2) var(--halo);
  margin-inline:calc(var(--halo) * -1);
}

.hm-dock::-webkit-scrollbar{ display:none; }
/* os três eixos lado a lado — é isto que devolve ~2 alturas de card à página.
   Prefixo `.harmony-page` porque `.hm-controls`/`.hm-group` são dos JOGOS também. */
/* 🔑 [16/09] ESTAS TRÊS LEVAM O ESCAPE NA ORIGEM, e não um remendo por cima.
   Tentei primeiro desfazê-las no bloco `?hv=0` com valores explícitos e apanhei
   o preço: pus `margin-bottom:8px` no `se-label` por ser o que a regra base diz,
   e no COMPUTADOR o valor de antes não era 8 — era 10, do `html.dt .se-label`
   (app.css:10048), que ganhava por vir depois. Resultado medido: o escape deixava
   M1 e M2 com 64 e 65 diferenças contra o ar, todas descendentes desses 2 px.
   Escrever o valor antigo à mão é a armadilha do número medido que envelhece: ele
   é do CONTEXTO, e há quatro contextos. Com `html:not([data-hv="0"])` a regra nova
   simplesmente NÃO EXISTE quando o escape está ligado, e a cascata devolve o que
   cada contexto tinha — sem eu ter de saber quanto era. */
/* 🔴 [16/09 · LOTE 4b] O `.hm-controls` SAIU DO MARKUP DA HARMONIA e a regra que
   o punha em linha saiu com ele. A tela passou a ter QUATRO `.hm-group` iguais
   dentro de uma `.hm-mesa` — não três controlos mais um resultado. A classe
   `.hm-controls` continua viva na tela dos JOGOS (index.html:5914), por isso a
   regra BASE dela fica onde está; o que saiu foi só a versão desta tela.
   As duas que ficam valem para os quatro grupos, e mantêm o `html` à frente pela
   razão de sempre: sem ele caíam de (0,2,1) para (0,2,0) e perdiam desempates
   que hoje ganham — o tipo de mudança que não se vê no ficheiro e aparece três
   telas adiante. */
/* 🔑 [16/09 · LOTE 4g] AS MEDIDAS DA FOLGA VIVEM AQUI, NO GRUPO, e não no
   `.hm-dock-wrap` onde o `--halo` vivia: quem precisa delas agora são DOIS
   ramos irmãos — a faixa (que reserva) e o rótulo (que se centra na folga).
   Um valor lido em dois sítios tem de nascer acima dos dois, senão volta a
   haver duas cópias do mesmo número a envelhecer em silêncio. */
html .harmony-page .hm-group{
  min-width:0; display:flex; flex-direction:column; position:relative;
  --halo:clamp(9px, 2.2vw, 14px);
  /* o alcance REAL do brilho: `--reach` com `--near:1`, que é o slot do centro */
  --sobra:calc(var(--halo) * .4 + 18px);
  /* metade do vão da mesa — espaço que já estava vazio, logo é de graça */
  --fora:calc(var(--hm-banda) / 2);
  /* o resto, que sai do tamanho do card */
  --dentro:max(0px, calc(var(--sobra) - var(--fora)));
}
html .harmony-page .hm-group .se-label{ text-align:center; margin-bottom:5px; }
/* a folha inteira numa tela: o herói desta tela não é de leitura, é um cabeçalho.
   🔴 [16/09 · lote 3, 2ª volta] ESTAS TRÊS SÃO DO TABLET E ESTAVAM NO CORPO, sem
   `tb` nem `dc` — logo chegavam ao TELEMÓVEL, que é a caixa declarada "BASE, não
   pode mudar". Medido no censo contra o ar (base-325 × depois-171):

       P1 e P2 · `toc-mega-title`  34 px → **26**      (o clamp resolve 4.2vw=16 em 390px)
       P1 e P2 · `lead`            15 px → **13**
       P1      · `toc-hero` altura 93,2 → 78,8

   E no telemóvel **não compraram nada**: o que elas existem para pagar é o "caber
   numa tela", e o telemóvel continua a não caber (`sobra 373` no P1, 515 no P2) —
   sete cartões de acorde não entram em 844 px de altura, com ou sem estes 8 px de
   título. Era orçamento tirado a quem não tinha a dívida.
   🔑 E quase me escapou: o comparador do censo faz `break` no PRIMEIRO campo que
   difere (x,y,w,h,fs), logo um elemento que mudou de posição E de corpo de letra
   é contado como 'x' e o `fs` nunca aparece. Eu ia cravar um critério de "nenhuma
   letra mudou" em cima desse ponto cego. Mediu-se campo a campo, à mão.
   Nos tablets nada muda: em `tb` quem manda no título é `html.tb .special h1`
   (app.css:10310, especificidade 0,2,1) e em paisagem o bloco 18. */
html.tb .harmony-page .toc-hero, html.dc .harmony-page .toc-hero{ margin-bottom:clamp(10px, 1.8vh, 18px); }
html.tb .harmony-page .toc-mega-title, html.dc .harmony-page .toc-mega-title{ font-size:clamp(26px, 4.2vw, 40px); }
html.tb .harmony-page .lead, html.dc .harmony-page .lead{ font-size:clamp(13px, 1.5vw, 16px); margin-bottom:0; }
/* cor por linha — neon frio → verde → laranja da marca */
.hm-dock.dock-tom  { --dock:#38BDF8; }
.hm-dock.dock-scale{ --dock:#22C55E; }
.hm-dock.dock-cx   { --dock:#F2790E; }
/* 🔑 [16/09] O TAMANHO DO CHIP SAIU DAQUI PARA DUAS REGRAS IRMÃS, logo abaixo —
   uma por desenho, as duas nesta MESMA POSIÇÃO do ficheiro e com `:where()` no
   gancho para não inflarem a especificidade. A razão é o computador: o padding
   lateral do chip no `dt` vem do `html.dt .hm-chip{padding:0 18px}`
   (app.css:10071), que empata em especificidade com esta regra e ganha POR VIR
   DEPOIS. Quando eu restaurava o desenho antigo lá no fim do ficheiro, passava à
   frente do `dt` e o texto do chip saltava 10 px — 26 diferenças por caixa em M1
   e M2 contra o ar, medidas. Com as duas irmãs aqui, o `dt` continua a vir depois
   e continua a ganhar, nos dois desenhos, exactamente como sempre ganhou. */
.hm-dock .hm-chip{
  flex:0 0 auto;scroll-snap-align:center;
  /* o .hm-chip base usa .10em; aqui é .02em porque o espaçamento entre letras
     é o que mais engorda rótulo longo em CAIXA ALTA ("Harmonisches Moll"), e
     cada milímetro poupado é corpo de letra a mais no ajuste. */
  letter-spacing:.02em;text-align:center;line-height:1.14;
  white-space:normal;overflow:hidden;
  background:
    radial-gradient(120% 100% at 50% 0%, color-mix(in srgb, var(--dock) 12%, transparent), transparent 70%),
    rgba(16,16,16,.72);
  border:1.5px solid color-mix(in srgb, var(--dock) 38%, transparent);
  color:color-mix(in srgb, var(--dock) 62%, #FFFFFF);
  /* NEON: anel interno fino + halo externo. Duas sombras, não uma borda grossa
     — borda grossa fica pesada no escuro; o halo é o que lê como neon. */
  /* o alcance de TODA sombra (blur − spread) fica <= --halo: é o que garante que
     o brilho termine dentro do quadro em vez de esbarrar na borda que corta. */
  box-shadow:0 0 0 1px color-mix(in srgb, var(--dock) 14%, transparent) inset,
             0 0 calc(var(--reach) + 4px) -4px color-mix(in srgb, var(--dock) 34%, transparent);   /* extensão = blur+spread = --reach */
  transform-origin:center bottom;
  transition:background .16s var(--e-out,ease),border-color .16s var(--e-out,ease),
             color .16s var(--e-out,ease);
}
/* o desenho de HOJE: LINHA larga e baixa (o carrossel é vertical) */
/* 🔴 [16/09 · LOTE 4] era `html:where(:not([data-hv="0"]))`, com o `:where()` a
   zerar o peso do guarda de propósito. Sem o escape fica `html`, que dá a MESMA
   especificidade (0,2,1) — e continua a ser essa igualdade que deixa o
   `html.dt .hm-chip{padding:0 18px}` ganhar por ordem, como sempre ganhou. */
html .hm-dock .hm-chip{
  /* 6 px de padding lateral, não 10: é o mesmo lever que o bloco 18e mediu para
     o telemóvel deitado (40 px de coluna poupados por 1,2 px de letra), e no
     iPhone 13/14 vale 13,2 → 14,4 px de rótulo. O halo do neon não depende
     disto — quem o reserva é o `--halo` do `.hm-dock`. */
  width:100%; height:var(--ch); min-height:0; padding:8px 6px;
  border-radius:16px;                       /* linha de cantos suaves */
}
/* 🔴 [16/09 · LOTE 4] AQUI ESTAVA O CHIP QUADRADO — o desenho do AR, que o
   `?hv=0` devolvia. Saiu com o escape (lápide do bloco 18j): sem quem acenda o
   `data-hv`, era um segundo desenho do chip a envelhecer num ficheiro onde
   ninguém o veria falhar. */
/* [08/09 · ANDROID_RESTO] o `will-change:transform` estava AQUI, na regra base —
 * e cada pílula do dock virava uma camada composta PERMANENTE. Medido na
 * ANDROID-RESTO-BANCADA (censo2.cjs, Android emulado): a tela da harmonia
 * desenhava 37 camadas contra 8 de todas as outras telas — 26 `BUTTON.hm-chip`
 * com razão `WillChangeTransform`, mais 3 camadas de TEXTO promovidas por
 * `Overlap` (1,9 MB) por estarem ao lado delas. É o mesmo estrago do
 * backdrop-filter do 278/279, causa outra.
 * O `will-change` só vale enquanto o transform ESTÁ a mudar — e aqui ele só
 * muda quando o dock ROLA (hmDockUpdate no scroll). Fora disso a promessa
 * custa uma superfície e não compra nada. O `hmDockInit` acende a classe no
 * scroll e apaga-a no repouso. */
.hm-dock.is-rolando .hm-chip{ will-change:transform; }
/* --near: 0 na borda da faixa, 1 no centro. Escrito pelo hmDockUpdate (a mesma
   curva do zoom). O ALCANCE do brilho segue essa curva — é o que permite manter
   card grande sem nunca cortar neon: no centro sobra tela de monte pro halo
   crescer; na ponta ele naturalmente recolhe pra dentro da folga. */
/* O alcance MÍNIMO (na borda) é derivado do próprio --halo, não de um número
   solto: é a folga que existe ali. Assim a garantia "o neon nunca é cortado"
   vale sozinha em qualquer largura, inclusive quando halo encolhe com a tela. */
/* 🔴🔑 [16/09 · LOTE 4h · Luis] *"ainda existe uma linha imaginaria cortando o
   efeito radiante dos cartoes, mas agora a linha ja esta quase no final do
   efeito"* — e o "quase" é a medida do defeito que sobrou: **16 %**.
   A CAUSA: `transform:scale()` escala TAMBÉM a sombra. O card do meio é pintado
   a 1,16, logo o halo dele sai a `--reach × 1,16` de píxeis REAIS, enquanto a
   folga reservada (`--sobra`, no `.hm-group`) continua a valer `--reach`. Medido
   numa réplica isolada do chip, coluna a coluna: a `--reach` do bordo o resto do
   brilho ainda vale **+2/255** sobre o fundo — é isso que se lê como uma recta.
   A CURA NÃO PAGA EM TAMANHO DE CARD, e é por isso que é esta: em vez de
   aumentar a folga (cada píxel de folga custa DOIS de card — a conta do `--cw`
   reserva-a dos dois lados), divide-se o alcance pela escala. O que fica pintado
   é `halo*.4 + 18*near` píxeis reais, EXACTAMENTE o que a geometria reservou.
   Medido na mesma réplica: com a divisão, o resíduo ao bordo da folga é **0**.
   ⚠️ `--esc` é escrito pelo `hmDockUpdate` a cada quadro da rolagem, ao lado do
   `--near` e do próprio `transform` — um dono só para os três números da curva.
   O `1` daqui é o valor de repouso (e o que vale sob `reduced-motion`, onde o
   `transform` é desligado e a divisão tem de sair do caminho — ver abaixo). */
/* 🔑 [18/09 · DESEMPENHO · C9b] ESTES DOIS NÚMEROS NÃO HERDAM.
   O `hmDockUpdate` escreve `--near` e `--esc` em CADA chip a CADA quadro da rolagem, e
   uma custom property por omissão é HERDADA: mudá-la obriga o Blink a recalcular o
   estilo de toda a SUBÁRVORE do chip — e o chip do grau leva um mini teclado em SVG
   dentro (≈25 nós). MEDIDO a 6x na fileira a rolar: recálculo de estilo 637 ms em 3,5 s.
   Quem os lê é só a regra do PRÓPRIO chip (o `--reach` aqui em baixo e o da fileira dos
   graus, bloco 19n) — nenhum descendente os consulta. Declarados com `inherits:false`,
   a escrita passa a invalidar um elemento em vez de uma árvore.
   O `initial-value` repete o que a regra abaixo já dizia (0 e 1), para o caso de a
   propriedade ser lida antes de a regra bater. */
@property --near{ syntax:'<number>'; inherits:false; initial-value:0 }
@property --esc{ syntax:'<number>'; inherits:false; initial-value:1 }
.hm-dock .hm-chip{ --near:0;--esc:1;--reach:calc((var(--halo) * .4 + 18px * var(--near)) / var(--esc)); }
.hm-dock .hm-chip-t{
  display:block;width:100%;
  font-size:15px;                            /* ponto de partida; o hmFitChips ajusta */
  /* SEM overflow-wrap aqui de propósito: com ele, uma palavra longa demais
     QUEBRAVA no meio em vez de transbordar — e a medida de largura do
     hmFitChips nunca disparava, então a letra nunca encolhia. A ordem certa é
     encolher primeiro; partir a palavra só em .is-tight, quando nem o corpo
     mínimo resolve. */
}
/* último recurso quando nem no corpo mínimo cabe: parte a palavra em vez de
   cortá-la. Só entra com a classe — quebrar no meio é feio e é para ser raro. */
.hm-dock .hm-chip.is-tight{ letter-spacing:0; }
.hm-dock .hm-chip.is-tight .hm-chip-t{ overflow-wrap:anywhere;hyphens:auto; }
/* [22/07 · Luis] ESCALA BLOQUEADA no Campo Harmônico: "apenas ficam preto e
   branco bloqueados". grayscale mata a cor da linha (o --dock verde) sem apagar
   o card — dá pra ler o nome da escala, que é o ponto. Inerte no clique. */
/* 🔴 [05/08 · 4ª rodada · Luis] "não deixe nenhuma caixa preto e branco com
   comportamento de bloqueado, comportamento de versão grátis: deixe TUDO COLORIDO
   durante a demonstração."
   No tour a tela é VITRINE — mostrar cadeado é vender o que ela não tem. Fora do
   tour o bloqueio continua idêntico, que é o que o `body.dia0-tour` delimita.
   ⚠️ Só o VISUAL cede: o chip segue inerte (o Mixolídio é escala paga e o clique
   dele continua sem efeito) — a demo mostra, não libera. */
body.dia0-tour .hm-dock .hm-chip.is-blocked{ filter:none; opacity:1; }
body.dia0-tour .hm-dock .hm-chip.is-blocked::after{ display:none; }
.hm-dock .hm-chip.is-blocked{
  filter:grayscale(1);opacity:.5;cursor:default;
  border-color:rgba(255,255,255,.22);color:rgba(255,255,255,.55);
}
.hm-dock .hm-chip.is-blocked:hover{filter:grayscale(1);opacity:.5}

/* A do centro ganha um degrau de brilho — mas NÃO texto branco nem anel cheio.
   Esses dois são exclusividade da SELECIONADA: com os dois estados no mesmo
   nível, o card que está só passando pelo centro parecia o escolhido. */
.hm-dock .hm-chip.is-center:not(.is-on){
  color:color-mix(in srgb, var(--dock) 30%, #FFFFFF);
  border-color:color-mix(in srgb, var(--dock) 58%, transparent);
  box-shadow:0 0 0 1px color-mix(in srgb, var(--dock) 20%, transparent) inset,
             0 0 calc(var(--reach) + 6px) -6px color-mix(in srgb, var(--dock) 46%, transparent);
}
/* SELECIONADA: neon cheio. Não é preenchimento sólido de propósito — o sólido
   apagava o efeito de contorno que o Luis pediu; aqui o anel é que grita. */
.hm-dock .hm-chip.is-on{
  background:
    radial-gradient(120% 100% at 50% 0%, color-mix(in srgb, var(--dock) 30%, transparent), transparent 72%),
    rgba(10,10,10,.86);
  border-width:2.5px;border-color:var(--dock);color:#fff;
  box-shadow:0 0 0 1px color-mix(in srgb, var(--dock) 55%, transparent) inset,
             0 0 calc(var(--reach) * .6 + 8px) -4px color-mix(in srgb, var(--dock) 75%, transparent),
             0 0 calc(var(--reach) + 6px) -6px color-mix(in srgb, var(--dock) 52%, transparent);
  text-shadow:0 0 12px color-mix(in srgb, var(--dock) 65%, transparent);
}
@media (hover:hover) and (pointer:fine){
  .hm-dock .hm-chip:not(.is-on):hover{
    color:#fff;border-color:color-mix(in srgb, var(--dock) 70%, transparent);
    box-shadow:0 0 0 1px color-mix(in srgb, var(--dock) 22%, transparent) inset,
               0 0 calc(var(--reach) + 6px) -6px color-mix(in srgb, var(--dock) 52%, transparent);
  }
}
@media (prefers-reduced-motion:reduce){
  .hm-dock{ scroll-behavior:auto }
  /* 🔴🔑 [16/09 · LOTE 4h] E O `--esc` TEM DE VIR JUNTO. Aqui o zoom é desligado,
     logo a sombra não é escalada — e o `--reach` dividido pela escala pintaria um
     halo 16 % MAIS CURTO do que a folga, sem razão nenhuma. O `!important` é o
     que ganha ao valor que o `hmDockUpdate` escreve INLINE (um inline sem
     `!important` perde para um `!important` de folha de estilo), sem obrigar o JS
     a saber o que esta media query decide.
     ⚠️ E é por isto que a BANCADA NÃO VIA O DEFEITO: as dez caixas correm com
     `reducedMotion:'reduce'` (caixas.cjs:50), ou seja mediam um dock SEM zoom —
     cards mais pequenos e halo mais curto do que o aluno vê. A régua desta cura
     corre com o zoom LIGADO, que é o estado que o Luis tem no aparelho. */
  .hm-dock .hm-chip{ transform:none!important; --esc:1!important }
}
.hm-chip{
  padding:8px 14px;min-height:40px;border-radius:999px;   /* [AUDITORIA] era 32-35px */
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--card2);border:1px solid var(--border2);color:var(--muted);
  font-family:var(--font-h);font-weight:700;font-size:11.5px;
  letter-spacing:.10em;text-transform:uppercase;cursor:pointer;white-space:nowrap;
  transition:background .14s cubic-bezier(.2,.8,.2,1),border-color .14s cubic-bezier(.2,.8,.2,1),color .14s cubic-bezier(.2,.8,.2,1);
}
.hm-chip.is-on{
  background:var(--accent,var(--primary));border-color:var(--accent,var(--primary));
  color:var(--text);box-shadow:0 4px 14px color-mix(in srgb, var(--accent,var(--primary)) 40%, transparent);
}
.hm-chip.is-static{ cursor:default;text-transform:none;letter-spacing:.04em; }
@media (hover:hover) and (pointer:fine){
  .hm-chip:not(.is-on):not(.is-static):hover{ color:var(--text);border-color:var(--accent,var(--primary)); }
}

/* grade dos 7 graus */
/* [16/09 · LOTE 4] regra de `.hm-grid` RETIRADA: a classe saiu do DOM com a grelha dos sete (ver a lápide dos blocos 18-18e). */
.hm-card{
  /* [22/07] era column (só texto). Virou linha pra abrigar o mini piano à
     direita — o tamanho do card NÃO mudou, só o uso do espaço que sobrava. */
  display:flex;flex-direction:row;align-items:center;gap:4px;
  padding:16px;border-radius:12px;text-align:left;cursor:pointer;
  background:rgba(18,18,18,.34);  border:1px solid var(--border2);border-left:3px solid var(--accent,var(--primary));
  transition:transform .14s cubic-bezier(.2,.8,.2,1),background .14s cubic-bezier(.2,.8,.2,1);
}
.hm-card:active{ transform:scale(.98); }
/* [06/08 · L10 item 01] a pílula PRATICAR do card: VERDE (a cor do mini piano),
   texto preto, TUDO MAIÚSCULO, e em posição FIXA — encostada no mini piano
   (margin-left:auto a empurra até ele, independente da largura da cifra).
   É um span (o card já é <button> — botão dentro de botão é HTML inválido e
   quebra o clique); a delegação trata primeiro. */
/* 🔴 [16/09 · LOTE 4] AQUI ESTAVA A PÍLULA `.hm-prat`, e ela SAIU DO DOM.
   Nasceu em 06/08 (L9·10) dentro de cada cartão de acorde; morreu quando o Luis
   trocou os sete cartões pelos blocos e pediu "um PRATICAR só, fora da fileira".
   🔴 E DEIXAR ESTA REGRA AQUI ERA UM VERDE FALSO, não uma sobra inofensiva: o
   `validate_dia0_ajustes` R10·01 cobra o TEXTO desta regra no ficheiro
   (`/\.hm-prat\{ flex:0 0 auto; align-self:center; margin-left:auto/`). Com a
   pílula fora do DOM e a regra dentro do CSS, a régua assinava verde uma peça
   que não existe — a forma exacta do "régua verde não é verdade" desta casa.
   Sai a regra, e a asserção foi reescrita com data e ordem. */
/* no tour a pílula é vitrine como o resto (a trava de clique geral já cobre) */
@media (hover:hover) and (pointer:fine){
  .hm-card:hover{ transform:translateY(-2px);background:color-mix(in srgb, var(--accent,var(--primary)) 7%, var(--card)); }
}
.hm-roman{
  font-family:var(--font-h);font-weight:800;font-size:11px;
  letter-spacing:.14em;color:var(--accent,var(--primary));
}
.hm-cifra{ font-family:var(--font-h);font-weight:900;font-size:26px;letter-spacing:-.5px;color:var(--text);line-height:1; }
.hm-nome{ font-family:var(--font-b);font-size:12px;color:var(--muted);line-height:1.3; }

/* [22/07 · Luis] O bloco .harm-detail (overlay do grau) foi REMOVIDO junto com
   a popup. O que ele mostrava — o teclado com a formação — virou o mini piano
   impresso no próprio card, abaixo. */

/* ---- card de acorde: rótulo à esquerda, mini piano à direita ---- */
/* 🔴 [06/08 · L10 item 01, 3ª medição] quem CEDE espaço agora é o TEXTO (quebra
   de linha), não o piano: com o rótulo incompressível, nos acordes estendidos o
   shrink comia a caixa do piano (112→59) e a pílula pulava de posição. Texto
   flexível + pílula e piano RÍGIDOS = pílula no mesmo x em todo card. */
.hm-card-txt{ display:flex;flex-direction:column;align-items:flex-start;gap:4px;
  flex:1 1 auto;min-width:0; }
.hm-card-kbd{
  /* 🔴 [06/08 · L10 item 01, 2ª volta] o piano TAMBÉM tinha margin-left:auto —
     com DOIS autos no card (pílula + piano) o espaço livre rachava no meio e a
     pílula flutuava conforme a largura da cifra ("cada card numa posição").
     Agora só a PÍLULA tem o auto: ela absorve toda a folga e fica em posição
     FIXA junto do piano, em todos os cards.
     ⚠️ 2ª medição: o PRÓPRIO PIANO muda de largura (59px num acorde de 1 oitava,
     110px numa tétrade estendida) — com a caixa no tamanho do desenho a pílula
     ainda pulava. A caixa agora é FIXA em 112px (a maior medida) e o desenho
     ancora à direita dentro dela: a pílula cai no MESMO x em todo card. */
  flex:0 0 112px;min-width:56px;
  display:flex;align-items:center;justify-content:flex-end;align-self:stretch;
}
/* dimensionado pela ALTURA, com a largura seguindo a proporção do desenho:
   com width:100% + preserveAspectRatio "meet" o SVG ocupava a caixa toda mas
   desenhava o piano MENOR e centrado nela — sobrava um vão à direita que fazia
   o card parecer desalinhado. */
.mk-svg{ height:58px;width:auto;max-width:100%;display:block;overflow:visible; }
.mk-k{ transition:fill .18s var(--e-out,ease),filter .18s var(--e-out,ease); }
.mk-w{ fill:#2A2A2A;stroke:#000;stroke-width:.5; }
.mk-b{ fill:#0B0B0B;stroke:#000;stroke-width:.5; }
/* as notas do acorde no verde da página — o card inteiro já é verde (borda,
   grau romano); pintá-las de laranja abriria uma 2ª linguagem de cor na tela */
.mk-k.is-on{ fill:color-mix(in srgb, var(--accent,var(--primary)) 62%, #0A0A0A); }
.mk-k.is-on.is-root{ fill:var(--accent,var(--primary)); }
/* eco visual do arpejo: acende nota a nota, no mesmo compasso de 55ms do som */
.mk-k.is-hit{
  fill:#FFFFFF;
  filter:drop-shadow(0 0 5px color-mix(in srgb, var(--accent,var(--primary)) 85%, transparent));
}
.hm-card.is-playing{
  border-color:color-mix(in srgb, var(--accent,var(--primary)) 55%, transparent);
  box-shadow:0 0 18px -4px color-mix(in srgb, var(--accent,var(--primary)) 55%, transparent);
}

@media (prefers-reduced-motion:reduce){
  .hm-chip,.hm-card,.mk-k{ transition:none }
  .hm-card:active,.hm-card:hover{ transform:none }
}

/* ============================================================================
 * [F6] HUB DE JOGOS + PLAYER DE QUIZ
 * ========================================================================= */
.gm-hub{ display:grid;grid-template-columns:1fr;gap:10px;max-width:760px; }
@media (min-width:680px){ .gm-hub{ grid-template-columns:1fr 1fr } }
.gm-card{
  display:flex;align-items:center;gap:14px;text-align:left;cursor:pointer;
  padding:16px;border-radius:12px;
  background:rgba(18,18,18,.34);  border:1px solid var(--border2);border-left:3px solid var(--accent,var(--primary));
  transition:transform .14s cubic-bezier(.2,.8,.2,1),background .14s cubic-bezier(.2,.8,.2,1);
}
/* [22/07 · Luis] JOGO BLOQUEADO = cadeado laranja e nada mais. Sem redirect pro
   checkout (pedido explícito) e sem acinzentar o card inteiro: o aluno tem que
   LER o que está bloqueado — é isso que dá vontade de comprar. */
.gm-card.is-locked{cursor:default;opacity:.62}
.gm-card.is-locked .gm-txt strong,.gm-card.is-locked .gm-txt em{color:var(--muted)}

.gm-card.is-locked:active{transform:none}
.gm-lock{flex:0 0 auto;margin-left:auto;display:flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:50%;color:var(--primary);
  background:rgba(232,105,10,.12);border:1px solid rgba(232,105,10,.42)}
.gm-lock svg{width:16px;height:16px;display:block}
@media (hover:hover) and (pointer:fine){ .gm-card.is-locked:hover{transform:none;background:rgba(18,18,18,.34)} }

/* [21/07] o "Montar as inversões" (kind=drill) tinha a borda VERDE pra marcar
 * execução ≠ reconhecimento. Luis pediu tudo laranja como os outros — a regra
 * caiu e o card volta ao --accent do .gm-card base. */
.gm-card:active{ transform:scale(.99); }
@media (hover:hover) and (pointer:fine){
  .gm-card:hover{ transform:translateY(-2px);background:color-mix(in srgb, var(--accent,var(--primary)) 7%, var(--card)); }
}
.gm-ico{ flex:none;width:42px;height:42px;display:flex;align-items:center;justify-content:center;
  border-radius:10px;background:var(--card2);border:1px solid var(--border2);color:var(--accent,var(--primary)); }
.gm-ico svg{ width:22px;height:22px; }
.gm-txt{ flex:1;min-width:0;display:flex;flex-direction:column;gap:3px; }
.gm-txt strong{ font-family:var(--font-h);font-weight:800;font-size:15px;color:var(--text); }
.gm-txt em{ font-family:var(--font-b);font-style:normal;font-size:12.5px;color:var(--muted);line-height:1.35; }
.gm-best{ flex:none;font-family:var(--font-h);font-weight:800;font-size:10px;
  letter-spacing:.10em;text-transform:uppercase;color:var(--accent,var(--primary)); }
/* [AUDITORIA] o rótulo 'ainda não jogou' foi REMOVIDO do markup: aparecia 6×,
   dominava a coluna direita e empurrava os títulos pra 2 linhas. O vazio é
   informação. Regra mantida vazia por segurança se alguém reintroduzir. */
.gm-best.is-new{ display:none; }
/* [AUDITORIA] eram 3 `!important`, que derrotavam os overrides de .lang-en,
   .lang-es e html.dt — no desktop o título ficava travado no teto do clamp
   mobile. (0,2,0) vence `.special h1` sem forçar, como manda o bloco E. */
.games-page .gm-setup-title, .game-setup-page .gm-setup-title{
  font-size:clamp(24px,6vw,38px);border-left:none;padding-left:0;margin:0; }

/* ---- player ---- */
.quiz-page{ display:flex;flex-direction:column;min-height:100vh;
  padding:18px clamp(14px,4vw,32px) calc(96px + env(safe-area-inset-bottom)); }
.qz-top{ display:flex;align-items:center;gap:12px;margin-bottom:20px; }
/* ===== lote 21/07 — backlog dos jogos (itens 3, 7, 9, 10, 11) ===== */
/* [item 3] nome do jogo. Fica FORA do .qz-top (flex de 3 colunas) pra não
 * espremer os dots; aceita 2 linhas — DE ("Überraschungstonart") e FR
 * ("Formation de l'oreille") não cabem em uma no iPhone SE. */
.qz-title{ font-family:var(--font-h);font-weight:900;font-size:clamp(19px,5.2vw,26px);
  letter-spacing:-.4px;line-height:1.15;color:var(--text);text-align:center;margin:-6px 0 14px; }
/* [item 11] barra de tempo própria — antes o relógio dividia o elemento do contador */
.qz-timebar{ height:4px;border-radius:999px;background:rgba(255,255,255,.10);overflow:hidden;margin:0 0 16px; }
.qz-timebar-fill{ display:block;height:100%;width:100%;border-radius:999px;background:var(--accent,var(--primary)); }
.qz-timebar.is-urgente .qz-timebar-fill{ background:#EF4444; }
.qz-timeup{ font-family:var(--font-h);font-weight:800;font-size:12px;letter-spacing:.12em;
  text-transform:uppercase;color:#EF4444;margin-bottom:8px;text-align:center; }
/* [itens 7 e 10] revelação com os DOIS acordes lado a lado */
.qz-two{ display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:nowrap; }
.qz-two-lado{ flex:1 1 0;min-width:0;text-align:center; }
.qz-two-lado svg{ width:100%;height:auto; }
.qz-two-lado .qz-legenda{ font-size:15px;margin-bottom:6px; }
.qz-two-seta{ flex:0 0 auto;font-style:normal;font-size:20px;color:var(--muted); }
/* a nota que FICA PARADA, acesa nos dois teclados — é a lição do jogo */
.qz-two rect[data-semi].qz-comum{ fill:var(--accent,var(--primary))!important; }
/* [item 9] "ouvir esta opção" dentro do botão de resposta */
.qz-opt-ear{ display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  width:30px;height:30px;margin-left:auto;border-radius:50%;color:var(--muted);
  border:1px solid var(--border2);background:rgba(255,255,255,.03); }
.qz-opt-ear svg{ width:15px;height:15px; }
@media (hover:hover) and (pointer:fine){ .qz-opt-ear:hover{ color:var(--text);border-color:var(--accent,var(--primary)) } }
/* [item 4 · REQ A1] preview ao vivo do configurador de inversões */
.gm-preview{ margin:6px auto 16px;max-width:520px; }
.gm-preview svg{ width:100%;height:auto;display:block; }
/* [22/07 · Luis] o preview virou instrumento: é TOCÁVEL e tem "ouvir
   referência". Não escuta — a detecção fica na prática. */
.gm-preview .kbd-svg{ cursor:pointer;touch-action:none; }   /* touch-action:none = glissando não vira scroll */
.game-setup-page .gm-bench{ margin-top:2px;margin-bottom:20px; }
.game-setup-page .gm-bench .audio-btn{ min-width:0;padding:10px 18px; }
.game-setup-page .gm-hear{ background:rgba(232,105,10,.10);border-color:var(--primary);color:var(--primary); }
@media (prefers-reduced-motion:reduce){ .qz-timebar-fill{ transition:none!important } }
.qz-dots{ flex:1;display:flex;gap:5px;flex-wrap:wrap; }
.qz-dot{ width:8px;height:8px;border-radius:50%;background:var(--border2);
  transition:background .2s cubic-bezier(.2,.8,.2,1),transform .2s cubic-bezier(.2,.8,.2,1); }
.qz-dot.is-now{ background:var(--accent,var(--primary));transform:scale(1.35); }
.qz-dot.is-hit{ background:#22C55E; }
.qz-dot.is-miss{ background:#EF4444; }
.qz-count{ flex:none;font-family:var(--font-h);font-weight:900;font-size:13px;
  color:var(--muted);font-variant-numeric:tabular-nums; }
.qz-body{ flex:1;display:flex;flex-direction:column;max-width:680px;width:100%;margin:0 auto; }

/* estímulo: mesmo lugar, qualquer que seja o tipo */
.qz-stimulus{ min-height:120px;display:flex;flex-direction:column;align-items:center;justify-content:center;
  margin-bottom:18px; }
.qz-stimulus svg{ width:100%;height:auto;max-width:520px;display:block; }
.qz-legenda{ font-family:var(--font-h);font-weight:900;font-size:22px;letter-spacing:-.4px;color:var(--text);margin-bottom:10px; }
.qz-big{ font-family:var(--font-h);font-weight:900;font-size:clamp(48px,14vw,84px);
  letter-spacing:-2px;color:var(--text);line-height:1; }
.qz-pair{ display:flex;align-items:center;gap:14px;font-family:var(--font-h);font-weight:900;
  font-size:clamp(28px,8vw,44px);letter-spacing:-1px;color:var(--text); }
.qz-pair i{ font-style:normal;color:var(--accent,var(--primary));font-size:.7em; }
.qz-pair-wide{ font-size:clamp(20px,5.5vw,30px);flex-wrap:wrap;justify-content:center; }
.qz-ear{ display:flex;flex-direction:column;align-items:center;gap:10px;cursor:pointer;
  padding:22px 34px;border-radius:16px;background:var(--card2);
  border:1px solid var(--accent,var(--primary));color:var(--accent,var(--primary)); }
.qz-ear svg{ width:40px;height:40px; }
.qz-ear span{ font-family:var(--font-h);font-weight:800;font-size:11px;letter-spacing:.14em;text-transform:uppercase; }
.qz-ear{ transition:transform .14s cubic-bezier(.2,.8,.2,1); }
.qz-ear:active{ transform:scale(.97); }

.qz-prompt{ font-family:var(--font-h);font-weight:800;font-size:clamp(16px,4.4vw,20px);
  color:var(--text);text-align:center;margin:0 0 18px;line-height:1.35; }

/* opções: alvos grandes, 2 colunas */
.qz-options{ display:grid;grid-template-columns:1fr 1fr;gap:10px; }
.qz-opt{ display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  min-height:68px;padding:12px;border-radius:12px;cursor:pointer;
  background:var(--card2);border:1.5px solid var(--border2);color:var(--text);
  transition:background .14s cubic-bezier(.2,.8,.2,1),border-color .14s cubic-bezier(.2,.8,.2,1),transform .1s cubic-bezier(.2,.8,.2,1); }
.qz-opt-lbl{ font-family:var(--font-h);font-weight:800;font-size:15px;text-align:center;line-height:1.2; }
.qz-mini{ font-family:var(--font-h);font-weight:600;font-size:11px;color:var(--muted);letter-spacing:.06em; }
.qz-opt:active{ transform:scale(.97); }
@media (hover:hover) and (pointer:fine){ .qz-opt:not(:disabled):hover{ border-color:var(--accent,var(--primary)) } }
.qz-opt.is-right{ background:rgba(34,197,94,.16);border-color:#22C55E;color:#22C55E; }
.qz-opt.is-wrong{ background:rgba(239,68,68,.16);border-color:#EF4444;color:#EF4444; }
.qz-opt:disabled{ cursor:default; }

/* a explicação — o que separa um quiz de um professor */
.qz-why{ margin-top:14px; }
.qz-why-box{ padding:14px 16px;border-radius:12px;
  background:rgba(34,197,94,.08);border:1px solid rgba(34,197,94,.35);
  border-left:3px solid #22C55E;color:var(--text);font-size:14px;line-height:1.5; }
.qz-next{ margin-top:14px; }

/* fim de rodada */
.qz-end{ max-width:520px;width:100%;margin:24px auto 0;text-align:center; }
.qz-end-score{ font-family:var(--font-h);font-weight:900;font-size:72px;line-height:1;
  letter-spacing:-3px;color:var(--accent,var(--primary)); }
.qz-end-score small{ font-size:30px;color:var(--muted);letter-spacing:-1px; }
.qz-end-pts{ font-family:var(--font-h);font-weight:800;font-size:16px;color:var(--text);margin-top:8px; }
.qz-record{ display:inline-block;margin-left:6px;padding:3px 10px;border-radius:999px;
  background:rgba(34,197,94,.16);border:1px solid rgba(34,197,94,.5);color:#22C55E;
  /* [AUDITORIA] herdava Inter 400 minúsculo com tracking de micro-label */
  font-family:var(--font-h);font-weight:800;text-transform:uppercase;
  font-size:10px;letter-spacing:.14em; }
.qz-end-combo{ font-family:var(--font-b);font-size:13px;color:var(--muted);margin-top:6px; }
.qz-end-actions{ display:flex;flex-direction:column;gap:10px;margin-top:24px; }

@media (prefers-reduced-motion:reduce){
  .gm-card,.qz-opt,.qz-dot{ transition:none }
  .gm-card:hover,.gm-card:active,.qz-opt:active,.qz-ear:active{ transform:none }
}

/* ============================================================================
 * [F7] XP · OFENSIVA · NÍVEIS — painel no modal de conta
 * ========================================================================= */
.gf-panel{ margin:0 0 20px;padding:16px;border-radius:14px;
  background:var(--card2);border:1px solid var(--border2); }
.gf-row{ display:flex;align-items:center;gap:16px;margin-bottom:14px; }
.gf-streak{ flex:none;display:flex;flex-direction:column;align-items:center;gap:1px;
  min-width:76px;padding:10px 8px;border-radius:12px;
  background:rgba(255,255,255,.04);border:1px solid var(--border2); }
.gf-streak.is-on{ background:rgba(232,105,10,.12);border-color:rgba(232,105,10,.45); }
.gf-fire{ font-size:20px;line-height:1; }
.gf-streak strong{ font-family:var(--font-h);font-weight:900;font-size:26px;letter-spacing:-.5px;line-height:1;color:var(--text); }
.gf-streak.is-on strong{ color:var(--primary); }
.gf-streak em{ font-family:var(--font-h);font-style:normal;font-weight:800;font-size:9px;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted); }
.gf-risk{ margin-top:4px;font-family:var(--font-h);font-weight:800;font-size:8.5px;
  letter-spacing:.10em;text-transform:uppercase;color:#F59E0B; }
.gf-level{ flex:1;min-width:0;display:flex;flex-direction:column;gap:6px; }
.gf-level-n{ font-family:var(--font-h);font-weight:900;font-size:15px;color:var(--text); }
.gf-bar{ height:6px;border-radius:3px;background:rgba(255,255,255,.06);
  border:1px solid var(--border2);overflow:hidden; }
.gf-bar i{ display:block;height:100%;width:0;border-radius:3px;
  background:linear-gradient(90deg,var(--primary),var(--primary-2));
  box-shadow:0 0 6px rgba(232,105,10,.5);transition:width .45s cubic-bezier(.2,.8,.2,1); }
.gf-level-sub{ font-family:var(--font-b);font-size:11.5px;color:var(--muted); }
.gf-heat{ display:grid;grid-template-columns:repeat(auto-fit,minmax(9px,1fr));gap:3px;margin-bottom:8px; }
.gf-cell{ aspect-ratio:1;border-radius:2px;background:rgba(255,255,255,.05); }
.gf-l1{ background:rgba(232,105,10,.35) } .gf-l2{ background:rgba(232,105,10,.62) }
.gf-l3{ background:var(--primary);box-shadow:0 0 4px rgba(232,105,10,.5) }
.gf-heat-cap{ font-family:var(--font-b);font-size:11px;color:var(--dim); }

/* ponto do botão de conta = ofensiva viva; pulsa quando ainda não praticou hoje */
.account-btn.has-streak .account-dot{ display:block;background:var(--primary); }
.account-btn.streak-risk .account-dot{ background:#F59E0B;animation:gfPulse 1.8s ease-in-out infinite; }
@keyframes gfPulse{ 0%,100%{transform:scale(1);opacity:1} 50%{transform:scale(1.35);opacity:.65} }

.gf-toast{ position:fixed;left:50%;bottom:calc(84px + env(safe-area-inset-bottom));
  transform:translate(-50%,14px);opacity:0;z-index:9999;
  display:flex;align-items:center;gap:9px;padding:12px 18px;border-radius:999px;
  background:rgba(18,18,18,.94);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border:1px solid rgba(232,105,10,.5);color:var(--text);
  font-family:var(--font-h);font-weight:800;font-size:13px;
  box-shadow:0 10px 30px rgba(0,0,0,.5);
  transition:opacity .3s ease,transform .3s cubic-bezier(.2,.8,.2,1);pointer-events:none; }
.gf-toast.is-in{ opacity:1;transform:translate(-50%,0); }
.gf-toast span{ font-size:17px; }
@media (prefers-reduced-motion:reduce){
  .gf-bar i,.gf-toast{ transition:none }
  .account-btn.streak-risk .account-dot{ animation:none }
}

/* [F7] Trilha "Tempero dos acordes bonitos" — vitrine de unidades que já
   existiam. Faixa própria pra não competir com os 62 cards da jornada. */
.tempero-band{ max-width:760px;margin:0 auto 26px;padding:18px;border-radius:14px;
  background:rgba(18,18,18,.34);backdrop-filter:blur(9px) saturate(1.1);
  -webkit-backdrop-filter:blur(9px) saturate(1.1);
  border:1px solid var(--border2);border-left:3px solid #3B82F6; }
.tempero-title{ font-family:var(--font-h);font-weight:900;font-size:clamp(19px,4.6vw,25px);
  letter-spacing:-.6px;color:var(--text);margin:0 0 6px;border:none;padding:0; }
.tempero-title span{ color:#3B82F6; }
/* [AUDITORIA] idem */
.toc-page .tempero-lead{ font-family:var(--font-b);font-size:13px;color:var(--muted);margin:0 0 14px;line-height:1.45; }
.tempero-chips{ display:flex;flex-wrap:wrap;gap:8px; }
.tempero-chip{ display:flex;flex-direction:column;align-items:center;gap:1px;min-width:64px;
  padding:8px 10px;border-radius:10px;text-decoration:none;
  background:var(--card2);border:1px solid var(--border2);
  transition:border-color .14s cubic-bezier(.2,.8,.2,1),transform .1s cubic-bezier(.2,.8,.2,1); }
.tempero-chip strong{ font-family:var(--font-h);font-weight:900;font-size:15px;color:#3B82F6;line-height:1; }
.tempero-chip span{ font-family:var(--font-h);font-weight:600;font-size:9.5px;
  letter-spacing:.06em;color:var(--muted);text-align:center;line-height:1.2; }
.tempero-chip:active{ transform:scale(.97); }
@media (hover:hover) and (pointer:fine){ .tempero-chip:hover{ border-color:#3B82F6 } }
@media (prefers-reduced-motion:reduce){ .tempero-chip{ transition:none } .tempero-chip:active{ transform:none } }

/* ======================================================= [PARTITURA · RESET]
 * A seção virou placeholder em 26/07 (reset — docs/partitura-reset.md): o CSS
 * próprio da experiência (chips de tom, o "papel" da pauta, o cursor por
 * classe .sc-cur, o layout de paisagem do score-view) saiu junto com a tela.
 * A raiz agora usa .toc-hero + .tabroot-soon, que já são padrão da casa.
 * Recuperar: tag `partitura/f1-experiencia`. O pacote tipográfico da barra de
 * 5 itens (app.css ~1006 e ~5253) NÃO é daqui e fica intocado — é da ABA.
 *
 * Paisagem: a rota mantém data-orient="free" (o 3º estado do lock, 307f4cc),
 * então o placeholder pode ser lido girado. Não precisa de regra própria — é
 * um card de texto centrado; quem tiver pauta de novo declara o layout aqui. */
.score-page .toc-hero{ margin-bottom:8px }
@media (orientation:landscape) and (max-width:900px) and (pointer:coarse){
  .score-page .toc-mega-title{ font-size:22px }
}

/* ============================================================= C1a-LEITURA
 * Curso de Leitura — launcher (raiz score) + fita (namespace ld-).
 * A tela da fita herda o fullscreen flex da .practice-page; aqui só o que é
 * dela: topo, papel da pauta, contagem, fecho e o teclado com altura fixa. */

/* [26/07 · Luis] A FITA VIROU PAPEL. O fundo estelar (#c) não aparece mais nesta
 * tela: o papel da partitura (mesmo creme de playbook PDF que era só a caixinha
 * da pauta) ocupa a tela inteira, com uma moldura preta contornando todo o
 * branco — ela desce pelas bordas da tela e FECHA rente ao topo das teclas.
 * Duas geometrias precisam bater e por isso viram variável: a altura do teclado
 * (que a moldura usa pra saber onde parar) e a folga do rodapé. Mexeu numa,
 * a moldura acompanha sozinha. */

/* [26/07 · 2ª rodada] TECLADO BRANCO E PRETO nesta tela. O renderKeyboard em
 * modo 'show' escreve fill="#2A2A2A" (cinza escuro) direto no rect — mas isso é
 * ATRIBUTO DE APRESENTAÇÃO, que perde pra qualquer regra de folha de estilo:
 * não precisa de !important e o resto do app (capa, splash, intro, prática)
 * segue com o cinza de sempre, porque a regra é escopada na fita.
 * As classes de aceso (.kbd-playing e cia) usam !important e continuam vencendo. */
/* [26/07 · 2ª rodada · Luis] A MOLDURA EMOLDURA A TELA DO APARELHO, não o papel.
 * A 1ª versão era um retângulo preto que parava no topo das teclas e era recuado
 * pela safe-area — reprovado. Agora ela é LARANJA, bem mais grossa, e nasce
 * colada no gume da tela dos quatro lados, EXATAMENTE como a moldura do click
 * (#click-border-flash, v355): mesma receita, `inset:0` + anel por box-shadow
 * INSET em vez de border. O anel interno não muda o box do elemento, então ele
 * cobre a tela inteira (papel E teclas) sem empurrar layout nenhum.
 * pointer-events:none pra não roubar o toque do papel nem das teclas. */

/* fases com fundo sólido mantêm o texto branco — o contraste ali é com a cor, não com o papel */

@keyframes ldBeat{0%{opacity:.95;transform:scale(1.14)}100%{opacity:0;transform:scale(.94)}}

/* teclado: altura fixa no rodapé (o papel flexa por cima). A altura vem da
 * variável porque a moldura preta calcula o próprio fim a partir dela — os dois
 * números NÃO podem viver separados. max-width some: com 49 teclas o piano usa
 * a largura toda, e um teto de 980px deixaria o traço da moldura passando por
 * cima de papel vazio nas pontas em telas largas. */

/* ============================================================================
 * [Partitura v3 · 28/07] Curso de Partitura — lista (sc-root) + player (fita)
 * Receitas herdadas da fita anterior (.ld-*, aprovadas pelo Luis 26/07):
 * papel claro, moldura laranja por box-shadow inset, teclado P&B de 49 teclas,
 * contagem gigante que pulsa. O overlay roxo/verde vem do partitura-lab
 * (mesma matemática de % do viewBox — o SVG força width:100%!important).
 * ========================================================================= */

/* ---- raiz: lista de módulos/lições (tema escuro padrão do app) ---- */
.sc-hub{padding:0 18px 28px;display:flex;flex-direction:column;gap:10px;max-width:760px}
/* 🔴 [30/07 · Luis: "a página do curso completo está muito extensa, transforme
 * os módulos em menu dropdown"] Os 14 módulos (56 lições) ficavam TODOS
 * abertos ao mesmo tempo — a lista virava uma rolagem enorme antes do aluno
 * achar a lição de hoje. Cada módulo agora é uma CAIXA com cabeçalho clicável;
 * só o módulo "onde eu parei" abre sozinho (`__scRootEnter`), o resto entra
 * fechado. Um aberto por vez — é o que segura a lista curta em vez de virar
 * 14 acordeões abertos igual ao problema de origem. */
.sc-module{border:1px solid rgba(255,255,255,.09);border-radius:16px;overflow:hidden;
  background:rgba(255,255,255,.025);transition:border-color .18s}
.sc-module.is-open{border-color:rgba(255,255,255,.16)}
.sc-module-head{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  padding:14px 14px;margin:0;background:none;border:0;color:inherit;font:inherit;cursor:pointer;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation}
.sc-module-head:active{background:rgba(255,255,255,.045)}
.sc-module-head strong{font-size:16px;font-weight:800;letter-spacing:.01em;flex:1;min-width:0}
.sc-module-tag{font-size:10px;font-weight:800;letter-spacing:.08em;padding:3px 8px;border-radius:999px;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.14);opacity:.85;flex:0 0 auto}
/* placar do módulo: única pista de progresso enquanto ele está FECHADO —
 * sem ela, recolher a lista escondia o "quanto falta" junto com as lições */
.sc-module-prog{flex:0 0 auto;font-size:11.5px;font-weight:800;color:var(--muted);
  font-variant-numeric:tabular-nums;letter-spacing:.01em}
.sc-module-prog.is-full{color:#23C063}
.sc-module-chev{flex:0 0 auto;width:14px;height:14px;color:var(--muted);
  transition:transform .22s ease}
.sc-module.is-open .sc-module-chev{transform:rotate(180deg);color:inherit}
/* 🔴 O COLAPSO É CSS PURO (grid-template-rows 0fr→1fr), não JS medindo altura.
 * Cada módulo tem de 2 a 7 lições — um max-height cravado ou é curto demais
 * pros módulos grandes ou vira uma pausa longa nos pequenos. O truque de grid
 * anima para o tamanho REAL do conteúdo sem medir nada em JS, e funciona com
 * qualquer altura porque quem colapsa é a LINHA do grid, não o conteúdo. */
.sc-module-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .26s ease}
.sc-module-body > div{overflow:hidden;min-height:0}
.sc-module.is-open .sc-module-body{grid-template-rows:1fr}
.sc-module .sc-lessons{padding:0 14px 14px}
.sc-lessons{display:flex;flex-direction:column;gap:8px}
.sc-lesson{display:flex;align-items:center;gap:12px;width:100%;text-align:left;cursor:pointer;
  background:rgba(255,255,255,.045);border:1px solid rgba(255,255,255,.09);border-radius:14px;
  padding:12px 14px;color:inherit;font:inherit;transition:background .18s,border-color .18s}
.sc-lesson:active{background:rgba(255,255,255,.10)}
.sc-lesson-n{flex:0 0 auto;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:800;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16)}
.sc-lesson.is-done .sc-lesson-n{background:rgba(35,192,99,.18);border-color:rgba(35,192,99,.5);color:#23C063}
.sc-lesson-t{font-size:13.5px;font-weight:600;line-height:1.3}
/* ===== [01/09 · Luis] O CURSO DE PARTITURA É PAGO DO MÓDULO 2 EM DIANTE =====
   "deixe apenas o primeiro modulo de graca".
   A marcação (`is-sc-pago` / `is-sc-free` + os dois cadeados + o selo) nasce
   SEMPRE no DOM, para todo mundo; quem a acende é `body.pw-nonpayer` — a mesma
   classe que já acende os cadeados da Jornada e que o `updateAccountUI`
   reescreve quando o entitlement chega. Assim o pagante cujo entitlement
   demora não vê cadeado piscando, e ninguém depende de um re-render da lista.
   O módulo trancado continua ABRINDO a gaveta: a lista mostra o que ensina, e
   o portao fica na LIÇÃO (mesma regra que a unit-intro segue desde 31/08). */
.sc-module-lock,.sc-lesson-lock,.sc-module-free{display:none}
body.pw-nonpayer .sc-module.is-sc-pago .sc-module-lock,
body.pw-nonpayer .sc-module.is-sc-pago .sc-lesson-lock{display:block;flex:0 0 auto;
  width:13.5px;height:13.5px;color:var(--muted)}
body.pw-nonpayer .sc-module.is-sc-pago .sc-lesson-lock{margin-left:auto}
body.pw-nonpayer .sc-module.is-sc-free .sc-module-free{display:inline-block;flex:0 0 auto;
  font-size:9.5px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;
  padding:3px 7px;border-radius:999px;color:#23C063;background:rgba(35,192,99,.14);
  border:1px solid rgba(35,192,99,.42)}
body.pw-nonpayer .sc-module.is-sc-pago .sc-lesson{opacity:.6}
body.pw-nonpayer .sc-module.is-sc-pago .sc-lesson-n{background:rgba(255,255,255,.05);
  border-color:rgba(255,255,255,.10);color:var(--muted)}
.sc-loading p{opacity:.5}

/* ---- player: papel, moldura e teclado (receita da .leitura-page) ---- */
.score-play-page{
  --sc-kbd-h:min(34.2vh,218.5px);
  --sc-paper:#F6F1E7;
  --sc-ink:#141414;
  --sc-frame:12px;
  --sc-frame-cor:var(--primary,#E8690A);
  --sc-roxo:#7C3AED;
  --sc-verde:#22C55E;
  --sc-vermelho:#EF4444;
  background:var(--sc-paper);
  color:var(--sc-ink);
  display:flex;flex-direction:column;
}
.score-play-page .kbd-key.kbd-w{fill:#FFFFFF;stroke:#101010}
.score-play-page .kbd-key.kbd-b{fill:#101010;stroke:#101010}
.score-play-page::before{
  content:'';position:absolute;inset:0;z-index:6;pointer-events:none;
  box-shadow: inset 0 0 0 var(--sc-frame) var(--sc-frame-cor);
}

/* topo */
.sc-top{display:flex;align-items:center;gap:10px;flex:0 0 auto;position:relative;z-index:4;
  padding:calc(16px + env(safe-area-inset-top)) calc(24px + env(safe-area-inset-right)) 6px calc(24px + env(safe-area-inset-left))}
.sc-pill{font-size:11px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;
  padding:5px 12px;border-radius:999px;border:1px solid rgba(0,0,0,.22);
  background:rgba(0,0,0,.05);color:var(--sc-ink);transition:background .25s,border-color .25s,color .25s;white-space:nowrap}
[data-sc-fase="tocar"] .sc-pill{background:var(--sc-roxo);border-color:transparent;color:#fff}
[data-sc-fase="contar"] .sc-pill{background:rgba(232,105,10,.92);border-color:transparent;color:#fff}
.sc-score{font-size:12px;font-weight:800;white-space:nowrap;font-variant-numeric:tabular-nums}
.sc-score.is-good{color:#15803D}
.sc-score.is-mid{color:#B45309}
.sc-score.is-low{color:#B91C1C}
/* [31/08 · item 09 · Luis] O SEGMENTADO DE MODO E O BOTAO DE PAUSA DA PAUTA.
   Vocabulario emprestado do segmentado da folha (.mp-modo): indicador que
   desliza, texto branco no ligado. Os dois somem DENTRO da fita — la os chips
   ja existem na .fr-bar — e somem tambem enquanto a licao nao comecou: pausar o
   que nao esta tocando nao e um estado, e um botao mentindo. */
.sc-modo{position:relative;display:flex;align-items:center;height:30px;padding:2px;
  border-radius:9px;border:1px solid rgba(0,0,0,.22);background:rgba(0,0,0,.05);flex:0 0 auto}
.sc-modo-ind{position:absolute;top:2px;left:2px;width:calc(50% - 2px);height:calc(100% - 4px);
  border-radius:7px;background:var(--sc-roxo,#6D4AFF);
  transition:transform .24s cubic-bezier(.4,0,.2,1)}
.sc-modo[data-modo="espera"] .sc-modo-ind{transform:translateX(100%)}
.sc-modo-btn{position:relative;z-index:1;flex:0 0 auto;background:transparent;border:0;
  padding:0 10px;height:100%;font-family:var(--font-h);font-weight:800;font-size:10.5px;
  letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;
  color:var(--sc-ink);opacity:.62;cursor:pointer;
  -webkit-tap-highlight-color:transparent;transition:opacity .2s,color .2s}
.sc-modo-btn[aria-pressed="true"]{color:#fff;opacity:1}
.sc-pausa{width:30px;height:30px;flex:0 0 auto;border-radius:50%;
  border:1px solid rgba(0,0,0,.25);background:rgba(0,0,0,.05);color:var(--sc-ink);
  display:flex;align-items:center;justify-content:center;padding:0;cursor:pointer;
  -webkit-tap-highlight-color:transparent}
.sc-pausa:active{background:rgba(0,0,0,.14)}
.sc-pausa svg{width:15px;height:15px;fill:currentColor}
.sc-pausa .sc-pausa-pl{display:none}
[data-sc-pausa="1"] .sc-pausa .sc-pausa-ii{display:none}
[data-sc-pausa="1"] .sc-pausa .sc-pausa-pl{display:block}
[data-sc-pausa="1"] .sc-pausa{background:var(--sc-roxo,#6D4AFF);border-color:transparent;color:#fff}
/* antes do "Comecar", e dentro da fita, os dois nao existem */
.score-play-page:not([data-sc-tocando="1"]) .sc-modo,
.score-play-page:not([data-sc-tocando="1"]) .sc-pausa,
.score-play-page[data-fr-on="1"] .sc-modo,
.score-play-page[data-fr-on="1"] .sc-pausa{display:none}
.sc-bpm{display:flex;align-items:center;gap:6px;margin-left:auto}
.sc-bpm-btn{width:30px;height:30px;border-radius:50%;border:1px solid rgba(0,0,0,.25);background:rgba(0,0,0,.05);
  color:var(--sc-ink);font-size:17px;font-weight:800;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
.sc-bpm-btn:active{background:rgba(0,0,0,.14)}
.sc-bpm-val{min-width:34px;text-align:center;font-size:13px;font-weight:800;font-variant-numeric:tabular-nums}
.sc-crumb{font-size:12px;color:var(--sc-ink);opacity:.55;letter-spacing:.02em;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:34vw}

/* papel + pauta + camadas de cursor (matemática do partitura-lab) */
/* [29/07 · Luis] a reserva da legenda (40px no topo) custava ~20% do tamanho da
   pauta em TODAS as fases, inclusive nas que não têm legenda. Agora ela só vale
   enquanto a legenda está na tela (.has-cap), e o encaixe é refeito na troca. */
.sc-paper{flex:1 1 auto;min-height:0;position:relative;z-index:4;display:flex;align-items:center;justify-content:center;overflow:hidden;
  --sc-cap-h:30px;
  padding:8px calc(26px + env(safe-area-inset-right)) 10px calc(26px + env(safe-area-inset-left))}
.sc-paper.has-cap{padding-top:calc(2px + var(--sc-cap-h) + 8px)}
/* [29/07 · Luis] o teto de 920px segurava a pauta ANTES de o papel acabar (no
 * iPad deitado sobrava 82px de cada lado por causa dele). Quem limita agora é
 * o papel: a largura final é calculada no render (o menor entre o que cabe na
 * altura e o que cabe na largura), então a pauta usa o espaço que existe. */
.sc-wrap{position:relative;width:100%;max-width:100%}
/* 🔴 MEDIDO no lab: o VexFlow escreve width/height INLINE no <svg> e o inline
 * vence a folha — sem o !important as camadas em % desalinham em até 40%. */
.sc-staff{position:relative;z-index:1}
.sc-staff svg{width:100%!important;height:auto!important;display:block}
.sc-staff .sc-cur path{fill:var(--sc-roxo) !important;stroke:var(--sc-roxo) !important}
.sc-beat{position:absolute;background:var(--sc-roxo);border-radius:7px;opacity:0;z-index:0;pointer-events:none;
  transition:left .09s cubic-bezier(.22,.9,.3,1),width .09s cubic-bezier(.22,.9,.3,1)}
.sc-beat.on{opacity:.18}
.sc-dot{position:absolute;width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;background:var(--sc-verde);
  box-shadow:0 0 0 3px rgba(34,197,94,.25);opacity:0;z-index:2;pointer-events:none;
  transition:left .07s linear,top .07s linear}
.sc-dot.on{opacity:1}
/* [29/07 · Luis] o ponto anda com a música e MUDA DE COR conforme o aluno:
   verde enquanto ele acompanha, vermelho quando a nota daquele ponto não foi
   tocada (ou saiu errada). A cor é a única coisa que responde ao aluno — o
   cursor nunca espera por ele. */
.sc-dot.is-erro{background:var(--sc-vermelho);box-shadow:0 0 0 3px rgba(239,68,68,.28)}
/* [29/08 · PAUTA ANDANTE 2 · Luis] a pauta que anda atravessa a tela de ponta a
   ponta: no modo andante o papel perde a margem cosmetica de 26px (a safe-area
   fica — a clave nao pode entrar no notch). */
.sc-paper.sc-and{padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right)}
/* o CABECALHO FIXO (chave + clave + armadura + formula): um segundo <svg> colado
   a esquerda, acima do desenho que anda (z 2 > .sc-staff z 1) e abaixo dos
   pontos, com fundo cor de papel para esconder as notas que rolam por baixo. */
.sc-cab{position:absolute;left:0;top:0;bottom:0;z-index:2;pointer-events:none;overflow:hidden;background:var(--sc-paper)}
.sc-cab svg{width:100%;height:auto;display:block}
/* a cabeça de nota errada FICA marcada: o erro não some quando o ponto passa */
.sc-staff .sc-errou path{fill:var(--sc-vermelho)!important;stroke:var(--sc-vermelho)!important}
/* [29/07 · Luis] e a ACERTADA fica verde — a passada inteira fica legível na
   pauta, não só o que deu errado. Mesma receita do vermelho (o VexFlow desenha
   cabeça, haste e bandeirola como <path> dentro do <g> do evento). */
.sc-staff .sc-acertou path{fill:var(--sc-verde)!important;stroke:var(--sc-verde)!important}

/* o recado da contagem: "ouça no tempo" antes da demonstração, "repita em"
   antes de o aluno entrar — a mesma mecânica com propósitos diferentes */
.sc-conta-msg{position:absolute;left:0;right:0;top:16%;text-align:center;pointer-events:none;z-index:4;
  font-family:Montserrat,system-ui,sans-serif;font-weight:800;font-size:clamp(13px,2.6vh,18px);
  color:var(--sc-ink);opacity:.72}
.sc-conta-msg[hidden]{display:none}
.sc-conta-msg.is-in{animation:scMsgIn .34s ease-out}
@keyframes scMsgIn{0%{opacity:0;transform:translateY(6px)}100%{opacity:.72;transform:none}}

/* contagem gigante (receita .ld-count) */
.sc-count{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:3;
  font-family:Montserrat,system-ui,sans-serif;font-weight:900;font-size:clamp(64px,16vh,110px);
  color:var(--sc-ink);text-shadow:0 4px 18px rgba(246,241,231,.9);opacity:0}
.sc-count.is-beat{animation:scBeat .48s ease-out}
@keyframes scBeat{0%{opacity:.95;transform:scale(1.14)}100%{opacity:0;transform:scale(.94)}}

/* legenda da locução (clipes ainda não gravados — texto na tela; toque pula) */
/* ===== LEGENDA DE UMA LINHA [Luis 28/07] ================================
 * 🔴 O que havia aqui era um bloco com `max-height:38%` e `overflow-y:auto`:
 * MEDIDO no device, 53 das 56 lições saíam cortadas (a M5·L1 perdia 40% — justo
 * a frase que explica o pontinho). Rolava, mas nada avisava que rolava, e tocar
 * na legenda PULA, então quem tentasse arrastar fechava a fala.
 * Agora é UMA LINHA no alto do papel, estilo legenda de filme, trocando no tempo
 * da locução. O gerador (gen_coreografia.mjs) corta o texto em ≤58 caracteres
 * (≤26 em CJK) — os dois números são o MESMO contrato: mexer aqui sem mexer lá
 * traz o corte de volta. */
.sc-caption{position:absolute;left:6%;right:6%;top:6px;z-index:5;cursor:pointer;
  height:var(--sc-cap-h,30px);display:flex;align-items:center;justify-content:center;
  background:rgba(20,20,20,.86);color:#F6F1E7;border-radius:999px;padding:0 18px;
  font-size:15px;line-height:1.1;text-align:center;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  opacity:0;transition:opacity .16s ease}
.sc-caption.on{opacity:1}
.sc-caption[hidden]{display:none}

/* ===== DESTAQUE (o zoom-in didático) ====================================
 * A palavra é dita e o elemento correspondente na tela cresce e ganha um anel.
 * A coreografia vem pronta do curso-fala-<lang>.json, derivada do alinhamento
 * por caractere do próprio TTS — por isso ela existe nos 8 idiomas sem ninguém
 * marcar nada à mão. */
.sc-spot{position:absolute;z-index:3;pointer-events:none;opacity:0;
  border:2.5px solid var(--sc-laranja,#E8690A);border-radius:10px;
  box-shadow:0 0 0 4px rgba(232,105,10,.16), 0 6px 18px rgba(232,105,10,.22);
  background:rgba(232,105,10,.10);
  transform:scale(1.55);transform-origin:center center}
.sc-spot.on{animation:sc-spot-in var(--sc-spot-d,1.1s) cubic-bezier(.22,1,.36,1) both}
@keyframes sc-spot-in{
  0%   {opacity:0;transform:scale(1.55)}
  22%  {opacity:1;transform:scale(1)}
  72%  {opacity:1;transform:scale(1)}
  100% {opacity:0;transform:scale(1.06)}
}
/* o próprio símbolo cresce junto — transform-box:fill-box para o <g> do VexFlow
 * girar no PRÓPRIO centro, e não na origem do SVG */
.sc-staff .sc-hi{transform-box:fill-box;transform-origin:center center;
  animation:sc-hi-zoom var(--sc-spot-d,1.1s) cubic-bezier(.22,1,.36,1) both}
@keyframes sc-hi-zoom{
  0%{transform:scale(1)} 22%{transform:scale(1.22)} 72%{transform:scale(1.22)} 100%{transform:scale(1)}
}
.sc-kbd.sc-hi-kbd{animation:sc-hi-kbd var(--sc-spot-d,1.1s) ease both}
@keyframes sc-hi-kbd{
  0%{filter:none} 25%{filter:brightness(1.14) drop-shadow(0 0 10px rgba(232,105,10,.55))}
  70%{filter:brightness(1.14) drop-shadow(0 0 10px rgba(232,105,10,.55))} 100%{filter:none}
}
@media (prefers-reduced-motion:reduce){
  .sc-spot.on,.sc-staff .sc-hi,.sc-kbd.sc-hi-kbd{animation-duration:.01ms;animation-iteration-count:1}
  .sc-spot.on{opacity:1;transform:none}
}

/* botão de partida (o gesto que destrava o áudio) */
.sc-start{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:5;
  background:rgba(246,241,231,.6)}
.sc-start[hidden]{display:none}
/* [29/07 · Luis] PÍLULA com play — a barra laranja atravessava a tela inteira
   e parecia um banner, não um botão. Agora é um alvo redondo, do tamanho do
   dedo, no meio do papel. */
.sc-start-btn{margin:0;display:inline-flex;align-items:center;gap:10px;
  padding:12px 26px 12px 20px;border-radius:999px;border:0;
  background:var(--primary,#E8690A);color:#111;
  font-family:Montserrat,system-ui,sans-serif;font-weight:900;font-size:15px;
  box-shadow:0 6px 20px rgba(232,105,10,.34)}
.sc-start-btn:active{transform:scale(.96)}
.sc-play-ico{width:22px;height:22px;display:block;flex:0 0 auto}
.sc-start-txt{white-space:nowrap}

/* fim da lição (receita .ld-fim — o display:flex TEM de ceder ao [hidden]) */
.sc-fim{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:5;
  background:rgba(246,241,231,.72);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}
.sc-fim[hidden]{display:none}
.sc-fim-card{display:flex;flex-direction:column;gap:14px;align-items:stretch;
  background:#161616;border:1px solid rgba(255,255,255,.1);border-radius:16px;padding:20px 22px;min-width:min(320px,80vw)}
.sc-fim-title{font-size:16px;font-weight:800;text-align:center;color:#F6F1E7}
.sc-fim-score{text-align:center;font-size:13px;font-weight:700;color:#9AE6B4;font-variant-numeric:tabular-nums}
.sc-fim-acts{display:flex;flex-direction:column;gap:8px}
.sc-fim-btn{margin:0}

/* teclado no rodapé — altura amarrada na moldura (mesma lei da fita antiga) */
.score-play-page .kbd-wrap.sc-kbd{flex:0 0 var(--sc-kbd-h);height:var(--sc-kbd-h);position:relative;z-index:4}
.score-play-page .kbd-wrap.sc-kbd .kbd-svg{width:100%;height:100%}

/* ============================================================================
   [Minhas Partituras · E2] editor mp- — MESMA FAMÍLIA visual do curso (papel,
   teclado no rodapé, lista .sc-lesson). O que é próprio: a RODA, o cursor de
   inserção e as pausas-fantasma.
   ============================================================================ */
.mp-edit-page{--mp-paper:#F6F1E7;--mp-kbd-h:min(34.2vh,218.5px);display:flex;flex-direction:column}
.mp-top{display:flex;align-items:center;gap:10px;padding:6px 10px;flex:0 0 auto;min-height:40px}
.mp-name{font-weight:800;font-size:14px;color:#F6F1E7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mp-msg{margin-left:auto;font-size:12px;font-weight:700;color:#FFB347;opacity:0}
.mp-msg.on{animation:mpMsgSome 1.8s forwards}
@keyframes mpMsgSome{0%,70%{opacity:1}100%{opacity:0}}
.mp-mid{flex:1;display:flex;gap:8px;min-height:0;padding:0 8px 6px}
/* 🔴 [E3c] MEDIDO no print, não na sonda: com o alternador de vozes a coluna
 * passou de 240px num vão de ~215 (iPhone deitado) e o FINALIZAR sumia embaixo
 * do teclado. A coluna agora é proporcional à altura da tela — o pior caso é
 * que manda, e sobra folga em telas altas. */
.mp-side{flex:0 0 132px;display:flex;flex-direction:column;gap:6px;align-items:center;justify-content:center;min-height:0}
/* 🔴 [30/07 · Luis] "remova completamente os ícones redondos da figura, deixe
   cada símbolo puro, dispostos nos anéis que agora vão ganhar um contorno entre
   um símbolo e outro, o contorno circular é interrompido".
   Some o disco de fundo da roda E o botão redondo de cada figura: o que dá a
   forma agora são os AROS (SVG com dash), e o que se toca é a área invisível
   do botão — que continua do tamanho do dedo, só deixou de ser desenhada. */
.mp-roda{position:relative;width:min(122px,27vh);height:min(122px,27vh);flex:0 0 auto;
  background:none;border:0}
/* os aros: um círculo com traço onde é para haver contorno e VÃO onde mora o
   símbolo. `rotate(-90deg)` porque o dash de um <circle> começa às 3 horas e as
   posições da roda começam ao NORTE. */
.mp-aro{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;
  transform:rotate(-90deg);overflow:visible}
.mp-aro circle{stroke:rgba(255,255,255,.30);stroke-width:1.1;vector-effect:non-scaling-stroke;
  stroke-linecap:round}
.mp-aro-raz{display:none}
.mp-roda.is-tup .mp-aro-fig{display:none}
.mp-roda.is-tup .mp-aro-raz{display:block}
.mp-roda.is-no{animation:mpNo .3s}
@keyframes mpNo{0%{transform:scale(1)}40%{transform:scale(.94)}100%{transform:scale(1)}}
.mp-fig{position:absolute;width:min(37px,8.2vh);height:min(37px,8.2vh);margin:min(-18.5px,-4.1vh) 0 0 min(-18.5px,-4.1vh);
  display:flex;align-items:center;justify-content:center;color:#F6F1E7;padding:0;
  background:none;border:0;border-radius:50%}
.mp-fig svg{width:19px;height:25px;display:block}
/* ARMADA: sem disco, quem marca a escolha é a própria figura — laranja e um
   halo suave, que é o que sobra quando o botão deixa de existir como forma */
.mp-fig.is-armed{color:var(--primary,#E8690A);
  filter:drop-shadow(0 0 6px rgba(232,105,10,.55))}
.mp-fig:active{transform:scale(.92)}
.mp-acts{display:flex;flex-direction:column;gap:5px;width:100%;flex:0 0 auto}
.mp-act{border:1px solid rgba(255,255,255,.18);background:#1d1d1d;color:#F6F1E7;border-radius:10px;
  padding:min(8px,1.7vh) 10px;font-size:12.5px;font-weight:800}
.mp-act.is-primary{background:var(--primary,#E8690A);border-color:var(--primary,#E8690A);color:#111}
.mp-act:active{transform:scale(.97)}
.mp-paper{flex:1;min-width:0;background:var(--mp-paper);border-radius:12px;padding:10px;
  display:flex;align-items:center;justify-content:center;overflow:hidden}
.mp-wrap{position:relative;width:100%}
.mp-staff{position:relative;z-index:1}
/* 🔴 [30/07] `!important` porque o VexFlow escreve `style="width:640px;
   height:125px"` INLINE no <svg>, e estilo inline vence CSS sem !important.
   Sem isto o recorte do viewBox vira LETTERBOX: o desenho encolhe dentro da
   caixa velha e o balão branco continua espesso — que era a queixa. É a mesma
   regra que a `.sc-staff svg` do curso já tinha; a do editor ficou para trás. */
.mp-staff svg{width:100%!important;height:auto!important;display:block}
/* o cursor de inserção: linha vertical piscando na posição da PRÓXIMA figura */
.mp-cursor{position:absolute;width:2.5px;border-radius:2px;background:var(--primary,#E8690A);
  opacity:0;z-index:2;pointer-events:none;transition:left .07s linear,top .07s linear}
.mp-cursor.on{animation:mpPisca 1.1s step-end infinite}
@keyframes mpPisca{0%,100%{opacity:.85}50%{opacity:.25}}
/* o ainda-não-escrito: pausas de preenchimento esmaecidas */
.mp-staff .mp-ghost{opacity:.28}
/* [E3a] figura SELECIONADA (edição no lugar): pintada de laranja */
.mp-staff .mp-sel path{fill:var(--primary,#E8690A)!important;stroke:var(--primary,#E8690A)!important}
/* [E3a] camada interna da roda: pausa (sticky) + ponto/ligadura (um tiro) */
.mp-tog{position:absolute;width:23px;height:23px;margin:-11.5px 0 0 -11.5px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;color:#F6F1E7;padding:0;
  background:none;border:0;font-size:17px;font-weight:900;line-height:1}
.mp-tog svg{width:12px;height:15px;display:block}
.mp-tog.is-on{color:var(--primary,#E8690A);filter:drop-shadow(0 0 5px rgba(232,105,10,.5))}
.mp-tog:active{transform:scale(.9)}
/* [E3a] modo pausa: a camada externa vira PAUSAS nas mesmas posições e o
 * teclado esmaece (pausa não tem altura) */
.mp-fig-nota,.mp-fig-pausa{display:flex;align-items:center;justify-content:center}
.mp-fig-pausa{display:none}
.mp-roda.is-pausa .mp-fig-nota{display:none}
.mp-roda.is-pausa .mp-fig-pausa{display:flex}
/* [E5] modo QUIÁLTERA: as 9 razões tomam o lugar das figuras */
.mp-raz{position:absolute;display:none;width:min(30px,6.6vh);height:min(30px,6.6vh);
  margin:min(-15px,-3.3vh) 0 0 min(-15px,-3.3vh);border-radius:50%;
  align-items:center;justify-content:center;padding:0;line-height:1;
  background:none;color:#F6F1E7;border:0;
  font-size:12px;font-weight:900}
.mp-raz small{font-size:8px;font-weight:800;opacity:.75}
.mp-roda.is-tup .mp-raz{display:flex}
.mp-roda.is-tup .mp-fig{display:none}
/* 🔴 [30/07 · Luis: "o ícone de 2,3 deve estar centralizado em relação ao
   ícone de fusa"] `width:auto` fazia o pill crescer com o texto SEM refazer a
   margem negativa (que é metade da largura FIXA) — o centro escorregava para a
   direita e o "2,3…" saía do eixo vertical da figura sul. Largura fixa e
   `transform` no lugar da margem: agora o centro é o centro, sempre. */
.mp-tog-tup{width:56px;margin-left:-28px;padding:0;border-radius:11px;
  font-size:9.5px;font-weight:900;letter-spacing:-.02em;white-space:nowrap}
/* grupo ARMADO: anel aceso, o usuário vê que as próximas notas entram nele */
/* grupo ARMADO: com a roda sem disco, quem acende é o ARO */
.mp-roda.tem-tup .mp-aro circle{stroke:rgba(232,105,10,.75);stroke-width:1.1}
.mp-fig-pausa svg{width:19px;height:25px}
.mp-edit-page.is-pausa .kbd-wrap.mp-kbd{opacity:.3;pointer-events:none;transition:opacity .2s}
.mp-edit-page .kbd-wrap.mp-kbd{flex:0 0 var(--mp-kbd-h);height:var(--mp-kbd-h);position:relative;z-index:4}
.mp-edit-page .kbd-wrap.mp-kbd .kbd-svg{width:100%;height:100%}
/* a pintura das teclas no modo practice é POR PÁGINA (mesma receita da 6061) —
 * sem isto o teclado renderiza preto-no-preto (visto no probe, não na sonda) */
.mp-edit-page .kbd-key.kbd-w{fill:#FFFFFF;stroke:#101010}
.mp-edit-page .kbd-key.kbd-b{fill:#101010;stroke:#101010}
/* a lista na raiz da aba Partitura (reusa .sc-module/.sc-lesson) */
.mp-hub{margin-top:14px}
/* 🗑️ [16/09] `.mp-nova .sc-lesson-n` — a porta deixou de ser `.sc-lesson` (lápide do bloco [12]). */

/* ============================================================================
   [E3c] duas vozes · clave editável · percussão · propriedades de criação
   ============================================================================ */
/* alternador de voz (só aparece com 2 vozes) */
.mp-vozes{display:flex;gap:4px;flex:0 0 auto}
.mp-vozes[hidden]{display:none}
.mp-voz{width:34px;height:min(26px,5.5vh);border-radius:8px;background:#1d1d1d;color:#F6F1E7;
  border:1px solid rgba(255,255,255,.16);font-size:13px;font-weight:900;padding:0}
.mp-voz.is-on{background:var(--primary,#E8690A);border-color:var(--primary,#E8690A);color:#111}
/* voz inativa esmaecida na gravura */
.mp-staff .mp-voz-off{opacity:.35}
/* hotspot invisível sobre a clave desenhada + menu de opções */
.mp-clef-hot{position:absolute;z-index:3;background:transparent;border:0;padding:0}
.mp-clef-hot[hidden]{display:none}
.mp-clef-menu{position:absolute;left:14px;top:14px;z-index:6;display:flex;flex-direction:column;gap:4px;
  background:#161616;border:1px solid rgba(255,255,255,.14);border-radius:10px;padding:6px}
.mp-clef-menu[hidden]{display:none}
.mp-clef-menu button{background:#1d1d1d;color:#F6F1E7;border:1px solid rgba(255,255,255,.14);
  border-radius:8px;padding:6px 12px;font-size:12.5px;font-weight:800;text-align:left}
.mp-clef-menu button:active{background:var(--primary,#E8690A);color:#111}
/* o .mp-paper precisa ancorar o menu */
.mp-paper{position:relative}
/* percussão: teclado some e a roda respira maior (spec 2.7) */
.mp-edit-page.is-perc .kbd-wrap.mp-kbd{display:none}
.mp-edit-page.is-perc .mp-roda{transform:scale(1.22)}
.mp-edit-page.is-perc .mp-side{flex-basis:150px}
/* folha de propriedades da criação (véu + cartão, família .sc-fim-card) */
.mp-sheet{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  background:rgba(0,0,0,.55);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}
.mp-sheet[hidden]{display:none}
/* 🔴 [E3c] MEDIDO no print: em pilha vertical a folha cortava a ARMADURA e o
 * botão Criar num iPhone deitado (390px). Vira grade rótulo→opções: 5 linhas
 * curtas em vez de 10, e cabe inteira sem rolagem. */
.mp-sheet-card{display:grid;grid-template-columns:auto 1fr;gap:5px 12px;align-items:center;
  background:#161616;border:1px solid rgba(255,255,255,.1);border-radius:16px;padding:14px 18px;
  min-width:min(420px,92vw);max-height:92vh;overflow-y:auto}
.mp-sheet-title{grid-column:1/-1;font-size:15px;font-weight:800;color:#F6F1E7;margin-bottom:2px}
.mp-opt-lbl{grid-column:1;justify-self:end;font-size:11px;font-weight:700;
  color:rgba(246,241,231,.55);text-transform:uppercase;letter-spacing:.04em}
.mp-opt{grid-column:2;display:flex;gap:5px;flex-wrap:wrap}
/* 🔴 [16/08 · Luis] `font-family:inherit` — SEM ela o botão cai na fonte do
 * SISTEMA (Arial), porque `<button>` NÃO herda `font-family` por padrão. O
 * defeito era antigo e passava despercebido em 12,5px; ficou gritante quando a
 * tela de fórmula de compasso nasceu com 17px e ele viu de imediato: "a fonte
 * principal de texto do app foi trocada". Medido nas duas telas (Nova partitura
 * e fórmula): Arial contra o Inter do título ao lado. Uma linha conserta as
 * duas — e o resto do app já fazia certo (`.fr-chip` sempre teve `inherit`). */
.mp-opt button{background:#1d1d1d;color:#F6F1E7;border:1px solid rgba(255,255,255,.14);
  border-radius:9px;padding:min(7px,1.5vh) 12px;font-size:12.5px;font-weight:800;min-width:44px;
  font-family:inherit}
.mp-opt button.is-on{background:var(--primary,#E8690A);border-color:var(--primary,#E8690A);color:#111}
.mp-opt.is-off,.mp-opt-lbl.is-off{opacity:.3;pointer-events:none}
.mp-sheet-criar{grid-column:1/-1;margin-top:6px}

/* [13 · 29/07] os estilos do player próprio (.mp-play-page) saíram junto com
   a tela: praticar a partitura do aluno é a MESMA .score-play-page do curso. */

/* linha da lista: abrir + ações (renomear/duplicar/excluir) */
/* ===== [09/09 · Luis] A PEÇA NATIVA (Für Elise resumida) =====================
   Ela é uma PEÇA, não uma porta: fica na linha das músicas do aluno e herda o
   .sc-lesson de sempre — o bloco das portas do hub (mp-nova / mp-imp-go /
   mp-scan-go / mp-vmt-go) é preso ao atributo da AÇÃO e não a toca.
   O que a distingue é o mínimo: o ♪ aceso em verde e o "(Grátis)" NA FRENTE do
   nome (ordem do Luis, 09/09) — no verde que o app já usa para dizer liberado
   (.sc-module-free, .sc-lesson.is-done). Sem ações ao lado, a linha ocupa a
   largura toda sozinha.
   🔑 O parêntese fica no MESMO fluxo do título, não num selo à parte: assim ele
   quebra junto com o nome num celular estreito, em vez de empurrar o título
   para uma segunda linha sozinho. */
.mp-hub .mp-item-nativa .sc-lesson-n{color:#23C063;background:rgba(35,192,99,.14);
  border-color:rgba(35,192,99,.42)}
.mp-hub .mp-item-nativa .sc-lesson-t{display:flex;flex-direction:column;align-items:flex-start;line-height:1.25}
.mp-hub .mp-item-nativa .mp-nativa-tit{display:block}
.mp-hub .mp-item-nativa .mp-free-tag{font-family:var(--font-h);font-weight:800;color:#23C063}
.mp-hub .mp-item-nativa small{font-family:var(--font-h);font-weight:600;font-size:11px;
  letter-spacing:.008em;color:rgba(246,241,231,.5);margin-top:3px}
/* [09/09 · Luis: "torne a partitura da fur elise free editavel, clicar na pauta
   abre o editor normalmente"] A REGRA QUE TIRAVA O TOQUE DAS PAUTAS DELA SAIU.
   Era `.mp-folha.is-nativa .mp-folha-pauta{pointer-events:none}` — o par visual
   da tranca que vivia no delegado do index.html. As duas caíram juntas: uma
   sem a outra deixaria o editor abrir com a pauta parecendo inerte, ou o
   contrário (convite visual num alvo que o delegado engole). A classe
   `is-nativa` continua no elemento — ela diz QUEM é a peça, já não o que ela
   deixa de fazer. */
.mp-linha{display:flex;align-items:stretch;gap:5px}
.mp-linha .sc-lesson{flex:1;min-width:0}
.mp-ac{flex:0 0 auto;width:36px;border-radius:10px;background:#1d1d1d;color:rgba(246,241,231,.75);
  border:1px solid rgba(255,255,255,.14);font-size:14px;padding:0}
.mp-ac:active{transform:scale(.94)}
.mp-ac.is-del:active{background:rgba(239,68,68,.25)}

/* ============================================================================
   [LOTE 29/07 · Luis] as telas novas do lote de ajustes.
   ============================================================================ */

/* ---- [03] a RAIZ da Partitura: 2 cards no padrão da escolha de modo -------
   O vocabulário visual vem do `.ms-like` (mesmos cards laranja/azul da
   mode-select). O que é próprio daqui é só o ENQUADRAMENTO: numa aba existe
   tab bar embaixo, então nada de altura de tela cheia. */
.sc-hub-page .ms-cards{ margin:8px auto 0; max-width:520px; }
.sc-hub-page .ms-card{ text-align:left; }
.sc-hub-page .toc-hero{ margin-bottom:22px; }
@media (min-width:600px){
  html.dt .sc-hub-page .ms-cards{ flex-direction:row; max-width:860px; gap:20px; }
  html.dt .sc-hub-page .ms-card{ flex:1 1 0; }
}

/* ---- cabeçalho das telas-filhas (voltar + título) ------------------------ */
.sc-sub-hero{ display:flex; align-items:center; gap:12px; text-align:left; margin-bottom:20px; }
.sc-sub-hero .toc-mega-title{ margin:0; }
.sc-sub-back{ flex:0 0 auto; }

/* ---- [06+07] o pop-up de fim de lição ------------------------------------
   Herda .catalog-confirm-modal inteiro; só a nota e os 2 cards são novos. */
.sc-fim-modal .ccm-card{ max-width:min(560px, 92vw); }
.sc-fim-nota{ font-family:var(--font-h);font-weight:900;font-size:26px;letter-spacing:-.5px;
  color:#F6F1E7;text-align:center;margin:2px 0 14px;font-variant-numeric:tabular-nums; }
/* 🔴 `flex-direction:row` EXPLÍCITO. O `.ccm-actions` da casa nasce em COLUNA e
   só vira linha acima de 480px de viewport — regra pensada para os modais que
   aparecem em retrato. Este pop-up nasce na PRÁTICA, que é paisagem, e o pedido
   foi literal: "2 cards LADO A LADO". Sem esta linha eles saíam empilhados. */
.sc-fim-acts{ display:flex; flex-direction:row; gap:12px; width:100%; }
.sc-fim-acts.is-solo{ justify-content:center; }
.sc-fim-op{ flex:1 1 0; min-width:0; display:flex; align-items:center; justify-content:center;
  min-height:64px; padding:14px 12px; border-radius:14px; cursor:pointer;
  background:#1b1b1d; border:1.5px solid rgba(255,255,255,.16); color:#F6F1E7;
  -webkit-appearance:none;appearance:none;font:inherit;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation;
  transition:transform .18s cubic-bezier(.2,.8,.2,1),border-color .18s,box-shadow .18s; }
.sc-fim-op.is-primary{ background:radial-gradient(ellipse 100% 130% at 20% 15%,rgba(232,105,10,.26) 0%,rgba(232,105,10,.07) 52%,transparent 76%),rgba(20,12,6,.7);
  border-color:rgba(232,105,10,.62); }
.sc-fim-op-solo{ flex:0 1 260px; }
.sc-fim-op-t{ font-family:var(--font-h);font-weight:900;font-size:17px;line-height:1.15;text-align:center; }
.sc-fim-op.is-primary .sc-fim-op-t{ color:var(--primary-2,#FF8A2B); }
.sc-fim-op:active{ transform:scale(.97); }
@media (hover:hover) and (pointer:fine){
  .sc-fim-op:hover{ transform:translateY(-2px); border-color:rgba(232,105,10,.7); box-shadow:0 16px 38px rgba(0,0,0,.5); }
}
/* em paisagem (a prática é landscape) o cartão não pode passar da tela */
@media (orientation:landscape) and (max-height:520px){
  .sc-fim-modal .ccm-card{ max-height:92vh; overflow-y:auto; }
  .sc-fim-nota{ font-size:21px; margin-bottom:10px; }
  .sc-fim-op{ min-height:52px; padding:10px; }
}

/* ---- [12] MINHAS PARTITURAS · a FOLHA ------------------------------------
   Retrato, a página inteira rolando, uma gravura por PAUTA. O alvo de toque é
   a LINHA toda (o dedo não precisa achar a cabeça da nota — isso é trabalho do
   editor, que abre logo em seguida). */
.mp-folha-page{ --mp-paper:#F6F1E7; }
.mp-folha-hint{ font-family:var(--font-h);font-weight:800;font-size:15px;color:var(--muted);
  text-align:center;margin:0 0 16px; }
.mp-folha-pautas{ display:flex; flex-direction:column; gap:12px; padding-bottom:12px; }
.mp-folha-pauta{ display:flex; align-items:center; gap:10px; width:100%; padding:8px 10px;
  background:var(--mp-paper); border:1.5px solid rgba(255,255,255,.10); border-radius:12px;
  cursor:pointer; -webkit-appearance:none;appearance:none;font:inherit;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation;
  transition:transform .16s,border-color .16s; }
.mp-folha-pauta:active{ transform:scale(.985); border-color:rgba(232,105,10,.6); }
@media (hover:hover) and (pointer:fine){
  .mp-folha-pauta:hover{ border-color:rgba(232,105,10,.55); }
}
.mp-folha-pauta-n{ flex:0 0 26px;height:26px;border-radius:8px;background:rgba(20,20,20,.10);
  color:#3a3a3a;font-weight:900;font-size:13px;display:flex;align-items:center;justify-content:center; }
.mp-folha-pauta-sv{ flex:1;min-width:0;display:block; }
.mp-folha-pauta-sv svg{ width:100%!important;height:auto!important;display:block; }
/* o que ainda não foi escrito: pausa-fantasma, o mesmo esmaecido do editor */
.mp-folha-pauta-sv .mp-ghost{ opacity:.28; }
/* 🔴 [19/09 · Luis] *"tem quatro botoes retangulares (…) diminua um pouquinho a
   largura deles, pois atualmente eles encostam nas bordas do papel da partitura
   e eu nao gosto."*
   O rodapé nunca teve recuo lateral: as fichas nasciam com a MESMA caixa das
   pautas (16 px de cada lado no telemóvel, 40 no tablet), então a borda da
   ficha caía EM CIMA da borda do papel — e é sobre o papel que este rodapé
   pousa, porque ele é `sticky` e a folha rola por baixo.
   O recuo entra no RODAPÉ, e não na fileira: a chave e o CTA descem no mesmo
   eixo das fichas, e estreitar só as fichas deixaria as três larguras diferentes
   na mesma pilha — que é o defeito que ele está a apontar, ao contrário. */
.mp-folha-foot{ position:sticky; bottom:0; padding:12px 12px calc(10px + env(safe-area-inset-bottom));
  background:linear-gradient(to top,var(--bg,#070707) 62%,transparent); }
/* ============================================================================
   [30/08 · Luis] AS QUATRO UTILIDADES NUMA FILEIRA SÓ — direção B.
   ============================================================================
   Saíram daqui: o par `.mp-exp-par` (Exportar PDF | Exportar MIDI), o par
   `.mp-folha-par` (Apagar vermelho | Nova pauta laranja) e o `min-height:52px`
   que existia só para as duas metades imitarem a altura do Praticar. A peça que
   aquele número media deixou de existir, então o número foi embora com ela.

   As quatro são utilidades secundárias e agora parecem quatro: mesma largura
   (`flex:1 1 0` com base ZERO, senão o rótulo mais longo faria a sua ficha
   crescer), mesma altura, ícone acima do rótulo. O rótulo quebra em até 3
   linhas porque as traduções são longas ("Exporter en MIDI", "Neue Notenzeile")
   e cortar texto com reticências esconde justamente a palavra que diferencia. */
.mp-folha-util{ display:flex; gap:8px; margin-bottom:10px; align-items:stretch; }
.mp-util-btn{ flex:1 1 0; min-width:0; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:5px; min-height:56px; padding:7px 4px;
  /* 🔴🔑 [16/09 · Luis] *"na pagina da musica, quando os botoes exportar pdf,
     midi, pauta, apagar, estao em cima da partitura, ficam quase invisiveis pois
     os botoes sao brancos e o papel da partitura tambem. aumente a opacidade dos
     botoes inteiros mantendo o texto branco."*

     O fundo era CREME A 6,6 % — desenhado contra o #070707 da página, onde 6,6 %
     de creme já é um degrau visível. Por cima do PAPEL, que é branco, 6,6 % de
     creme sobre branco é branco: o botão desaparecia e ficava só o texto creme a
     93,6 %, que sobre branco também é branco. Não era contraste a menos: era o
     fundo a ser TRANSPARENTE numa tela onde o que está por trás mudou de cor.
     A cura é a que ele nomeou — opacidade no botão INTEIRO. O fundo passa a ser
     opaco e escuro, logo não depende do que está atrás, e o texto fica branco por
     cima dele. Na página escura o botão continua a ler-se igual (é a mesma
     família de cinzentos do `.mp-act`, app.css:6964); sobre o papel passa a
     existir. [31/08 · item 05] o "+20 % em cada alfa" sai com este bloco: o que
     ele media era um fundo translúcido que deixou de existir.

     🔴 [16/09 · LOTE 4h · Luis] *"os botoes de exportar pdf, exportar midi, etc,
     na pagina da musica ficou 100% opaco, diminua um pouquinho a opacidade."*
     Um pouquinho é o que ele pediu e é o que se dá: **88 %**. A conta que manda
     continua a ser a da ordem anterior — o botão tem de EXISTIR por cima do papel
     branco. A 88 % o fundo sobre papel resolve em (52,52,52), que com o texto
     branco dá **12,4:1** de contraste; e sobre o #070707 da página escura resolve
     em (22,22,22) contra os (24,24,24) de antes — dois pontos, invisíveis. É a
     translucidez a voltar onde ela se lê, sem reabrir o defeito que a tirou. */
  border-radius:12px; background:rgba(24,24,24,.88); border:1px solid rgba(246,241,231,.26);
  color:#F6F1E7; font-family:var(--font-h); font-weight:700;
  font-size:9.5px; line-height:1.08; letter-spacing:.04em; text-align:center;
  overflow:hidden; overflow-wrap:anywhere;
  -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  -webkit-appearance:none; appearance:none; cursor:pointer;
  transition:transform .14s, background .14s, border-color .14s, color .14s; }
.mp-util-btn svg{ width:16px; height:16px; flex:0 0 auto; }
/* 🔴 O RÓTULO OCUPA DUAS LINHAS SEMPRE, mesmo quando cabe em uma. Sem isto a
   ficha do MIDI (a única cujo texto quebra em pt) ficava mais alta por dentro e
   empurrava o ícone dela para cima: quatro fichas do mesmo tamanho com um ícone
   fora do eixo dos outros três. Reservar as duas linhas alinha os quatro em
   QUALQUER idioma — e é o mesmo motivo de não cortar o texto com reticências. */
.mp-util-btn span{ display:block; min-height:2.16em; }
.mp-util-btn:active{ transform:scale(.97); background:#262626; }
/* 🔴 Apagar é destrutivo e PARA DE GRITAR EM REPOUSO. Vermelho cheio encostado
   no laranja do CTA era o par de cores mais barato da tela, e cor não protege
   ninguém: quem protege é a confirmação (mp.pauta.delconf), que sabe o que vai
   perder. A cor vermelha volta no toque, que é quando ela informa alguma coisa. */
.mp-util-btn.is-del:active{ background:#4A1512; border-color:rgba(224,90,80,.55);
  color:#FFC9C2; }
.mp-util-btn[disabled]{ opacity:.34; pointer-events:none; }

/* ══ [16/09 · Luis] AS PORTAS DE ENTRADA VOLTAM A CARDS HORIZONTAIS ═════════
   *"os botoes na pagina minhas musicas (nova partitura, importar arquivo, etc)
     volte para cards horizontais em linha, mas agora sem as cores. mantenha
     cores e estilo dos botoes atual."* e, a seguir: *"faca eles com algum tipo
     de diferenciacao clara dos cards de musica."*

   🔑 SÃO DUAS ORDENS QUE SE ANULAM SE LIDAS SOZINHAS, e juntas dizem uma coisa
   só: a FORMA volta a ser a de antes (uma linha por porta, ícone à esquerda,
   rótulo ao lado) e a TINTA é a de hoje (a família neutra do `.mp-util-btn` —
   nada dos quatro acentos laranja/azul/violeta/verde que ele revogou de manhã).
   Esta regra não redesenha nada: só troca o EIXO da mesma peça.

   🔴 E A DIFERENCIAÇÃO NÃO PODE SER A COR, porque a cor é justamente o que ele
   tirou. É a LINHA: a porta é TRACEJADA e sem preenchimento, o card de música é
   contínuo e preenchido (`.sc-lesson`, app.css:6668). Tracejado lê-se como
   "ainda não existe, vai nascer" em qualquer interface, e é o que estas quatro
   fazem — criam peça. O ícone fica num quadrado de cantos suaves, contra o
   CÍRCULO do `♪` do card de música: duas formas diferentes à mesma distância do
   olho, sem uma palavra a explicar.

   ⚠️ (0,3,1) de propósito: `html.tb .mp-util-btn` e `html.dt .mp-util-btn`
   valem (0,2,1) e mandam as fichas para 44 % de largura / 2 por linha. Aqui a
   porta é sempre UMA por linha, e a especificidade tem de ganhar sem depender
   da ordem do ficheiro — que é a lição que esta frente já pagou três vezes. */
html .mp-folha-util.mp-hub-util{
  display:flex; flex-direction:column; flex-wrap:nowrap; gap:8px; margin:14px 0 0;
}
html .mp-folha-util.mp-hub-util .mp-util-btn{
  flex:0 0 auto; width:100%;
  flex-direction:row; align-items:center; justify-content:flex-start; gap:12px;
  min-height:58px; padding:12px 14px; border-radius:14px;
  background:transparent; border:1.5px dashed rgba(246,241,231,.26);
  font-size:13px; letter-spacing:.02em; text-align:left; line-height:1.25;
  overflow-wrap:break-word;
}
html .mp-folha-util.mp-hub-util .mp-util-btn:active{
  background:rgba(246,241,231,.08); border-color:rgba(246,241,231,.42);
}
/* o ícone no quadrado — o contraponto do círculo do card de música */
html .mp-folha-util.mp-hub-util .mp-util-btn svg{
  width:20px; height:20px; box-sizing:content-box;
  padding:7px; border-radius:11px;
  background:rgba(246,241,231,.07); border:1px solid rgba(246,241,231,.16);
}
/* 🔴 o `min-height:2.16em` do rótulo era para alinhar QUATRO fichas em coluna
   com alturas de texto diferentes. Em linha não há o que alinhar, e ele só
   deixava a porta 20 px mais alta do que o texto pede. */
html .mp-folha-util.mp-hub-util .mp-util-btn span{ min-height:0; flex:1 1 auto; }
/* [16/09 · LOTE 4h] a SEGUNDA LINHA: o formato/dica por baixo do rótulo. Fica
   `block` para quebrar a linha sozinha, dois pontos mais pequena e na cor
   apagada — o rótulo continua a ser o que se lê primeiro, e a dica responde ao
   "de que tipo?" sem competir com ele.
   ⚠️ SEM `text-transform`: eu ia escrever `none` "porque o botão está em caixa
   alta", e fui medir antes — o `.mp-util-btn` resolve `text-transform:none` em
   todas as caixas. A regra teria sido inerte e o comentário ficava a mentir
   sobre a tela a partir do dia em que alguém a lesse.
   Medido depois da cura (P1 e C): as quatro portas continuam com a MESMA altura
   (62 px no telemóvel, 60 no tablet deitado) — a "Nova partitura", que não tem
   dica, não fica mais baixa que as outras três. */
html .mp-folha-util.mp-hub-util .mp-util-btn small{
  display:block; margin-top:3px;
  font-size:11px; font-weight:600; letter-spacing:.01em; line-height:1.3;
  color:rgba(246,241,231,.58);
}

/* ---- o SEGMENTADO do modo ------------------------------------------------
   "Praticar partitura" e "Praticar na fita" eram dois botões empilhados que só
   se distinguiam pela última palavra. São a mesma ação em dois MODOS — então a
   tela pergunta o modo uma vez e deixa um CTA só.
   🔴 O indicador é CREME, não laranja: o CTA logo abaixo é laranja, e dois
   saturados encostados voltariam a disputar a leitura. Quem diz "sou o ligado"
   é o texto branco e o ícone na cor da marca. */
/* 🔴🔑 [19/09 · Luis] *"a chave seletora partitura e fita esta muito fina e os
   nomes estao mais para baixo do que para cima dentro da chave. torne a chave
   seletora da mesma altura dos botoes e coloque os nomes centralizados
   verticalmente dentro da chave."*

   SÃO O MESMO DEFEITO, e o segundo é consequência do primeiro. A chave estava
   cravada em 42 px enquanto a ficha do Exportar mede 57,5 no telemóvel e 67,2
   no tablet — e no TABLET o `html.tb .mp-modo-btn{min-height:48px}` (o piso de
   alvo do dedo) é MAIOR do que os 34 px que sobravam por dentro da chave de 42.
   O botão transbordava 10 px para BAIXO, e como o alinhamento da fileira é
   `stretch` (que ancora em cima), o rótulo centrado dentro do botão de 48 caía
   20,3 px abaixo do topo contra 6,3 do fundo. Medido.
   🔑 Não se cura mexendo no alinhamento do texto: o texto já estava centrado
   DENTRO do botão. O que estava fora do sítio era o botão dentro da chave.

   A altura é a receita da ficha, parcela a parcela (`.mp-util-btn`):
   14 (padding 7+7) + 16 (ícone) + 5 (vão) + 2,16em de rótulo + 2 (borda).
   Com o rótulo a 9,5 px dá 57,52; a 12 px (computador) 62,9 — e aí manda o
   `min-height:64` da própria ficha; a 14 px (tablet) 67,24.
   `min-height` e não `height`: um rótulo que quebre em duas linhas faz a chave
   CRESCER em vez de cortar o texto — e com 57,5 de piso ela não encolhe nunca. */
.mp-modo{ position:relative; display:flex; min-height:57.5px; margin-bottom:10px; padding:3px;
  background:rgba(246,241,231,.072); border:1px solid rgba(246,241,231,.12); border-radius:12px; }   /* [item 05] x1,2 */
.mp-modo-ind{ position:absolute; top:3px; left:3px; width:calc(50% - 3px); height:calc(100% - 6px);
  background:rgba(246,241,231,.168); border-radius:9px;   /* [item 05] .14 x1,2 */
  box-shadow:inset 0 1px 0 rgba(255,255,255,.12);
  transition:transform .26s cubic-bezier(.4,0,.2,1); }
.mp-modo[data-modo="fita"] .mp-modo-ind{ transform:translateX(100%); }
.mp-modo-btn{ position:relative; z-index:1; flex:1 1 0; min-width:0; background:transparent;
  border:none; display:flex; align-items:center; justify-content:center; gap:7px;
  font-family:var(--font-h); font-weight:800; font-size:13.5px; line-height:1.1;
  color:rgba(246,241,231,.66); cursor:pointer;   /* [item 05] .55 x1,2 */
  -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  -webkit-appearance:none; appearance:none; transition:color .2s; }
.mp-modo-btn svg{ width:15px; height:15px; flex:0 0 auto; }
.mp-modo-btn[aria-pressed="true"]{ color:#fff; }
.mp-modo-btn[aria-pressed="true"] svg{ color:var(--primary-2,#FF7B1A); }


.mp-pdf{ position:fixed; inset:0; z-index:70; display:flex; align-items:center; justify-content:center;
  background:rgba(0,0,0,.62); backdrop-filter:blur(2px); -webkit-backdrop-filter:blur(2px); }
.mp-pdf[hidden]{ display:none; }
.mp-pdf-card{ position:relative; display:flex; flex-direction:column; gap:10px;
  background:#161616; border:1px solid rgba(255,255,255,.1); border-radius:16px;
  padding:14px; width:min(420px,92vw); max-height:92vh; }
.mp-pdf-x{ position:absolute; top:6px; right:8px; width:32px; height:32px; border-radius:50%;
  background:transparent; color:rgba(246,241,231,.6); font-size:22px; line-height:1; }
.mp-pdf-status{ font-size:12.5px; font-weight:700; color:rgba(246,241,231,.7);
  text-align:center; padding-right:26px; }
.mp-pdf-palco{ flex:1 1 auto; min-height:0; overflow:auto; display:flex; justify-content:center; }

/* A FOLHA da prévia. `aspect-ratio` de A4 (595.28×841.89) — não é enfeite:
   é o que garante que o que o aluno vê tem a MESMA proporção do arquivo.
   Fundo branco fixo porque papel é branco, mesmo com o app escuro. */
.mp-pdf-folha{ position:relative; width:100%; aspect-ratio:595.28/841.89;
  background:#fff; border-radius:4px; overflow:hidden; flex:0 0 auto; align-self:flex-start; }
.mp-pdf-sis{ position:absolute; }
.mp-pdf-sis svg{ display:block; width:100%; height:auto; }
.mp-pdf-tit{ position:absolute; left:0; right:0; top:3.5%; text-align:center;
  font-size:2.9cqw; font-weight:800; color:#111; }
.mp-pdf-sub{ position:absolute; left:0; right:0; top:7.4%; text-align:center;
  font-size:1.7cqw; color:#555; }
.mp-pdf-pe{ position:absolute; left:6.7%; bottom:3.4%; font-size:1.25cqw; color:#8a8a8a; }
/* container query para o texto acompanhar a largura da folha em qualquer tela */
.mp-pdf-folha{ container-type:inline-size; }
/* a marca d'água: cinza clara, 45°, POR BAIXO (z-index 0 contra os sistemas
   que vêm depois no fluxo) — a mesma ordem do content stream do PDF */
.mp-pdf-marca{ position:absolute; left:50%; top:50%; z-index:0;
  transform:translate(-50%,-50%) rotate(-45deg); transform-origin:center;
  font-size:10.5cqw; font-weight:800; color:#ededed; white-space:nowrap; pointer-events:none; }
/* [30/07 · Luis] a marca virou LOGOMARCA. Sem rotação: logo torto lê como
   erro, não como marca d'água. A opacidade aqui espelha a fusão que o
   __mpPdfMarcaImagem faz no canvas — se as duas divergirem, a prévia mente. */
.mp-pdf-marca.is-logo{ transform:translate(-50%,-50%); width:52cqw; height:auto; }
.mp-pdf-marca.is-logo img{ width:100%; height:auto; display:block; opacity:.09;
  /* 🔴 o MESMO invert(1) que o __mpPdfMarcaImagem faz no canvas: o ícone é
     piano CLARO sobre placa ESCURA, e sem inverter o que aparece no papel é a
     placa (um quadrado cinza), não o piano. Se este filtro sair daqui, a
     prévia deixa de bater com o arquivo. */
  filter:invert(1) grayscale(1); }

.mp-pdf-acts{ display:flex; gap:8px; flex:0 0 auto; }
.mp-pdf-act{ flex:1 1 0; padding:12px; border-radius:12px; font-size:14px; font-weight:800;
  background:#1d1d1d; color:#F6F1E7; border:1px solid rgba(255,255,255,.16); }
.mp-pdf-act.is-primary{ background:var(--primary,#E8690A); border-color:var(--primary,#E8690A); color:#111; }
.mp-pdf-act[disabled]{ opacity:.4; pointer-events:none; }
.mp-pdf-act:active{ transform:scale(.97); }

/* ============ [30/07] importar arquivo + conferência ============ */
/* 🗑️ [16/09] `.mp-imp-abrir` (2 regras) — as portas de importar/escanear/vídeo
   deixaram de ter `<small>` e de ser `.sc-lesson` (lápide do bloco [12]). */
/* [22/08 · Luis] AQUI MORAVAM DUAS REGRAS DAS PORTAS DE ÁUDIO:
   · `.mp-aud-zap` — o aviso do WhatsApp na 2ª linha da porta. Já estava órfão
     desde 16/08, quando o Luis mandou o aviso sair; agora a porta inteira foi.
   · `.mp-aud-meter-opt` — a escolha de fórmula de compasso, que era passo
     DELAS e de mais ninguém.
   Nenhuma das duas tem HTML que case depois desta remoção. */

.mp-imp{ position:fixed; inset:0; z-index:72; display:flex; align-items:center; justify-content:center;
  background:rgba(0,0,0,.62); backdrop-filter:blur(2px); -webkit-backdrop-filter:blur(2px); }
.mp-imp[hidden]{ display:none; }
.mp-imp-card{ position:relative; display:flex; flex-direction:column; gap:9px;
  background:#161616; border:1px solid rgba(255,255,255,.1); border-radius:16px;
  padding:14px; width:min(460px,93vw); max-height:92vh; }
.mp-imp-tit{ font-size:15px; font-weight:800; color:#F6F1E7; padding-right:30px; }
.mp-imp-status{ font-size:12px; font-weight:700; color:rgba(246,241,231,.62);
  text-transform:uppercase; letter-spacing:.03em; }
/* a peça desenhada: fundo BRANCO porque pauta é preta sobre papel, e o aluno
   precisa conferir a gravura, não admirar o tema escuro do app */
.mp-imp-palco{ flex:1 1 auto; min-height:0; overflow:auto; background:#fff;
  border-radius:8px; padding:8px 6px; }
.mp-imp-palco:empty{ display:none; }
.mp-imp-sis svg{ display:block; width:100%; height:auto; }
.mp-imp-mais{ text-align:center; font-size:11px; color:#999; margin:2px 0 0; }
.mp-imp-perdas{ flex:0 0 auto; max-height:26vh; overflow:auto; }
.mp-imp-perdas-tit{ margin:0 0 4px; font-size:12.5px; font-weight:800; color:#E8B34A; }
.mp-imp-perdas-tit.is-ok{ color:#7BC67B; }
.mp-imp-lista{ margin:0; padding-left:16px; }
.mp-imp-lista li{ font-size:12px; line-height:1.45; color:rgba(246,241,231,.72); }
.mp-imp-incerto{ margin:6px 0 0; font-size:12px; font-weight:700; color:#E8B34A; }

/* ===== [10/08 · transcrição] áudio → partitura =============================
   Tudo aqui só pinta elemento que a flag TT_TRANSCRICAO_PRONTA cria. Com a
   flag desligada estas regras existem sem alvo — CSS sem elemento não muda
   um pixel, e é isso que o A/B com a flag false tem de mostrar. */

/* o aviso de ritmo, DENTRO do palco branco e ANTES da peça: quem lê depois de
   já ter olhado a partitura leu tarde demais. Cor de alerta sobre o papel. */
.mp-imp-aviso{ margin:0 0 8px; padding:7px 9px; border-radius:7px;
  background:#FFF4DA; border:1px solid #E8B34A; color:#7A5310;
  font-size:12px; font-weight:700; line-height:1.4; }

/* 🔒 A NOTA DUVIDOSA É PINTADA, NUNCA APAGADA (trava do Luis). Por isso é cor
   e halo, não remoção nem opacidade baixa: a nota continua legível e tocável;
   o que mudou foi só o aviso de que ela merece um olhar. O ponto de operação
   (H3 + onset < 0,80) marca 17% das notas com precisão de 48% — quase metade
   do que for pintado está CERTO, então esconder ou apagar seria pior que
   inútil. Alvo é o <g> que o score.js já marca com data-ev/data-ev-bass. */
.mp-imp-sis .is-duvidosa{ color:#C2410C; }
.mp-imp-sis .is-duvidosa path,
.mp-imp-sis .is-duvidosa rect,
.mp-imp-sis .is-duvidosa ellipse,
.mp-imp-sis .is-duvidosa circle{ fill:#C2410C; stroke:#C2410C; }
.mp-imp-sis .is-duvidosa text{ fill:#C2410C; }

/* A RECUSA reusa a casca do card da conferência (mesma moldura, mesmo fundo,
   mesmo × de fechar) — são telas irmãs, não um segundo sistema.
   [22/08 · Luis] Este bloco era "GRAVAR e RECUSA". A tela de GRAVAR saiu com
   as duas portas de áudio, e com ela o `.mp-rec-card`, o `.mp-rec-aviso` (o
   aviso da caixa de som), o `.mp-rec-tempo` (o relógio) e o `.mp-rec-status`.
   ⚠️ `.mp-recusa` NÃO é `.mp-rec` com sufixo: é a tela de recusa, que a 6ª
   porta (vídeo de notas caindo) usa para dizer por que não deu. Os dois nomes
   dividiam as duas primeiras regras — aqui elas passam a ser só dela. */
.mp-recusa{ position:fixed; inset:0; z-index:73; display:flex;
  align-items:center; justify-content:center;
  background:rgba(0,0,0,.62); backdrop-filter:blur(2px); -webkit-backdrop-filter:blur(2px); }
.mp-recusa[hidden]{ display:none; }
.mp-recusa-txt{ margin:0; font-size:13.5px; line-height:1.5;
  color:rgba(246,241,231,.82); }

/* 🔴 NA CONFERÊNCIA, SÓ A NOTA PINTADA RECEBE TOQUE — e isto não é enfeite.
   MEDIDO no navegador: a LINHA DA PAUTA (<path> do vf-stave) é desenhada por
   cima e INTERCEPTA o ponteiro sobre a cabeça de nota. O toque chegava na
   linha, o `closest('[data-mp-duv]')` subia pelos ancestrais DELA e devolvia
   null — a nota simplesmente não respondia. Só notas altas, fora da pauta,
   funcionavam, o que esconderia o defeito num teste descuidado.
   Apagar o ponteiro no SVG inteiro e devolvê-lo só a quem é alvo é mais
   robusto que caçar a linha: barra também barra de compasso, ligadura e
   qualquer coisa que o VexFlow desenhe por cima amanhã. A conferência não
   tem nenhum outro alvo de clique dentro da gravura. */
.mp-imp-sis svg{ pointer-events:none; }
.mp-imp-sis .is-duvidosa{ pointer-events:auto; }

/* a nota pintada que É cabeça de acorde vira ALVO DE TOQUE. O alvo do dedo é
   o <g> inteiro da nota (cabeça + haste), não a elipse de 4px da cabeça. */
.mp-imp-sis .is-alvo{ cursor:pointer; }
.mp-imp-sis .is-alvo:active{ opacity:.55; }

/* o diálogo CURTO — desenho do Luis: título com o nome da nota e dois botões.
   z acima da conferência porque nasce DE DENTRO dela. */
.mp-duv{ position:fixed; inset:0; z-index:74; display:flex;
  align-items:center; justify-content:center; background:rgba(0,0,0,.55); }
.mp-duv[hidden]{ display:none; }
.mp-duv-card{ display:flex; flex-direction:column; gap:13px;
  background:#161616; border:1px solid rgba(255,255,255,.12); border-radius:14px;
  padding:16px 18px; width:min(320px,88vw); text-align:center; }
.mp-duv-tit{ font-size:15px; font-weight:800; color:#F6F1E7; }
.mp-duv-sub{ margin:0; font-size:12.5px; line-height:1.45; color:#E8B34A; }
.mp-duv-sub[hidden]{ display:none; }
.mp-duv-acts{ display:flex; gap:9px; justify-content:center; }
.mp-duv-acts .mp-pdf-act{ flex:1 1 0; }
.mp-duv-acts .mp-pdf-act[hidden]{ display:none; }
/* apagar é DESTRUTIVO e tem de parecer: o aluno está tirando uma nota da
   própria peça. Mas sem alarme — quase metade do que é pintado está certo. */
.mp-duv-acts .is-del{ color:#E8785A; border-color:rgba(232,120,90,.4); }
.mp-folha-praticar{ width:100%; }
.mp-folha-praticar[disabled]{ opacity:.42; pointer-events:none; filter:grayscale(.6); }

/* ---- [12] MINHAS PARTITURAS · o EDITOR DE UMA PAUTA (retrato) ------------
   Empilhado de cima para baixo: pauta · anel grande · ações · piano. */
/* 🔴 O ORÇAMENTO DE ALTURA DO EDITOR, medido em 390×844 com a tab bar de 58px:
   sobram 786px para topo(44) + pauta + anel + ações(48) + registro(38) + piano.
   O anel dimensionado SÓ pela largura (72vw = 281px) estourava esse orçamento e
   empurrava o piano para fora da tela — por isso todas as três peças grandes
   passaram a ter também um teto em `vh`. Sem o teto em altura, "maior e mais
   confortável" vira "invisível". */
.mp-edit-page{ --mp-kbd-h:min(17vh,132px); --mp-oct-h:42px; }
/* 🔴 [30/07 · Luis] "vamos subir o título da pauta que fica junto à nav tag,
   ela não está no topo da página e pode subir para economizar espaço." */
.mp-ed-top{ display:flex;align-items:center;gap:10px;padding:2px 12px 4px;flex:0 0 auto;min-height:0; }
.mp-ed-tit{ display:flex;flex-direction:column;min-width:0;flex:1; }
.mp-ed-pauta-n{ font-size:11.5px;font-weight:800;color:var(--muted); }
/* o que ROLA: pauta + anel + ações. O piano e o registro ficam fora dele. */
/* 🔴 `flex:1 1 0`, NÃO `1 1 auto`. Com base `auto` o rolo entra na conta com a
   altura do CONTEÚDO e o encolhimento não fechava: medido em 390×844, ele ficava
   com 512px onde cabiam 483, e os 29px de sobra saíam do PIANO (que é `flex:0 0`
   e não cede). Com base ZERO o rolo recebe exatamente o que sobra depois das
   peças fixas — o piano nunca mais paga a conta de ninguém. */
/* com a barra do instrumento FIXA, o miolo é fluxo normal: rola a página */
/* deixou de ser "rolo": `min-height:0` é o que permite os filhos encolherem
   dentro de um flex — sem ele o conteúdo empurra e o overflow:hidden RECORTA
   em vez de acomodar (é a diferença entre caber e ser cortado). */
.mp-ed-rolo{ flex:1 1 0;min-height:0;display:flex;flex-direction:column;overflow:hidden; }
.mp-edit-page .kbd-wrap.mp-ed-kbd{ margin:0; }
/* 🔴 [30/07 · Luis] "o balão branco que abriga a pauta é muito espesso, pode
   diminuir a fim de abrigar apenas a pauta sem deixar grande espaço branco
   vazio." O espesso vinha do SVG (o VexFlow reservava folga que a pauta não
   usa — recortado no __mpRender); aqui sobra tirar o respiro do papel. */
.mp-ed-pauta{ flex:0 0 auto;padding:0 12px 4px; }
.mp-ed-pauta .mp-paper{ padding:6px 8px; border-radius:10px; }
/* a gravura é vetor: desenhada no plano de 640 e escalada pela LARGURA. O
   `width` que o __mpRender calculava para o wrap era para o caso do plano ser
   o da tela — agora só atrapalharia. */
.mp-ed-pauta .mp-wrap{ width:100%!important; }
/* o ANEL, "bem maior e mais confortável para clicar" (pedido explícito):
   era min(122px,27vh) na coluna lateral do landscape; aqui manda a LARGURA. */
/* 🔴 [30/07 · Luis] "a estrutura de anéis está pequena, pode aumentar o
   suficiente para encaixar todo o conteúdo restante abaixo na mesma página sem
   scroll." `flex:1 1 0` (base ZERO, e PODE encolher): o anel passa a receber
   exatamente o que sobra depois das peças fixas — cresce nas telas altas e
   cede nas curtas, que é o que faz a página inteira caber sem rolar. */
.mp-ed-anel{ flex:1 1 0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:6px;min-height:0;padding:2px 12px; }
/* 🔴 O ANEL É DIMENSIONADO PELO QUE SOBRA, não por um teto em `vh` cravado.
   Teto em vh não fecha em tela nenhuma exceto a que foi usada para calibrá-lo:
   as peças fixas somam ~376px, então a fração livre é (H-376)/H — 0,44 num
   844, 0,43 num 667, 0,34 num 568. Um número só serve para as três é mentira,
   e com `overflow:hidden` a mentira vira anel CORTADO.
   `height:100%` + `aspect-ratio:1` resolve pela caixa: o anel recebe a altura
   que o flex deu ao `.mp-ed-anel` e vira um quadrado nela. Cresce sozinho na
   tela alta, cede sozinho na curta. O `max-width` só impede que ele fique
   maior que a largura em telas muito altas. */
.mp-roda-grande{ height:100%; width:auto; aspect-ratio:1/1; max-width:min(88vw,340px);
  max-height:min(88vw,340px); }
.mp-roda-grande .mp-fig{ width:min(20vw,9vh,84px);height:min(20vw,9vh,84px);
  margin:calc(min(20vw,9vh,84px) / -2) 0 0 calc(min(20vw,9vh,84px) / -2); }
.mp-roda-grande .mp-fig svg{ width:min(11vw,4.6vh,46px);height:min(14.6vw,6.1vh,60px); }
.mp-roda-grande .mp-fig-pausa svg{ width:min(11vw,4.6vh,46px);height:min(14.6vw,6.1vh,60px); }
/* o pill do "2,3…" tem escala PRÓPRIA: no mesmo 1.45 dos outros ele fica com
   95px de largura e atravessa o aro interno — o texto é maior que um glifo. */
.mp-roda-grande .mp-tog{ transform:scale(1.45); }
.mp-roda-grande .mp-tog-tup{ transform:scale(1.12); }
.mp-roda-grande .mp-raz{ transform:scale(1.45); }
.mp-ed-acts{ flex:0 0 auto;display:flex;gap:10px;padding:0 12px 6px; }
.mp-ed-acts .mp-act{ flex:1;padding:min(11px,1.4vh) 10px;font-size:14px; }
/* o registro do mini piano (◀ Dó4–Dó6 ▶) */
.mp-ed-oitava{ flex:0 0 auto;display:flex;align-items:center;justify-content:center;gap:12px;padding:0 12px 5px; }
.mp-oct-btn{ width:44px;height:min(32px,3.8vh);border-radius:9px;background:#1d1d1d;color:#F6F1E7;
  border:1px solid rgba(255,255,255,.16);font-size:13px;font-weight:900;padding:0;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation; }
.mp-oct-btn:active{ transform:scale(.94); }
.mp-oct-lbl{ font-family:var(--font-h);font-weight:800;font-size:12.5px;color:var(--muted);
  min-width:92px;text-align:center;font-variant-numeric:tabular-nums; }
/* 🔴 A TELA DO EDITOR CABE NA TELA — as duas metades deste conserto.
   MEDIDO em 390×844: o piano tinha 128 dos seus 180px atrás da tab bar.
   Reservar o rodapé sozinho NÃO resolveu: a página media 913px num viewport de
   844, porque `.page` cresce com o conteúdo. Com a página maior que a tela, a
   reserva de 58px ficava a 855px — fora do vidro.
   Então são DUAS coisas, e as duas são necessárias:
     (a) a página é PRESA à altura da tela (100dvh, o mesmo que o resto do app
         usa por causa da barra do Safari), e
     (b) reserva o rodapé da tab bar — `env()` CRU, como a própria barra faz:
         se o JS de medição falhar, reserva a MAIS (feio) em vez de a MENOS
         (piano coberto).
   O `overflow:hidden` fecha a conta: quem cede espaço é o anel (flex:1), não o
   piano — perder metade do anel é feio, perder o piano é perder a feature. */
/* 🔴 A TELA DO EDITOR CABE NA TELA — e por que NÃO com position:fixed.
   MEDIDO: com a barra do instrumento em `position:fixed; bottom:58px`, ela
   parava 16px baixo demais. Causa: `position:fixed` só ancora no viewport
   quando NENHUM ancestral tem transform/filter/backdrop-filter — e a `.page`
   tem transform (é a animação de troca de tela). O bloco contêiner virou a
   PÁGINA, que é mais alta que o vidro. É a mesma armadilha que já derrubou a
   seta do install-gate; aqui ela custou uma rodada.
   Então a barra volta ao FLUXO, e a conta é feita com três regras:
     · a página é presa à altura da tela e não deixa nada escapar;
     · o miolo (pauta+anel+ações) é quem ROLA;
     · o piano PODE encolher (até um piso), em vez de ser empurrado para fora.
   Piano menor é feio; piano fora da tela é a feature perdida. */
/* 🔴 O EDITOR PRECISA DE 3 CLASSES PARA CONTINUAR SENDO FLEX.
   `body.mode-interactive .page.visible{display:block}` (linha ~3576) tem TRÊS
   classes e vence qualquer `.mp-edit-page{display:flex}`. O editor escapava
   disso por ser `.practice-page`, que está na lista de exceções ali. Ao virar
   `.special` (escrever não é praticar) ele CAIU DA LISTA e virou `display:block`
   — e sem flex, toda regra de altura vira decoração: passei três rodadas
   mexendo em flex-basis, flex-shrink e vh com o computado dizendo "block".
   O próprio comentário daquela linha já avisava; a lição é MEDIR O COMPUTADO
   antes de ajustar a terceira casa decimal de um valor que não está sendo lido.
   Esta regra tem a mesma especificidade e vem depois. */
body.mode-interactive .mp-edit-page.visible{ display:flex; flex-direction:column; }
/* 🔴 A PÁGINA ROLA — não recorta.
   Medido em 6 aparelhos: prendendo a altura em 100dvh e recortando o excesso,
   as telas curtas (375×667, 320×568) perdiam de 92 a 162px do piano, mesmo com
   ele encolhendo até o piso. Recorte esconde; rolagem só pede um arrasto.
   Então a altura é MÍNIMA (cabe inteiro em 390+ sem rolar) e o rodapé reserva a
   tab bar, que é fixa e cobriria o piano. */
/* 🔴 [30/07 · Luis] "vamos fazer alterações no layout a fim de REMOVER
   COMPLETAMENTE O SCROLL da página; a página será toda vista sem scroll,
   scroll não estará habilitado."
   Isto inverte a decisão de 29/07, que preferiu rolar a recortar porque nas
   telas curtas o piano perdia até 162px. O que mudou desde então: o papel
   emagreceu (viewBox recortado no conteúdo real) e o anel passou a ser a peça
   elástica (`flex:1 1 0`). Agora quem cede é o ANEL, que tem de onde ceder —
   o piano continua com piso próprio e nunca paga a conta. */
/* 🔴 [05/08 · 3ª rodada · Luis · com PRINT] "essa página não tem necessidade de
   scroll: espaço sobrando no topo e piano cortado no meio lá embaixo. Ache um jeito
   de matar o scroll e deixá-la exatamente como no print, NATIVAMENTE."
   MEDIDO em 390×844 antes de mexer: documento com scrollHeight 854 contra 844 de
   viewport — 10px de rolagem; o teclado terminava em 796 e a tab bar começava em
   786, ou seja, o piano ficava POR BAIXO da barra. E a `.page` genérica declara
   `min-height:100vh` — que num iPhone com barra de endereço é MAIOR que 100dvh e
   sozinho já garante rolagem. Três coisas, um conserto:
     · `min-height:0` mata a herança do 100vh (a altura desta página é a do dvh);
     · `justify-content:flex-start` + `padding-top` PRÓPRIO tiram o vão de cima —
       a `.page` genérica reserva 48px+ para telas que têm cabeçalho flutuante, e
       esta tem cabeçalho próprio;
     · o rodapé reserva a barra INTEIRA (altura + safe-area), então o piano nunca
       mais divide espaço com ela.
   ⚠️ `100dvh` e não `100vh`: com a barra do Safari o vh mente, e é exatamente essa
   mentira que empurra o piano pra baixo da tab bar. */
.page.mp-edit-page{
  height:100dvh; max-height:100dvh; min-height:0; overflow:hidden; box-sizing:border-box;
  justify-content:flex-start;
  padding-top:calc(env(safe-area-inset-top, 0px) + 4px);
  padding-left:0; padding-right:0;
  padding-bottom:calc(58px + env(safe-area-inset-bottom)); }
html.tt-sab-js .page.mp-edit-page{ padding-bottom:calc(58px + var(--tt-sab, 0px)); }
/* as peças recuperam o respiro lateral que o padding da página dava */
.mp-edit-page .mp-ed-top{ padding-left:16px; padding-right:16px; }
/* 🔴 E A ENTRADA NÃO PODE TRANSLADAR ESTA PÁGINA. Medido: o `ttPageEnter` começa em
   `translateY(10px)`, e numa página de altura EXATA (100dvh) esses 10px viram 10px
   de rolagem — foi ele que empurrou a página pra baixo no print do Luis (o cabeçalho
   subia para fora, o piano descia para debaixo da tab bar). Aqui a entrada é só
   FADE: mesma sensação, zero geometria. */
body.mode-interactive .page.mp-edit-page.visible,
body.dia0-tour .page.mp-edit-page.visible{ animation-name:ttElemFade; }
.mp-ed-fundo{ flex:0 1 auto; min-height:0; display:flex; flex-direction:column;
  background:linear-gradient(to top, var(--bg,#070707) 78%, rgba(7,7,7,.86)); padding-top:4px; }
/* `0 1` (pode encolher) + PISO: o piano cede antes de sumir, e nunca abaixo do
   que ainda dá pra tocar */
.mp-edit-page .kbd-wrap.mp-ed-kbd{ flex:0 1 var(--mp-kbd-h);height:var(--mp-kbd-h);
  min-height:84px;position:relative;z-index:4; }
.mp-edit-page .kbd-wrap.mp-ed-kbd .kbd-svg{ width:100%;height:100%; }
/* a folha também rola até o fim sem a barra comer o botão Praticar */
.mp-folha-page .mp-folha-foot{ padding-bottom:calc(58px + env(safe-area-inset-bottom)); }
html.tt-sab-js .mp-folha-page .mp-folha-foot{ padding-bottom:calc(58px + var(--tt-sab, 0px)); }

/* 🔴 [12] O MINI PIANO ACESO — "se acende em uma animação especial indicando
   que ao clicar qualquer nota ali, ela será inserida".
   O aceso é ESTADO (a moldura fica viva enquanto houver figura armada); a
   ANIMAÇÃO é o instante em que ele arma. Transparência no ELEMENTO, cor sólida
   — alfa em currentColor de SVG soma nos cruzamentos e vira pontinho claro. */
.mp-ed-kbd{ border-radius:12px;transition:box-shadow .25s ease,filter .25s ease; }
.mp-ed-kbd.is-armado{ box-shadow:0 0 0 2px rgba(232,105,10,.55),0 0 26px rgba(232,105,10,.28); }
.mp-ed-kbd.kbd-acende{ animation:mpKbdAcende .62s cubic-bezier(.2,.8,.2,1); }
@keyframes mpKbdAcende{
  0%{ box-shadow:0 0 0 0 rgba(232,105,10,0),0 0 0 rgba(232,105,10,0); }
  38%{ box-shadow:0 0 0 4px rgba(232,105,10,.85),0 0 44px rgba(232,105,10,.55); }
  100%{ box-shadow:0 0 0 2px rgba(232,105,10,.55),0 0 26px rgba(232,105,10,.28); }
}
@media (prefers-reduced-motion:reduce){
  .mp-ed-kbd.kbd-acende{ animation:none; }
}

/* ===== [DIA0 v4] coreografia total do fluxo novo: legenda + destaque um-por-vez ===== */
/* Legenda de linha única (padrão visual do curso), acima do tabbar/safe-area. */
/* 🔴 left:50% + shrink-to-fit dá ao elemento só METADE da largura disponível
   (o bloco de contenção é o viewport, e a sobra à direita do left é o teto).
   Medido num iPhone de 414px: a legenda quebrava em 5 linhas usando 205px.
   left/right:0 + margin auto + fit-content deixa o texto usar os 92vw. */
/* 🔴 [05/08 · Luis] "o box que contém a legenda da locução não pode tampar a barra
   de navegação inferior — desloque-a um pouco pra cima."
   A conta é a MESMA da .tabbar (58px + safe-area) mais um respiro de 14px, e é uma
   conta, não um número mágico: no dia em que a barra mudar de altura a legenda
   acompanha. Onde a barra não existe (prática em paisagem, gates) o `body.tabbar-on`
   não está posto e a legenda volta pro rodapé — nada de vão vazio de 72px. */
#dia0-cap{ position:fixed; left:0; right:0; margin-inline:auto; width:fit-content;
  bottom:calc(env(safe-area-inset-bottom, 0px) + 18px);
  transform:translateY(8px); max-width:min(92vw, 720px);
  padding:10px 20px; border-radius:14px; background:rgba(10,8,6,.82);
  border:1px solid rgba(232,105,10,.35); color:#fff; text-align:center;
  font-weight:700; font-size:clamp(14px, 3.6vw, 18px); line-height:1.35;
  opacity:0; pointer-events:none; z-index:2147483200;
  transition:opacity .28s ease, transform .28s ease;
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); }
body.tabbar-on #dia0-cap{ bottom:calc(58px + env(safe-area-inset-bottom, 0px) + 14px); }
#dia0-cap.on{ opacity:1; transform:translateY(0); }
/* Destaque: anel laranja pulsante no alvo citado; o resto da tela escurece levemente.
   🔴 [05/08 · Luis] "ao mostrar o menu Técnica e scrollar em demonstração, criaram-se
   bugs visuais entre os cards." CAUSA: o anel era um box-shadow ANIMADO no próprio
   elemento — e o elemento destacado ali é a COLUNA INTEIRA de cards (.toc-phases).
   Animar box-shadow repinta a área toda a cada frame; os cards por baixo têm
   backdrop-filter; e a página está rolando ao mesmo tempo. É exatamente a receita de
   stale-paint que já mordeu a tabbar no iOS (ver reference_ios_fixed_backdrop_filter_tabbar).
   AGORA: o anel é um ::after com borda, e o que pulsa é a OPACIDADE dele — propriedade
   de composição, sem repaint. E o backdrop-filter dos filhos é desligado ENQUANTO o
   destaque está de pé (blur dentro de área destacada e rolando não sobrevive no iOS;
   o vidro volta sozinho quando o destaque sai). */
.dia0-hi{ position:relative; z-index:2147483100 !important; border-radius:12px; }
.dia0-hi::after{ content:""; position:absolute; inset:-3px; border-radius:inherit;
  border:3px solid rgba(232,105,10,.85); box-shadow:0 0 30px rgba(232,105,10,.35);
  pointer-events:none; z-index:1; animation:dia0HiPulse 1.5s ease-in-out infinite; }
@keyframes dia0HiPulse{ 0%,100%{ opacity:1; } 50%{ opacity:.55; } }
.dia0-hi, .dia0-hi *{ backdrop-filter:none !important; -webkit-backdrop-filter:none !important; }
/* sem o vidro, o card precisa de fundo PRÓPRIO — senão vira texto solto sobre a
   constelação enquanto está justamente sendo apontado. */
.dia0-hi .toc-phase, .dia0-hi.toc-phase{ background:rgba(18,18,18,.86) !important; }
body.dia0-spot::after{ content:""; position:fixed; inset:0; background:rgba(0,0,0,.34);
  pointer-events:none; z-index:2147483000; transition:opacity .3s ease; }
/* 🔴 [05/08 · print em 390px] O destaque sobe o alvo para z-index 2.147.483.100 — e o
   alvo da Técnica é a COLUNA INTEIRA de cards, mais alta que a tela. Resultado: os
   últimos cards eram pintados POR CIMA da barra de navegação (z-index 200), que é
   exatamente o que o Luis pediu pra não acontecer com a legenda. A barra passa a
   ficar acima do destaque enquanto ele existe — ela é moldura do app, não conteúdo
   apontado. Fica acima do véu também, de propósito: barra escurecida some do olho. */
body.dia0-spot .tabbar{ z-index:2147483150; }
@media (prefers-reduced-motion:reduce){ .dia0-hi::after{ animation:none; } }

/* ===== [05/08 · Luis] TRANSIÇÃO DE ETAPA NO TOUR — coreografada, não corte seco =====
   "faça transições de telas bem mais elegantes entre etapas, preservando o background
    estrelado vivo enquanto os elementos fazem transições coreografadas entre as telas."
   O fundo é o canvas #c (constelação), que vive ATRÁS de todas as .page e não é tocado
   por nenhuma regra daqui — é ele que costura as etapas, e por isso o que anima é só o
   CONTEÚDO. A saída sobe e some; a entrada vem de baixo em CASCATA (um filho por vez).
   ⚠️ A cascata é restrita a .tabroot-page/.toc-page — as telas de MENU do tour. Prática,
   fita e editor ficam de FORA de propósito: ali há geometria medida em pixels (o teclado
   que o detector lê, a gravura que o motor encaixa) e um transform de entrada mexeria
   justamente no que essas telas medem.
   ⚠️ A duração casa com _LEAVE_MS (index.html): mudar uma sem a outra devolve o corte. */
body.dia0-tour .page.page-leaving{ animation:dia0PageExit .42s cubic-bezier(.4,0,.6,1) both; }
@keyframes dia0PageExit{ from{ opacity:1; transform:none; } to{ opacity:0; transform:translateY(-10px) scale(1.025); } }
body.dia0-tour .page.visible{ animation:dia0PageEnter .5s ease; }
@keyframes dia0PageEnter{ from{ opacity:0; } to{ opacity:1; } }
body.dia0-tour .page.visible.tabroot-page > *,
body.dia0-tour .page.visible.toc-page > *{ animation:dia0ElemIn .5s cubic-bezier(.2,.8,.2,1) both; }
@keyframes dia0ElemIn{ from{ opacity:0; transform:translateY(18px); } to{ opacity:1; transform:none; } }
body.dia0-tour .page.visible.tabroot-page > *:nth-child(2),
body.dia0-tour .page.visible.toc-page > *:nth-child(2){ animation-delay:.07s; }
body.dia0-tour .page.visible.tabroot-page > *:nth-child(3),
body.dia0-tour .page.visible.toc-page > *:nth-child(3){ animation-delay:.14s; }
body.dia0-tour .page.visible.tabroot-page > *:nth-child(n+4),
body.dia0-tour .page.visible.toc-page > *:nth-child(n+4){ animation-delay:.2s; }
@media (prefers-reduced-motion:reduce){
  body.dia0-tour .page.page-leaving, body.dia0-tour .page.visible,
  body.dia0-tour .page.visible.tabroot-page > *, body.dia0-tour .page.visible.toc-page > *{ animation:none; } }

/* 🔴 [05/08 · 3ª rodada · Luis] NO TOUR, O CURSO NASCE TODO ABERTO.
   "a página vai nascer com todos os dropdowns abertos, e o scroll vai descer
    lentamente mostrando todas as aulas" — em vez de abrir/fechar durante a rolagem,
   que era o que fazia a tela pular pra cima e pra baixo.
   ⚠️ É CSS, não estado: a lista é "1 módulo aberto por vez" e o que o ALUNO deixou
   aberto continua sendo dele. A classe sai no fim do tour e a lista volta ao que era.
   ⚠️ `grid-template-rows` porque é assim que o colapso do curso é feito (30/07) —
   mexer no `height` aqui brigaria com a transição dele. */
body.dia0-tudo-aberto .sc-module .sc-module-body{ grid-template-rows:1fr !important; }
body.dia0-tudo-aberto .sc-module .sc-module-body > div{ overflow:visible !important; }
body.dia0-tudo-aberto .sc-module .sc-module-head svg{ transform:rotate(180deg); }

/* ===== [05/08 · Luis] DURANTE A LOCUÇÃO, A TELA NÃO ACEITA TOQUE =====
   "quando a locução estiver explicando elementos da tela e a coreografia ocorrendo,
    trave o clique em qualquer botão, a fim de evitar interrupções da coreografia."
   Já existia o escudo de captura do walkthrough (_ttWalkShield), mas ele decide por
   uma LISTA DE EXCEÇÕES — e uma delas é o teclado tocável, que fica vivo justamente
   nos beats em que a fala roda sobre a prática (o fecho da escala). Esta regra é a
   garantia declarativa e sem exceção: enquanto o tour narra, nada abaixo aceita
   ponteiro. Sai sozinha quando `tt-narrating` sai, no fim de cada clipe.
   ⚠️ `pointer-events:none` NÃO bloqueia rolagem de toque no iOS — o passeio da
   Técnica e a leitura continuam funcionando; o que morre é só o clique.
   ⚠️ O pop-up de Continuar e a legenda ficam de fora por construção: eles vivem
   FORA do #app, e o pop-up só aparece quando não há fala. */
body.dia0-tour.tt-narrating #app,
body.dia0-tour.tt-narrating .tabbar,
body.dia0-tour.tt-narrating .menu-btn{ pointer-events:none !important; }
/* ⚠️ EXCEÇÃO ÚNICA: o alvo que o tour ACABOU DE CONVIDAR a tocar. A trava protege a
   coreografia; o toque convidado É a coreografia. Sem esta linha o card da Escala
   Blues cintilava pedindo clique e o clique não fazia nada (relatado 05/08). */
body.dia0-tour.tt-narrating.dia0-espera-toque #app{ pointer-events:none !important; }
body.dia0-tour.dia0-espera-toque .wt-target-active,
body.dia0-tour.dia0-espera-toque .wt-target-active *{ pointer-events:auto !important; }

/* ===== [05/08 · Luis] POP-UP "CONTINUAR" do fim da demo de partitura =====
   Veste as MESMAS classes do pop-up do curso (.catalog-confirm-modal.sc-fim-modal):
   "pop up igual do curso de partitura". Só precisa do z-index acima do escudo do
   walkthrough — senão ele seria pintado por baixo do véu de destaque. */
/* 🔴 [05/08 · Luis] "REMOVA OS CADEADOS que aparecem na tour — não mostre a tour
   como se fosse um teste grátis."
   O tour é VITRINE: ele apresenta o que o produto É. Cadeado ali conta a história
   errada (o que você NÃO tem) no exato momento em que a gente está mostrando o que
   ele faz — e a venda vem depois, na tela de decisão, que é onde essa conversa mora.
   ⚠️ Só o VISUAL cede, e só sob `body.dia0-tour`: nenhuma trava de acesso é tocada
   (o showPage segue com o gate de sempre) e fora do tour a tela é idêntica. */
body.dia0-tour .toc-phase-lock,
body.dia0-tour .gm-lock,
body.dia0-tour .t-opt-lock,
body.dia0-tour .sc-lesson-lock,
body.dia0-tour .sc-module-lock{ display:none !important; }
body.dia0-tour .toc-phase.is-locked,
body.dia0-tour .gm-card.is-locked,
body.dia0-tour .hm-chip.is-blocked{ opacity:1 !important; filter:none !important; }
body.dia0-tour .toc-phase-free{ display:none !important; }   /* o selo "GRÁTIS" é a outra metade da mesma história */

/* 🔴 [05/08 · Luis] A ISCA DO CARD É A LÂMINA — não o contorno.
   `wt-target-active` é a classe que o ESCUDO usa pra saber o que pode ser clicado,
   então ela tem de ficar. Mas ela também carrega o desenho do walkthrough antigo
   (anel laranja + pulso), que é justamente o que ele mandou tirar do tour inteiro.
   Aqui a classe segue valendo como PERMISSÃO e perde o visual: quem chama o olho é
   a lâmina passando, que é o que ele pediu ("aquele que combinamos"). */
body.dia0-tour .wt-target-active:not(.dia0-pop-btn){
  outline:none !important; box-shadow:none !important; animation:none !important; }
body.dia0-tour .tt-beckon{ animation:none !important; }

/* 🔴 [05/08 · Luis] A ISCA VOLTA A TER O CONTORNO.
   "a escala blues mudou seu comportamento isca para apenas a lâmina de brilho que
    passa, mas deve ter também aquele contorno laranja cintilante."
   A regra logo acima continua valendo pra DESTAQUE — apontar um elemento enquanto a
   locução o explica é trabalho da lâmina, e foi isso que ele mandou padronizar.
   O que volta aqui é só a ISCA: o instante em que o tour PARA de falar e PEDE o
   toque, marcado por `dia0-espera-toque`. São dois gestos diferentes e agora têm
   desenhos diferentes — explicação passa a lâmina, convite acende o contorno (e a
   lâmina segue por baixo, as duas juntas). */
body.dia0-tour.dia0-espera-toque .wt-target-active:not(.dia0-pop-btn){
  outline:3px solid var(--primary) !important;
  outline-offset:6px;
  border-radius:14px;
  animation:wtPulse 1.6s ease-in-out infinite !important;
  z-index:430; }

/* 🔴 [05/08 · Luis · item 01] "o botão continuar pós escala blues CONTINUA demorando
   a ficar clicável — ele deve ser clicável desde o primeiro momento em que aparece."
   A causa não estava no JS (onde eu já tinha mexido duas vezes), estava AQUI. O botão
   do pop-up veste `wt-target-active` — é assim que o escudo do walkthrough sabe que
   ele pode ser tocado. E a regra v485 `body.tt-narrating .wt-target-active{
   pointer-events:none}` deixa TODO alvo inerte enquanto um clipe toca, de propósito:
   tocar num alvo não pode cortar a narração.
   Isso era inofensivo enquanto o pop só nascia no silêncio — e o comentário lá em
   cima ainda afirma isso. Deixou de ser verdade quando o fecho da escala passou a
   tocar SEM ser esperado, pra o pop aparecer junto da última nota: desde então o
   botão nascia MORTO pela duração inteira do clipe. É exatamente o atraso relatado.
   O pop-up é a única coisa que essa trava jamais deveria pegar — ele É a saída da
   pausa. Sai por ID, que vence a v485 na especificidade. */
#dia0-pop, #dia0-pop .dia0-pop-btn{ pointer-events:auto !important; }

/* [06/08 · L13] o atalho pra oferta MUDOU DE CASA: saiu da tela de Minhas Músicas
   (o .dia0-pular morreu) e virou o botão do POP a partir do 3/5 — ver
   .dia0-pop-pular junto das regras do #dia0-pop. */

/* [4ª rodada · Luis] o pop-up virou SÓ O BOTÃO: mesmo halo laranja, sem cartão e
   sem check verde. O véu fica — é ele que impede um toque perdido de furar a pausa. */
#dia0-pop{ z-index:2147483300; display:none; align-items:center; justify-content:center; }
#dia0-pop.is-open, #dia0-pop.closing{ display:flex; }
/* 🔴 [06/08 · L9 item 01] "não deixe o clique atrapalhar a coreografia" — enquanto
   o tour ROLA (passeio da Técnica/curso, volta pra Blues, varredura da harmonia),
   nenhum toque entra. A classe é posta/tirada pelo scrollTrava() do módulo, que
   nem liga a trava quando o tour está PEDINDO um toque (dia0-espera-toque). */
body.dia0-tour.dia0-rolando #app,
body.dia0-tour.dia0-rolando .tabbar,
body.dia0-tour.dia0-rolando .menu-btn{ pointer-events:none !important; }
/* 🔴 [06/08 · L12 · Luis] "o scroll automático da técnica está meio travando,
   aumente o fps." O menu são 62 cards de VIDRO (backdrop-filter blur 9px) — num
   scroll contínuo cada frame re-rasteriza o blur de todos os cards visíveis, e é
   isso que derruba o fps no aparelho (mesma família do stale-paint de iOS já
   documentado). Durante a rolagem AUTOMÁTICA o vidro desliga e entra um fundo
   sólido equivalente; ao parar, o vidro volta sozinho com a classe. Em movimento
   a troca é imperceptível — parado, ninguém a vê. */
body.dia0-tour.dia0-rolando .toc-phase{
  -webkit-backdrop-filter:none !important; backdrop-filter:none !important;
  background:rgba(18,18,18,.72);
}

/* 🔴 [06/08 · Luis, item 07] o Continuar virou uma NAV DE SETA: grande, redonda,
   laranja, com comportamento de ISCA — o mesmo contorno cintilante (wtPulse) do
   convite da escala. A palavra vive só no aria-label/leitor de tela. */
#dia0-pop .dia0-pop-btn{ position:relative; z-index:1; flex:0 0 auto;
  width:clamp(84px,22vw,110px); height:clamp(84px,22vw,110px);
  min-width:0; padding:0; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:var(--primary); color:#160b02; border:0;
  box-shadow:0 0 28px rgba(232,105,10,.5);
  outline:3px solid var(--primary); outline-offset:6px;
  animation:wtPulse 1.6s ease-in-out infinite; }
#dia0-pop .dia0-pop-btn svg{ width:46%; height:46%; display:block; }
#dia0-pop .dia0-pop-btn .sc-fim-op-t{ position:absolute; width:1px; height:1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }
#dia0-pop .dia0-pop-btn:active{ transform:scale(.94); }
/* [06/08 · L11] seta + fração empilhadas; o véu continua atrás */
#dia0-pop .dia0-pop-core{ position:relative; z-index:1;
  display:flex; flex-direction:column; align-items:center; gap:18px; }
/* a FRAÇÃO do progresso (1/5 … 5/5): desenhada como fração DIAGONAL — numerador
   grande e alto em branco, barra laranja inclinada (a cor de ação do app),
   denominador menor e baixo, apagado. Tipografia da casa (font-h 900). */
#dia0-pop .dia0-pop-frac{ display:flex; align-items:center; gap:1px;
  font-family:var(--font-h); line-height:1; user-select:none;
  filter:drop-shadow(0 2px 12px rgba(0,0,0,.55)); }
#dia0-pop .dia0-pop-frac b{ font-weight:900; font-size:30px; letter-spacing:-1px;
  color:var(--text); transform:translateY(-5px); }
#dia0-pop .dia0-pop-frac i{ font-style:normal; font-weight:900; font-size:26px;
  color:var(--primary); transform:rotate(14deg); margin:0 2px;
  text-shadow:0 0 14px rgba(232,105,10,.55); }
#dia0-pop .dia0-pop-frac span{ font-weight:800; font-size:17px;
  color:var(--muted); transform:translateY(4px); }
/* [06/08 · L13] o atalho pra oferta, discreto sob a fração — pílula fantasma
   laranja: presente pra quem procura, sem competir com a seta */
#dia0-pop .dia0-pop-pular{ cursor:pointer; pointer-events:auto;
  background:rgba(232,105,10,.10); border:1px solid rgba(232,105,10,.55);
  color:var(--primary); border-radius:999px; padding:11px 24px;
  font-family:var(--font-h); font-weight:800; font-size:12.5px;
  letter-spacing:.03em; line-height:1; margin-top:2px;
  transition:background .18s ease, transform .12s ease; }
#dia0-pop .dia0-pop-pular:active{ transform:scale(.95); }

/* ===== [DIA0-DEC · 04/08] TELA DE DECISÃO DO DIA 0 (paywall silencioso) =====
   Mínima de propósito: o tour já fez o trabalho que headline faz em paywall comum. A tela é
   contrato, não argumento — headline, a fita da prova, um card, um botão. Nada de timer falso,
   nada de texto a mais. Ela se MONTA durante o pitch (ícone por ícone) e o card assenta quando
   a voz cala; a leitura do preço acontece em silêncio. */
#dia0-dec{background:transparent}   /* fundo estelar (#c) cheio, igual à decisão antiga — o card tem backdrop próprio */
.dd{align-items:center;justify-content:flex-start;overflow-y:auto;-webkit-overflow-scrolling:touch;
  padding:30px 20px calc(env(safe-area-inset-bottom) + 24px)}
.dd-inner{width:100%;max-width:440px;margin:auto 0;display:flex;flex-direction:column;
  align-items:center;gap:clamp(18px,4.2vh,30px);text-align:center}
/* 🔴 [05/08 · 2ª rodada] O TÍTULO NASCE VAZIO e é escrito pela coreografia do
   "um ano". A ALTURA É RESERVADA desde o início (min-height): sem isso o .dd-inner
   colapsaria a linha vazia e a fita + o card subiriam — pra descerem de novo quando
   o título aparecesse. Nada pode saltar na tela do dinheiro.
   "título um pouco mais em cima do que é hoje": deslocamento por `top` relativo, que
   move só a PINTURA — as bolinhas ficam onde estão, que é o pedido seguinte dele. */
.dd-head{font-family:var(--font-h);font-weight:900;font-size:clamp(26px,7.2vw,38px);line-height:1.04;
  letter-spacing:-1px;color:var(--text);margin:0;
  position:relative;top:calc(-1 * clamp(10px,2.4vh,22px));text-wrap:balance}
/* ⚠️ As 3 partes são INLINE, não itens de flex: como flex, cada uma virava uma
   linha própria e o título saía quebrado em 4 ("Um ano / de evolução / começando /
   hoje"). Inline, ele quebra onde a frase quebra.
   ⚠️ E NÃO há min-height reservado: as partes b e c já ocupam layout desde o início
   (nascem só com opacity:0), então a altura final existe antes de o título aparecer
   e nada salta quando ele se forma. */
.dd-head .dd-h-c{color:var(--primary)}
/* as 3 partes entram em tempos diferentes: "Um ano" chega VOANDO do karaokê (por isso
   nasce invisível e é revelada no fim do voo), o resto se forma atrás dela. */
.dd-head .dd-h-a{opacity:0;transition:opacity .28s ease}
.dd-head.dd-h-1 .dd-h-a{opacity:1}
.dd-head .dd-h-b,.dd-head .dd-h-c{opacity:0;transform:translateY(10px);filter:blur(6px);
  transition:opacity .5s ease,transform .6s cubic-bezier(.2,.9,.3,1),filter .5s ease}
.dd-head.dd-h-2 .dd-h-b{opacity:1;transform:none;filter:none}
.dd-head.dd-h-2 .dd-h-c{opacity:1;transform:none;filter:none;transition-delay:.16s}

/* ===== O KARAOKÊ DO PITCH =====
   🔴 [06/08 · L9 item 07] "sai em blocos centralizados; deve sair numa CAIXA
   RETANGULAR, com o texto todo retinho — e não pode sobrepor nenhum elemento que
   vai entrar na tela, hora nenhuma."
   A legenda deixou de ocupar a tela inteira: virou uma caixa na METADE DE BAIXO,
   com o texto corrido e alinhado à esquerda, rolando sozinha pra última linha.
   O título e a fita entram na metade DE CIMA (o .dd-inner centrado os põe lá,
   porque o card — invisível até o assenta — reserva o espaço inferior), então as
   duas zonas nunca se cruzam; e o card só entra DEPOIS de a caixa sumir (o
   assenta agora sequencia). */
/* [06/08 · L10 item 03] "a disposição ficou boa, mas não precisava do box" —
   sai o retângulo (fundo/borda/vidro), fica o TEXTO exatamente onde está, com o
   fundo estrelado atravessando. A geometria não muda um pixel. */
.dd-kara{position:fixed;left:clamp(16px,5vw,44px);right:clamp(16px,5vw,44px);
  top:53%;bottom:calc(env(safe-area-inset-bottom) + 18px);z-index:6;
  display:block;overflow:hidden;scroll-behavior:smooth;
  padding:15px 17px;
  text-align:left;pointer-events:none;opacity:1;transition:opacity .42s ease}
.dd-kara.dd-kara-off{opacity:0}
.dd-kara .dd-k-l{font-family:var(--font-h);font-weight:800;letter-spacing:-.2px;
  font-size:clamp(15.5px,4.3vw,20px);line-height:1.42;color:var(--text);
  transition:opacity .45s ease}
/* 🔴 [3ª rodada] KARAOKÊ DE VERDADE: a unidade que acende é a PALAVRA, não a linha
   (ele viu "aparecendo em blocos"). Cada palavra entra sozinha, e as PALAVRAS-CHAVE
   nascem laranja — é a mesma lista de nomes que a fita de recap usa. */
.dd-kara .dd-k-w{display:inline-block;opacity:0;transform:translateY(9px);filter:blur(5px);
  transition:opacity .28s ease,transform .34s cubic-bezier(.2,.9,.3,1),filter .28s ease}
.dd-kara .dd-k-w.on{opacity:1;transform:none;filter:none}
.dd-kara .dd-k-w.chave{color:var(--primary);font-weight:900}
/* 🔴 [05/08 · 4ª rodada · Luis] "não divida o texto em blocos, escurecendo o resto
   e mudando formatação. O karaokê segue TODOS OS PARÁGRAFOS DO MESMO JEITO (sem
   divisão visual entre parágrafos), palavras-chave em laranja."
   O `.past` (que escurecia o já dito) saiu, e as linhas deixaram de ser blocos: o
   `display:inline` faz o texto correr como um parágrafo só, e o que distingue o
   dito do não-dito é só a palavra ter acendido — nada de dois níveis de cinza. */
.dd-kara .dd-k-l{display:inline}
.dd-kara .dd-k-l.past{opacity:1}
/* o congelamento: tudo some MENOS o "um ano", que é quem vai subir */
.dd-kara.dd-kara-freeze .dd-k-l{opacity:0;filter:blur(8px);transition:opacity .38s ease,filter .38s ease}
.dd-kara.dd-kara-freeze .dd-k-l.tem-ano{opacity:1;filter:none}
.dd-kara.dd-kara-freeze .dd-k-l.tem-ano .dd-k-w:not(.dd-k-ano){opacity:0;transition:opacity .38s ease}
.dd-k-ano{display:inline-block;color:var(--primary);will-change:transform;
  transition:transform .82s cubic-bezier(.22,.85,.24,1), color .6s ease}
/* durante o VOO a palavra vira branca: ela pousa exatamente na cor em que o título
   a espera. Sem isto havia um estalo de cor no instante do pouso (visto no print). */
.dd-kara.dd-kara-freeze .dd-k-ano{color:var(--text)}

/* --- a fita: a memória da prova. Cada item nasce APAGADO e acende no nome falado. --- */
.dd-recap{display:flex;justify-content:center;align-items:flex-start;flex-wrap:nowrap;
  gap:clamp(7px,3.1vw,19px);width:100%}
/* 🔴 [Luis] "aumente o tamanho das 5 bolinhas apenas um pouco, e faça elas
   aparecerem DE UMA VEZ, sem estarem apagadas antes."
   Elas nasciam cinzas e acendiam uma a uma no nome falado — era a coreografia
   anterior. Agora a fita inteira entra junto (uma só transição, no .dd-recap) e
   nenhum ícone fica apagado esperando a vez. O `acende()` da API continua existindo
   e virou inofensivo, pra o pitch não precisar saber que a regra mudou. */
.dd-recap{opacity:0;transform:translateY(14px) scale(.97);
  transition:opacity .7s ease .12s,transform .8s cubic-bezier(.2,.9,.3,1) .12s}
.dd-recap.dd-in{opacity:1;transform:none}
/* o título também entra devagar: as 3 partes eram .5s com blur e liam como corte */
.dd-head .dd-h-a{transition:opacity .5s ease}
/* [4ª rodada · Luis] "essas bolinhas precisam ser um pouquinho maiores e um
   pouquinho mais afastadas (pouca coisa)" — e voltam a ENTRAR uma a uma, quando
   citadas: não existem antes de serem ditas (não é o cinza esperando de antes). */
.dd-ic{display:flex;flex-direction:column;align-items:center;gap:6px;min-width:0;flex:0 1 auto;
  opacity:0;transform:translateY(10px) scale(.9);
  transition:opacity .42s ease,transform .5s cubic-bezier(.2,.9,.3,1)}
.dd-ic.on{opacity:1;transform:none}
.dd-ic i{width:clamp(44px,12vw,55px);height:clamp(44px,12vw,55px);border-radius:50%;
  display:flex;align-items:center;justify-content:center;color:var(--primary);
  border:1px solid rgba(232,105,10,.35);background:rgba(232,105,10,.08)}
.dd-ic i{box-shadow:0 0 0 3px rgba(232,105,10,.16)}
.dd-ic i svg{width:58%;height:58%;display:block}
.dd-ic em{font-style:normal;font-family:var(--font-b);font-size:clamp(9.5px,2.7vw,11.5px);
  font-weight:700;color:var(--muted);letter-spacing:.01em;line-height:1.15;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* --- o card: entra por último, quando a locução termina --- */
/* [Luis] "card exatamente onde está, porém com um contorno mais bonito laranja neon" */
.dd-card{width:100%;padding:20px 18px;border-radius:18px;border:1.5px solid rgba(255,140,66,.85);
  background:rgba(18,18,20,.62);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  box-shadow:0 0 0 1px rgba(232,105,10,.35), 0 0 22px rgba(232,105,10,.42),
             0 0 60px rgba(232,105,10,.18), 0 18px 48px rgba(0,0,0,.55);
  display:flex;flex-direction:column;gap:12px;
  opacity:0;transform:translateY(18px);transition:opacity .55s ease,transform .6s cubic-bezier(.2,.9,.3,1)}
.dd-card.dd-in{opacity:1;transform:none}
.dd-price{display:flex;flex-direction:column;align-items:center;gap:2px}
.dd-val{font-family:var(--font-h);font-weight:900;font-size:clamp(28px,8vw,40px);letter-spacing:-.6px;color:var(--text)}
/* a âncora diária: o cérebro compara o número contra a VIDA, não contra outro plano */
.dd-anc{font-family:var(--font-b);font-size:clamp(12.5px,3.4vw,14px);color:var(--muted)}
.dd-load{color:var(--muted)}
.dd-cta{width:100%;padding:16px 18px;border:0;border-radius:14px;cursor:pointer;
  font-family:var(--font-h);font-weight:900;font-size:clamp(16px,4.4vw,19px);letter-spacing:-.2px;
  color:#160b02;background:var(--primary);transition:transform .14s ease,background .18s ease}
.dd-cta:hover{background:var(--primary-2,#FF8C42)}
.dd-cta:active{transform:scale(.985)}
.dd-micro{margin:0;font-family:var(--font-b);font-size:clamp(11px,3vw,12.5px);line-height:1.4;color:var(--muted)}

/* --- porta de quem já pagou: entra depois do card, discreta --- */
.dd-sec{opacity:0;transform:translateY(10px);transition:opacity .5s ease,transform .5s ease;pointer-events:none}
.dd-sec.dd-in{opacity:1;transform:none;pointer-events:auto}
/* [Luis] "o botão de já comprei vai um pouco mais pra baixo e se transforma em pílula" */
.dd-sec{margin-top:clamp(6px,2vh,18px)}
.dd-sec-btn{cursor:pointer;font-family:var(--font-h);font-weight:700;font-size:13px;
  color:var(--muted);text-decoration:none;padding:11px 24px;border-radius:999px;
  border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.04);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  transition:color .2s,border-color .2s,background .2s}
.dd-sec-btn:hover{color:var(--text);border-color:rgba(255,255,255,.3);background:rgba(255,255,255,.08)}
.dd-sec-btn:active{transform:scale(.97)}
/* [06/08 · L9 item 08 → L10 item 04] a MARCA, bem grande, fecha a tela — abaixo
   do "já comprei". L10: "pode deixar BEM maior e mais pra baixo". */
.dd-sec{display:flex;flex-direction:column;align-items:center}
.dd-brand{margin-top:clamp(34px,7vh,72px);font-family:var(--font-h);font-weight:900;
  font-size:clamp(36px,11.5vw,56px);letter-spacing:-1.6px;line-height:1;
  color:var(--text);opacity:.96;text-align:center}
/* a micro some por DADO quando o plano não a pede (ver o comentário no markup) */
.dd-micro[hidden]{display:none}

/* 🔴 [05/08 · 5ª rodada] A DECISÃO DEITADA — e por que ela passou a existir.
   O tour custava 4 viradas de tela. Medido: NENHUMA reordenação dos blocos baixa
   disso, porque Técnica e Partitura têm, cada uma, um retrato→paisagem DENTRO de si
   e terminam deitadas (ver PLANO-GIROS-DIA0.md). A única virada que dá pra apagar
   sem estragar tela nenhuma é a ÚLTIMA — a que existe só para pôr a venda em pé.
   Em paisagem o layout de coluna dava 85px de rolagem e empurrava a FITA DE RECAP
   (a prova que ancora o preço) para fora da dobra. Em duas colunas ele cabe: a
   promessa e a prova de um lado, o preço do outro.
   ⚠️ `orientation:landscape` e não `min-width`: o que muda o desenho aqui é a forma
   da tela, não o tamanho — um tablet em pé tem largura de sobra e deve seguir em
   coluna. */
@media (orientation:landscape) and (max-height:560px){
  .dd{ padding:16px 24px calc(env(safe-area-inset-bottom) + 14px); }
  .dd-inner{ max-width:min(920px,96vw); display:grid; grid-template-columns:1fr 1fr;
    align-items:center; column-gap:clamp(20px,4vw,44px); row-gap:10px; }
  .dd-head{ grid-column:1; grid-row:1; top:0; margin:0; text-align:left; }
  .dd-recap{ grid-column:1; grid-row:2; justify-content:flex-start; }
  .dd-card{ grid-column:2; grid-row:1 / span 2; }
  .dd-sec{ grid-column:1 / -1; grid-row:3; margin-top:2px; }
  .dd-ic i{ width:clamp(36px,7.4vh,44px); height:clamp(36px,7.4vh,44px); }
  .dd-head{ font-size:clamp(22px,5.4vh,34px); }
  .dd-kara .dd-k-l{ font-size:clamp(18px,4.4vh,26px); }
}

@media (prefers-reduced-motion:reduce){
  .dd-ic,.dd-card,.dd-sec,.dd-recap,.dd-kara,.dd-kara .dd-k-l,.dd-k-ano,
  .dd-head .dd-h-a,.dd-head .dd-h-b,.dd-head .dd-h-c{transition-duration:.01ms}
}
/* telas curtas (iPhone SE deitado no limite / retrato baixo): a fita encolhe antes do card */
@media (max-height:640px){
  .dd-inner{gap:16px}
  .dd-ic i{width:32px;height:32px}
}

/* ===== [E6 · 03/08 · Luis] TRANSPOR: o card de tonalidade da FOLHA =====
   O card dos exercícios (.pb-tom) é um quadradinho numa barra de PAISAGEM.
   Aqui a tela é retrato e larga: o card vira uma LINHA — rótulo à esquerda,
   chip laranja (cifra grande + leitura) à direita, seta indicando que abre.
   A identidade é a mesma (laranja, font-h 900, cifra dominando a leitura);
   o que muda é a geometria, que é o que a tela nova pedia. */
.mp-tom-row{ width:100%; display:flex; align-items:center; gap:12px;
  margin:0 0 14px; padding:10px 12px 10px 14px; border-radius:14px;
  background:rgba(232,105,10,.07); border:1.5px solid rgba(232,105,10,.30);
  font:inherit; cursor:pointer; -webkit-appearance:none; appearance:none;
  -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  transition:background .15s, border-color .15s, transform .12s; }
.mp-tom-row[hidden]{ display:none; }
.mp-tom-row:active{ transform:scale(.99); background:rgba(232,105,10,.12); }
.mp-tom-row.is-open{ border-color:rgba(232,105,10,.70); background:rgba(232,105,10,.14); }
.mp-tom-lbl{ flex:1 1 auto; text-align:left; min-width:0;
  font-family:var(--font-h); font-weight:800; font-size:14px; color:var(--muted);
  letter-spacing:.02em; }
.mp-tom-chip{ flex:0 0 auto; display:flex; flex-direction:column; align-items:center; gap:1px;
  padding:4px 12px; border-radius:10px; background:rgba(232,105,10,.14);
  border:1px solid rgba(232,105,10,.35); }
.mp-tom-chip b{ font-family:var(--font-h); font-weight:900; font-size:26px; line-height:1;
  letter-spacing:-1px; color:var(--primary); }
.mp-tom-chip em{ font-style:normal; font-family:var(--font-h); font-weight:700; font-size:10.5px;
  line-height:1; color:rgba(246,241,231,.72); letter-spacing:.02em; }
.mp-tom-arrow{ flex:0 0 auto; font-size:22px; line-height:1; color:var(--primary);
  transition:transform .18s cubic-bezier(.2,.8,.2,1); }
.mp-tom-row.is-open .mp-tom-arrow{ transform:rotate(90deg); }
/* opção impossível: o motivo já vem escrito no <em>, aqui só o estado morto */
#mp-tom-menu .t-opt:disabled{ cursor:default; }
#mp-tom-menu .t-opt:disabled:hover{ background:transparent; }

/* ===== [DIA0 v2] brilho cintilante dos cards de jogo =====
   O Luis pediu "idêntico ao que ocorre nas tags iniciante/intermediário/avançado
   dentro da apresentação do menu jornada completa". Aquilo é o tierShine da
   experiencia.html (#tiertags .toc-tier-tag.shine, linhas 322-331) — copiado
   aqui BYTE A BYTE (gradiente de 5 paradas, translate -200%, skew -18deg,
   opacidade entrando e saindo), só trocando o seletor. Se um lado mudar, o
   outro tem de mudar junto: "idêntico" foi requisito, não estilo. */
.dia0-shine{ position:relative; overflow:hidden }
.dia0-shine::after{ content:""; position:absolute; top:0; bottom:0; left:0; width:55%;
  background:linear-gradient(100deg,transparent 0%,rgba(255,255,255,0) 25%,rgba(255,255,255,.6) 50%,rgba(255,255,255,0) 75%,transparent 100%);
  transform:translateX(-200%) skewX(-18deg); opacity:0; pointer-events:none;
  animation:dia0Shine .72s cubic-bezier(.3,0,.3,1); }
@keyframes dia0Shine{
  0%{ transform:translateX(-200%) skewX(-18deg); opacity:0 }
  12%{ opacity:1 }
  88%{ opacity:1 }
  100%{ transform:translateX(240%) skewX(-18deg); opacity:0 } }
@media (prefers-reduced-motion:reduce){ .dia0-shine::after{ animation:none } }

/* ============================================================================
 * [07/08 · FITA ROLANTE · REDESIGN 17/08] modo fita do score-play (prefixo fr-
 * — namespace próprio, conferido livre; sc- é da pauta, sv- do song-view).
 *
 * 🔴 A LEI DE ESCOPO DESTE BLOCO (17/08): TUDO que muda aparência mora sob
 * `.score-play-page[data-fr-on="1"]`. A PAUTA (o papel creme, a moldura
 * laranja, o teclado branco, os chips de ler partitura) fica BYTE A BYTE como
 * está — a fita e a pauta dividem a mesma <section>, e o único jeito de
 * redesenhar uma sem mexer na outra é o atributo que já separa as duas.
 *
 * O QUE MUDOU E POR QUÊ (pedido do Luis: "o design da fita difere
 * drasticamente do resto do app"):
 *  · A fita nascia com a pele da PAUTA (papel #F6F1E7 + moldura laranja de
 *    12px). Faz sentido para ler partitura — é papel. Não faz nenhum para uma
 *    fita rolante, que é a tela de PRÁTICA do app: constelação, preto,
 *    laranja da marca, Montserrat. Agora a fita usa o vocabulário da
 *    .practice-bar (a tela horizontal da Técnica), que é a linguagem de casa.
 *  · FUNDO ESTRELADO CONGELADO (pedido literal): a fita fica TRANSPARENTE e
 *    quem aparece é o #c global — o mesmo do app inteiro. Congelar é do JS
 *    (Constellation.congela), não daqui: o custo some no loop, não no pixel.
 *    Sobre as estrelas vai só um véu escuro em gradiente, que é o que dá
 *    contraste pros blocos sem apagar a constelação.
 *  · PÍLULAS = .pb-stage (Montserrat 800 · uppercase · letter-spacing · ativa
 *    laranja com glow). Agrupadas em TRILHOS segmentados: além de ser o
 *    desenho certo, o trilho economiza 2 bordas e 2 vãos por grupo — é o que
 *    devolve a folga que fazia "Calibrar" cair fora do quadro em 667px.
 *
 * CORES DAS MÃOS (decisão 07/08, Etapa 0 — INTOCADA): direita = roxo
 * --sc-roxo #7C3AED (continuidade com o cursor/tempo da seção), esquerda =
 * laranja --primary #E8690A (brand). Verde ficou como semântica de acerto;
 * vermelho é PROIBIDO neste modo (lei anti-punição do v333 — o renderizador
 * nem tem esse estado). O redesign muda o ACABAMENTO do bloco (degradê,
 * brilho, halo), NUNCA o matiz — é a cor que a tecla acesa e a legenda usam.
 * ========================================================================= */
.score-play-page{ --fr-cor-r:#7C3AED; --fr-cor-l:#E8690A; }

/* toggle na barra do score-play — "voltar pra pauta" quando a fita está viva.
 * Base (fita desligada) segue o vocabulário do .sc-pill, que é o da pauta. */
.fr-toggle{ display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:800;
  letter-spacing:.04em; padding:5px 12px; border-radius:999px; border:1.5px solid rgba(0,0,0,.28);
  background:rgba(0,0,0,.05); color:var(--sc-ink); cursor:pointer; white-space:nowrap;
  font-family:inherit; -webkit-tap-highlight-color:transparent; touch-action:manipulation; }
.fr-toggle[data-fr-vivo="1"]{ background:var(--fr-cor-r); border-color:transparent; color:#fff; }
/* ══ [19/09] O ROTULO DO MODO CRESCEU — e a barra tinha de pagar ═══════════
   *"substitua toda nomeclatura fita por notas caindo"* (Luis). O `fita.modo`
   deste botao passou de "Fita" a "Notas caindo": 50 px viraram 110 num
   `.sc-top` que e `display:flex` sem `wrap`. MEDIDO antes de escrever a regra:
   sem guarda nenhuma, os 60 px a mais saiam do ← (`.nav-tag.is-round`), que
   encolhia de 36 para 16 px — abaixo de qualquer piso de dedo, no alvo mais
   usado da tela — e a 320 px a barra ainda transbordava 2 px.
   🔑 Quem NAO pode pagar e o ←: ele e o unico caminho de volta. Quem paga e o
   crumb, pela razao que esta casa ja escreveu no bloco do `[data-fr-on="1"]` —
   ele repete a licao que o aluno acabou de escolher, e os controlos ao lado nao
   tem substituto. No tablet (800 px) ha espaco e ele FICA: a regra e so do
   telemovel. Medido depois: 0 px de transbordo em 320/360/390/430/800, e o ←
   inteiro em todas (36 no telemovel pela guarda abaixo; no tablet ele ja tinha
   os 48 do `html.tb` sem guarda nenhuma).

   🔴🔑 E A GUARDA DO ← VIVE DENTRO DO `@media`, nao fora — porque a `.fr-bar`
   da pratica e IRMA dele dentro da mesma `.sc-top` (index.html), e nao um bloco
   a parte como eu tinha suposto. Com a regra solta, o que o ← deixava de
   encolher saia da barra de chips: o `validate_fr_barra` mediu o transbordo
   dela a crescer 4–11 px em 667/560/1180 (as caixas onde ela JA rolava), sem
   nenhuma prova mudar de cor. Medido a seguir: acima de 480 px o ← nao encolhe
   nem com o rotulo grande (38 px a 481/560/667, 44 a 844), ou seja a guarda la
   nao servia para nada e so cobrava a barra. Fechada nos ≤480, o `fr_barra`
   volta NUMERO A NUMERO ao que era. */
@media (max-width:480px){
  .sc-top > .nav-tag.is-round{ flex:0 0 auto; }
  /* o `:has()` degrada bem: onde nao existir, o crumb volta a encolher ate 0 —
     que e o que ele ja fazia hoje, so que sem o ← encolher junto. */
  .score-play-page .sc-top:has(> .fr-toggle:not([hidden])) .sc-crumb{ display:none; }
  .score-play-page .sc-top{ gap:7px; }
  .score-play-page .sc-top > .fr-toggle{ font-size:11px; padding:5px 9px; letter-spacing:.01em; }
}
/* sem MIDI: o modo aparece DESABILITADO (aria-disabled; o clique mostra a
 * mensagem curta — nunca some, o aluno precisa saber que o modo existe) */
.fr-toggle[aria-disabled="true"]{ opacity:.42; }
.fr-avisomidi{ position:absolute; left:50%; top:52px; transform:translateX(-50%); z-index:30;
  background:#141414; color:#fff; font-size:12.5px; font-weight:600; line-height:1.35;
  padding:9px 14px; border-radius:12px; max-width:min(420px,86vw); text-align:center;
  box-shadow:0 6px 22px rgba(0,0,0,.35); opacity:0; pointer-events:none; transition:opacity .22s; }
.fr-avisomidi.on{ opacity:1; }

/* o modo fita SUBSTITUI o papel da pauta (mesmo slot flex) */
.score-play-page[data-fr-on="1"] .sc-paper{ display:none; }
.score-play-page[data-fr-on="1"] .sc-bpm,
.score-play-page[data-fr-on="1"] .sc-pill,
.score-play-page[data-fr-on="1"] [data-sc-score]{ display:none; }
.fr-area{ display:none; }
.score-play-page[data-fr-on="1"] .fr-area{ display:flex; flex-direction:column; flex:1; min-height:0;
  position:relative; background:transparent; }

/* ---------------------------------------------------------------------------
 * O PALCO — a fita passa a viver na CONSTELAÇÃO do app
 * ------------------------------------------------------------------------ */
/* 🔑 transparente de propósito: quem pinta o fundo é o #c global (canvas
 * fixed, z-index:-1), o MESMO das outras telas. Pintar fundo próprio aqui
 * apagaria as estrelas — é a mesma regra já registrada na sc-hub-page.
 * O congelamento do #c é feito no JS (Constellation.congela) ao ligar a fita:
 * o loop para, o último frame fica, e o custo por frame vai a zero. */
.score-play-page[data-fr-on="1"]{ background:transparent; color:var(--text); }
/* [25/08 · Luis] O TECLADO DA FITA ENCOLHE 25%. Ordem: *"Diminua a altura do
 * teclado em 25%"*. So dentro da fita ([data-fr-on="1"]): a MESMA .score-play-page
 * serve a pauta do curso e a de Minhas Partituras, e la a altura foi medida
 * para a leitura, nao para a pista. O que o teclado devolve vai inteiro para o
 * .fr-canvas-wrap, que e flex:1 — a pista de fita ganha exatamente estes px. */
/* [29/08 · Luis] E MAIS 15%: *"diminua a altura das teclas do modo fita
 * rolante em 15%"*. 25,65vh x 0,85 = 21,8025vh · 163,875px x 0,85 = 139,29375px.
 * Continua escopado em [data-fr-on="1"]: a MESMA .score-play-page serve a
 * pauta do curso e a de Minhas Partituras, e la a altura e da LEITURA. */
.score-play-page[data-fr-on="1"]{ --sc-kbd-h:min(21.8025vh,139.29375px); }
/* a moldura laranja de 12px é da PAUTA (papel emoldurado). Na fita ela vira
 * uma tarja grossa em volta de um jogo — sai, e no lugar entra o divisor de
 * 1px da .practice-bar. */
.score-play-page[data-fr-on="1"]::before{ box-shadow:none; }

/* o véu que dá contraste SEM apagar a constelação: escurece de leve em cima
 * (onde as notas nascem) e mais embaixo (onde elas são julgadas), com o glow
 * laranja da casa no topo — a mesma curva radial da .practice-intro-page. */
.score-play-page[data-fr-on="1"] .fr-canvas-wrap{
  touch-action:none; user-select:none; -webkit-user-select:none;
  background:
    radial-gradient(ellipse 120% 85% at 50% 0%, rgba(232,105,10,.13) 0%, transparent 62%),
    linear-gradient(180deg, rgba(7,7,9,.10) 0%, rgba(7,7,9,.34) 55%, rgba(7,7,9,.66) 100%); }

/* ---------------------------------------------------------------------------
 * A BARRA — pílulas da .practice-bar, em trilhos segmentados
 * ------------------------------------------------------------------------ */
/* [22/08 · UMA LINHA] a barra MUDOU DE CASA: mora dentro da .sc-top, na mesma
 * linha do voltar/toggle — a fileira que ela ocupava virou pista de fita.
 * Fora da fita ela nao pinta nada (display:none): a pauta segue identica.
 * flex:1 + min-width:0 = ela e quem cede espaco; o scroll + fade seguem
 * como rede quando nem cedendo cabe. */
.fr-bar{ display:none; }
.score-play-page[data-fr-on="1"] .fr-bar{ display:flex; align-items:center; gap:8px;
  padding:2.3px 0; flex-wrap:nowrap; flex:1 1 auto; min-width:0;
  overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none;
  position:relative; z-index:2; }
.fr-bar::-webkit-scrollbar{ display:none; }
/* [17/08] o TRILHO: um grupo de escolha mutuamente exclusiva desenhado como
 * UMA peça, não como N pílulas soltas. É o que a .pb-stages faz na Técnica —
 * e é também o que faz a barra caber (ver a conta no bloco de largura). */
.fr-seg{ flex:0 0 auto; display:flex; align-items:center; gap:2px; padding:3.6px;
  border-radius:999px; border:1px solid var(--border2); background:rgba(18,18,20,.78); }
.fr-chip{ flex:0 0 auto; font-family:var(--font-h); font-weight:800; text-transform:uppercase;
  font-size:12.65px; letter-spacing:.09em; padding:7.05px 12.8px; line-height:1;
  border-radius:999px; border:1px solid transparent; background:transparent;
  color:var(--muted); cursor:pointer; white-space:nowrap;
  /* [21/09 · O ALVO DO DEDO] a caixa vai CRESCER (bloco `--fr-alvo`, mais abaixo) e o
     texto tem de ficar no meio dela. Sem isto o rotulo encosta no topo da pilula — que
     e o defeito que o 347 ja pagou noutra caixa: um `min-height` maior do que a caixa
     nao move o botao, move o TEXTO dentro dele. */
  display:inline-flex; align-items:center; justify-content:center;
  -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  transition:background .14s, color .14s, box-shadow .14s; }
.fr-chip:hover{ color:var(--text); background:rgba(255,255,255,.06); }
.fr-chip:active{ transform:scale(.97); }
/* ativa = a MESMA pílula acesa da Técnica (.pb-stage.mt-active) */
.fr-chip.on{ background:var(--primary); border-color:var(--primary); color:#fff;
  box-shadow:0 0 12px rgba(232,105,10,.45); }
/* ação avulsa (Calibrar) — fora do trilho, então carrega a própria casca */
.fr-chip.fr-chip-solo{ border-color:var(--border2); background:rgba(18,18,20,.78);
  display:inline-flex; align-items:center; gap:6.9px; padding:8.2px 13.95px; }
.fr-chip.fr-chip-solo:hover{ border-color:rgba(232,105,10,.55); color:var(--text); }
.fr-chip-solo svg{ width:14.95px; height:14.95px; flex:0 0 auto; }
/* [22/08] O ANDAMENTO no lugar da porcentagem. O trilho continua sendo o mesmo
   .fr-seg (nada de vocabulario novo): o que muda e que dentro dele ha DOIS
   PASSOS e UM NUMERO, e o numero nao e botao — por isso ele nao herda a pilula
   acesa (.fr-chip.on) e sim a leitura tabular da .sc-bpm-val da pauta, que e
   onde o aluno ja le BPM. Largura minima com tabular-nums para o trilho nao
   pular de 2 para 3 digitos (95 -> 100 BPM). */
.fr-seg-bpm{ gap:1px; }
/* 🔑 O PASSO NÃO PODE CRESCER A CAIXA. Ele usa a MESMA font-size da .fr-chip;
   quem dá o alvo de toque é a largura mínima, não um glifo maior. MEDIDO: um
   "−" de 14px punha ~2,5px a mais na altura da barra, a pista do canvas perdia
   esses px e o cartão do Calibrar voltava a VAZAR 1px em 926×428 — exatamente
   o defeito que a suíte visual cobre (baseline 0 → 1 só com este bloco). */
.fr-chip.fr-bpm-btn{ min-width:29.9px; padding-left:9.35px; padding-right:9.35px;
  letter-spacing:0; color:var(--text); }
.fr-chip.fr-bpm-btn[aria-disabled="true"]{ opacity:.34; }
.fr-bpm-val{ flex:0 0 auto; min-width:66.7px; text-align:center; font-family:var(--font-h);
  font-weight:800; font-size:12.65px; line-height:1; letter-spacing:.06em; color:var(--text);
  font-variant-numeric:tabular-nums; white-space:nowrap; padding:0 2px; }
/* o separador antigo continua definido (markup legado / trilhos ausentes) */
.fr-sep{ flex:0 0 auto; width:1px; height:20.7px; background:rgba(255,255,255,.12); margin:0 2px; }
/* legenda das mãos: as MESMAS cores em toda a feature (fita, tecla, legenda) */
.fr-leg{ flex:0 0 auto; display:inline-flex; align-items:center; gap:5px;
  font-family:var(--font-h); font-size:11.5px; font-weight:800; letter-spacing:.10em;
  text-transform:uppercase; color:var(--muted); }
.fr-leg i{ width:10.35px; height:10.35px; border-radius:3px; display:inline-block; }
.fr-leg .fr-leg-r{ background:var(--fr-cor-r); box-shadow:0 0 8px rgba(124,58,237,.7); }
.fr-leg .fr-leg-l{ background:var(--fr-cor-l); box-shadow:0 0 8px rgba(232,105,10,.7); }

/* [16/08 · mantido] A BARRA CABE, em vez de só rolar. Ela sempre teve
 * `overflow-x:auto`, então nada quebrava — mas em paisagem de iPhone o
 * conteúdo media 851px contra 667 de tela, e "Calibrar" ficava fora do
 * quadro. Controle que existe mas não se vê é lido como controle que falta
 * (foi o relato). O scroll fica como rede; o que muda é caber.
 * [17/08] O trilho segmentado ajuda a conta: cada grupo de 3 perdeu 2 bordas
 * e 2 vãos. A legenda continua sendo a primeira a sair porque as duas cores
 * já se explicam sozinhas na fita e na tecla acesa. */
@media (max-width:1100px){
  .fr-bar{ gap:5px; }
  .fr-chip{ font-size:12.07px; padding:5.9px 8.2px; letter-spacing:.05em; }
  .fr-seg{ padding:2.45px; }
  /* ⚠️ a legenda sai já aqui, e não só no celular estreito: em 926×428 (o
   * iPhone Pro Max deitado) o app entra no layout `dt` e o container da
   * prática mede 651px, não 926 — medido. É esse caso que precisa dos ~110px
   * que ela ocupa. */
  .fr-leg{ display:none; }
}
@media (max-width:800px){
  .fr-bar{ gap:4px; }
  .fr-chip{ font-size:11.5px; padding:5.9px 7.05px; letter-spacing:.03em; }
}
/* o trilho do andamento encolhe junto com o resto da barra (a conta de largura
   do bloco acima vale para ele igual: cada px aqui e px que "Calibrar" ganha) */
@media (max-width:1100px){
  .fr-chip.fr-bpm-btn{ min-width:25.3px; padding-left:7.05px; padding-right:7.05px; }
  .fr-bpm-val{ min-width:59.8px; font-size:12.07px; letter-spacing:.03em; }
}
@media (max-width:800px){
  .fr-chip.fr-bpm-btn{ min-width:23px; padding-left:5.9px; padding-right:5.9px; }
  .fr-bpm-val{ min-width:55.2px; font-size:11.5px; }
}
/* ---------------------------------------------------------------------------
 * QUANDO "CALIBRAR" VIRA SÓ O ÍCONE — e por que a largura da JANELA não serve
 * de régua aqui.
 * ---------------------------------------------------------------------------
 * Medido: com o rótulo, a barra pede 684px. Isso cabe em 844 (sobra folga
 * visível) e NÃO cabe em 667. Até aí, `max-width` resolveria.
 * O caso que quebra a régua é o iPhone Pro Max deitado, 926×428: a janela tem
 * 926px, mas o app entra no layout `dt` e o container da prática mede **651** —
 * medido, e já registrado no comentário da legenda acima. Uma media query de
 * viewport leria "926, tem espaço de sobra" e deixaria a barra estourar por
 * 96px, que é exatamente o defeito de 16/08 voltando.
 * Então a régua são as DUAS condições reais, cada uma no seu sinal:
 *   · janela estreita de verdade (≤800) — o iPhone SE deitado;
 *   · layout `dt` num viewport que ainda é pequeno (≤1000) — o Pro Max deitado,
 *     onde o `dt` é justamente o que encolhe o container.
 * Em 844 (nem uma, nem outra) o rótulo FICA, que é onde ele cabe e faz falta.
 * O nome nunca some de verdade: segue no `aria-label` e no `title`.
 * ------------------------------------------------------------------------ */
@media (max-width:800px){
  .fr-chip.fr-chip-solo{ padding:7.05px 10.5px; gap:0; }
  .fr-chip-solo .fr-chip-rot{ display:none; }
}
@media (max-width:1000px){
  html.dt .fr-chip.fr-chip-solo{ padding:7.05px 10.5px; gap:0; }
  html.dt .fr-chip-solo .fr-chip-rot{ display:none; }
}
/* [22/08 · UMA LINHA] com voltar+toggle na MESMA linha da barra, os chips
 * cedem mais um passo nos dois sinais estreitos, e o sufixo "BPM" sai — o
 * trilho do andamento se explica pelos ± e o numero (o nome segue no
 * aria-label do grupo). Medido pela validate_fita_visual, nao por viewport. */
@media (max-width:800px){
  .fr-bar{ gap:3px; }
  .fr-chip{ font-size:10.92px; padding:4.75px 5.9px; letter-spacing:.02em; }
  .fr-seg{ padding:1.3px; gap:1px; }
  .fr-bpm-suf{ display:none; }
  .fr-bpm-val{ min-width:29.9px; font-size:11.5px; letter-spacing:.02em; }
  .fr-chip.fr-bpm-btn{ min-width:20.7px; padding-left:4.75px; padding-right:4.75px; }
  .score-play-page[data-fr-on="1"] .fr-toggle[data-fr-vivo="1"]{
    font-size:10.92px; padding:5.97px 9.42px; letter-spacing:.06em; }
}
@media (max-width:1000px){
  html.dt .fr-bar{ gap:3px; }
  html.dt .fr-chip{ font-size:10.92px; padding:4.75px 5.9px; letter-spacing:.02em; }
  html.dt .fr-seg{ padding:1.3px; gap:1px; }
  html.dt .fr-bpm-suf{ display:none; }
  html.dt .fr-bpm-val{ min-width:29.9px; font-size:11.5px; letter-spacing:.02em; }
  html.dt .fr-chip.fr-bpm-btn{ min-width:20.7px; padding-left:4.75px; padding-right:4.75px; }
  html.dt .score-play-page[data-fr-on="1"] .fr-toggle[data-fr-vivo="1"]{
    font-size:10.92px; padding:5.97px 9.42px; letter-spacing:.06em; }
}
/* ---------------------------------------------------------------------------
 * PAISAGEM BAIXA (≤470px de altura): o cartão do véu encolhe de verdade.
 * ---------------------------------------------------------------------------
 * Os `clamp(px, vh, px)` acima medem a JANELA, e a janela mente sobre a caixa:
 * em 926×428 o app entra no layout `dt` e o container da prática mede 651px de
 * largura — o texto do "Calibrar" quebra em 2 linhas e o cartão volta a passar
 * da pista (medido: 10px pra cada lado). Aqui os números caem de vez.
 * ------------------------------------------------------------------------ */
@media (max-height:470px){
  .score-play-page[data-fr-on="1"] .fr-veu{ padding:5px 10px; }
  .score-play-page[data-fr-on="1"] .fr-veu-card{ gap:3px; padding:6px 14px; border-radius:14px; }
  .score-play-page[data-fr-on="1"] .fr-veu-t{ font-size:9.5px; letter-spacing:.14em; }
  .score-play-page[data-fr-on="1"] .fr-placar{ font-size:clamp(18px, 4.4vh, 26px); }
  .score-play-page[data-fr-on="1"] .fr-veu-sub{ font-size:10.5px; line-height:1.25; max-width:56ch; }
  .score-play-page[data-fr-on="1"] .fr-veu-acoes{ gap:8px; margin-top:2px; }
  .score-play-page[data-fr-on="1"] .fr-veu-btn{ font-size:11.5px; padding:7px 17px; letter-spacing:.09em; }
  .score-play-page[data-fr-on="1"] .fr-veu-btn.fr-sec{ font-size:10.5px; padding:6px 13px; }
}
/* e a rede de segurança para qualquer largura que ainda não caiba: um fade na
 * borda direita DIZ que a barra continua. Sem ele o corte seco é lido como
 * "acabou aqui". Onde tudo cabe, o conteúdo termina antes e o fade não pinta
 * nada. */
.fr-bar{ -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 20px),transparent);
  mask-image:linear-gradient(to right,#000 calc(100% - 20px),transparent); }

/* ---------------------------------------------------------------------------
 * [09/09 · Luis] A SOBRA DA BARRA, REPARTIDA EM PARTES IGUAIS
 * ---------------------------------------------------------------------------
 * "existe um espaco vazio entre o botao editar e o icone de auto falante ...
 *  todos os icones distribuidos uniformemente e simetricamente na barra de
 *  icones tanto da fita, quanto da partitura" — e a barra e a MESMA nas duas
 * vistas (blocos e pauta), entao a regra e uma so.
 * Quem decide se cabe e o `__frBarraEspalha` no JS (ele mede, o CSS pinta): o
 * atributo so aparece quando o conteudo CABE na caixa. Com ele:
 *   · space-evenly — vaos iguais TAMBEM nas pontas (o que `space-between` nao
 *     da: ele cola o primeiro e o ultimo nas bordas e engorda so o meio);
 *   · sem mascara — o fade da ponta direita existe para dizer "a barra
 *     continua"; onde tudo cabe ele so apagaria o ultimo icone, que agora
 *     termina perto da borda em vez de 90px antes dela.
 * Sem o atributo (nao cabe) fica tudo como sempre foi: empacotado a esquerda,
 * rolando, com o fade. Ver a conta inteira no comentario do __frBarraEspalha.
 * ------------------------------------------------------------------------ */
.score-play-page[data-fr-on="1"] .fr-bar[data-fr-bar-cabe="1"]{ justify-content:space-evenly; }
.fr-bar[data-fr-bar-cabe="1"]{ -webkit-mask-image:none; mask-image:none; }

/* ---------------------------------------------------------------------------
 * [21/09 · Luis] O ALVO DO DEDO NA BARRA DA PRATICA — "a barra tem uma altura
 * mas os botoes nao aproveitam toda essa altura"
 * ---------------------------------------------------------------------------
 * MEDIDO NESTA BANCADA, ANTES DE ESCREVER A REGRA (playwright, peca do aluno,
 * as duas vistas — a `.fr-bar` e a MESMA nas duas):
 *
 *    tela                 .sc-top   .fr-bar   chip    ←
 *    iPhone SE 667x375     55,69     38,59    25,30   38
 *    iPhone 14 844x390     61,09     35,63    22,42   44   (html.dt)
 *    ProMax    926x428     61,09     35,63    22,42   44   (html.dt)
 *    Tab A7   1180x800     78,88     61,78    48,00   48   (html.tb)
 *
 * 🔑 O TABLET JA TINHA PISO DE DEDO e o telemovel NAO: `html.tb .fr-chip{
 * min-height:var(--tt-tap)}` (48px) existe desde a varredura do `tb`, e no
 * telemovel nunca houve regra nenhuma — o chip fica com a altura do TEXTO
 * (font-size 10,92 + padding 4,75x2 + borda) = 22,4px dentro de uma fileira de
 * 61. Sao 22 px de alvo onde o iOS pede 44 e o Android 48.
 *
 * 🔑 E A FILEIRA NAO PODE CRESCER — cada pixel vertical aqui e area de fita (a
 * conta esta no bloco do `.sc-top` da fita: 16→9 no topo devolveu ~14px de
 * pista). Quem paga o crescimento do chip e a PROPRIA BARRA: ela tinha
 * `padding:2.3px 0` de casca vertical, que nao pinta nada (o trilho ja tem a
 * sua borda e o seu respiro) e custava 4,6px de fileira. Com ele em 0, o chip
 * cresce exatamente esses 4,6 mais a folga que a fileira ja tinha por causa do
 * ←, e a `.sc-top` fica igual ou MENOR do que era.
 *
 * O numero nao e escolhido a dedo: e a receita da fileira menos a casca do
 * trilho (`.fr-seg` = padding 1,3 + borda 1, dos dois lados). Uma variavel so,
 * declarada na PAGINA e herdada — nunca redeclarada na propria caixa (a licao
 * de 16/09: declaracao local vence heranca, e um override a meio caminho
 * ficaria inerte).
 * ------------------------------------------------------------------------ */
.score-play-page[data-fr-on="1"]{ --fr-alvo:32px; }
html.dt .score-play-page[data-fr-on="1"]{ --fr-alvo:38px; }
/* no tablet o piso ja era 48 e continua a ser: sem esta linha a regra abaixo
   (mais especifica que `html.tb .fr-chip`) BAIXARIA o alvo do tablet para 32 */
html.tb .score-play-page[data-fr-on="1"]{ --fr-alvo:var(--tt-tap); }
.score-play-page[data-fr-on="1"] .fr-bar{ padding:0; }
.score-play-page[data-fr-on="1"] .fr-chip{ min-height:var(--fr-alvo); }
/* a acao avulsa esta FORA do trilho, entao ela propria tem de medir o que o
   trilho mede — senao a fileira fica com dois tamanhos de botao */
.score-play-page[data-fr-on="1"] .fr-chip.fr-chip-solo{ min-height:calc(var(--fr-alvo) + 6px); }
/* o numero do andamento nao e botao (nao tem alvo a crescer), mas mora dentro
   do trilho que cresceu: sem isto ele encostaria no topo da pilula */
.score-play-page[data-fr-on="1"] .fr-bpm-val{ display:inline-flex; align-items:center; justify-content:center; }
/* o ESCAPE do lote (`?alvo=0`): devolve a barra byte a byte ao que esta no ar —
   o chip volta a medir a altura do texto e a barra recupera o proprio padding.
   Escopado no `html.tt-alvo0`, que so nasce com a query. */
html.tt-alvo0 .score-play-page[data-fr-on="1"] .fr-bar{ padding:2.3px 0; }
html.tt-alvo0 .score-play-page[data-fr-on="1"] .fr-chip,
html.tt-alvo0 .score-play-page[data-fr-on="1"] .fr-chip.fr-chip-solo{ min-height:0; }
html.tt-alvo0.tb .fr-chip{ min-height:var(--tt-tap); }

/* o canvas da fita: exatamente a largura do teclado, altura = o que sobra */
.fr-canvas-wrap{ position:relative; flex:1; min-height:0; }
.fr-canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }

/* ---------------------------------------------------------------------------
 * OS VÉUS (começar / pausado / fim / calibração)
 * ---------------------------------------------------------------------------
 * 🔴 [17/08 · DEFEITO MEDIDO E CORRIGIDO] Em 844×390 (iPhone deitado — o caso
 * real) o canvas tem ~165px de altura. O conteúdo dos véus era uma coluna com
 * gap fixo de 10px e botões de 16px/12px: o "fim" media ~190px e o "calibrar"
 * ~200px. Resultado nos retratos do ANTES: o título passava POR TRÁS da barra
 * de chips e o 2º botão pousava EM CIMA do teclado. Não era estilo — era
 * conteúdo maior que a caixa, calado.
 * A cura tem três partes, e nenhuma delas é "diminuir a fonte e torcer":
 *   1. os botões viram FILEIRA (.fr-veu-acoes), não pilha — dois botões
 *      passam a custar UMA altura, não duas;
 *   2. tipografia e vãos em `clamp(px, vh, px)` — encolhem com a altura REAL
 *      da caixa, que é a variável que estourava;
 *   3. `overflow:auto` no véu como rede: se algum idioma longo ainda passar,
 *      ele rola em vez de vazar por baixo do teclado.
 * ------------------------------------------------------------------------ */
.fr-veu{ position:absolute; inset:0; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:10px; background:rgba(246,241,231,.9); z-index:6; text-align:center;
  padding:14px; }
.fr-veu[hidden]{ display:none; }
.score-play-page[data-fr-on="1"] .fr-veu{
  gap:0; padding:8px 12px; overflow:auto; -webkit-overflow-scrolling:touch;
  background:linear-gradient(180deg, rgba(7,7,9,.70) 0%, rgba(7,7,9,.88) 100%); }
/* o CARTÃO — vocabulário dos cards da casa: canto 18, fio laranja translúcido,
 * miolo quase preto com um sopro de laranja no topo */
.fr-veu-card{ display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:clamp(5px, 1.5vh, 11px);
  max-width:min(600px, 94%); margin:auto;
  padding:clamp(10px, 2.4vh, 20px) clamp(16px, 3vw, 28px);
  border-radius:18px; border:1px solid rgba(232,105,10,.30);
  background:
    radial-gradient(ellipse 130% 150% at 50% 0%, rgba(232,105,10,.16) 0%, transparent 62%),
    rgba(15,15,17,.94);
  box-shadow:0 18px 52px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.03) inset; }
.fr-veu-acoes{ display:flex; align-items:center; justify-content:center; gap:10px;
  flex-wrap:wrap; margin-top:clamp(1px, .6vh, 5px); }
.fr-veu-btn{ display:inline-flex; align-items:center; gap:9px; font-size:16px; font-weight:800;
  padding:12px 22px; border-radius:999px; border:0; background:var(--fr-cor-r); color:#fff;
  cursor:pointer; font-family:inherit; box-shadow:0 6px 20px rgba(124,58,237,.35);
  -webkit-tap-highlight-color:transparent; }
.fr-veu-btn.fr-sec{ background:rgba(0,0,0,.08); color:var(--sc-ink); box-shadow:none; font-size:13.5px;
  padding:9px 16px; }
.fr-veu-t{ font-size:15px; font-weight:800; color:var(--sc-ink); }
.fr-veu-sub{ font-size:12.5px; color:var(--sc-ink); opacity:.7; max-width:400px; }
.fr-placar{ font-size:26px; font-weight:800; color:var(--sc-ink); }
/* --- as mesmas peças, na pele do app --- */
.score-play-page[data-fr-on="1"] .fr-veu-t{
  font-family:var(--font-h); font-weight:800; text-transform:uppercase;
  letter-spacing:.18em; font-size:clamp(9.5px, 1.6vh, 12px);
  color:var(--primary-2); opacity:1; }
.score-play-page[data-fr-on="1"] .fr-placar{
  font-family:var(--font-h); font-weight:900; letter-spacing:-.02em;
  font-size:clamp(23px, 5.6vh, 44px); line-height:1; color:#fff; }
.score-play-page[data-fr-on="1"] .fr-veu-sub{
  font-family:var(--font-b); font-size:clamp(10.5px, 1.8vh, 13.5px); line-height:1.35;
  color:var(--muted); opacity:1; max-width:44ch; }
/* o CTA: a mesma pílula quente do "Praticar"/"Pular" da Técnica */
.score-play-page[data-fr-on="1"] .fr-veu-btn{
  font-family:var(--font-h); font-weight:800; text-transform:uppercase; letter-spacing:.11em;
  font-size:clamp(12px, 2vh, 15px);
  padding:clamp(9px, 1.8vh, 14px) clamp(18px, 3.2vw, 28px);
  border-radius:999px; border:0; color:#fff;
  background:linear-gradient(180deg, var(--primary-2) 0%, var(--primary) 100%);
  box-shadow:0 8px 24px rgba(232,105,10,.42); }
.score-play-page[data-fr-on="1"] .fr-veu-btn:hover{
  background:linear-gradient(180deg, #FF8A2E 0%, var(--primary-2) 100%); }
.score-play-page[data-fr-on="1"] .fr-veu-btn:active{ transform:scale(.97); }
/* a secundária continua secundária: contorno, sem peso, sem glow */
.score-play-page[data-fr-on="1"] .fr-veu-btn.fr-sec{
  background:transparent; border:1.5px solid var(--border2); color:var(--muted);
  box-shadow:none; font-size:clamp(10.5px, 1.6vh, 12.5px);
  padding:clamp(7px, 1.4vh, 11px) clamp(13px, 2.4vw, 20px); }
.score-play-page[data-fr-on="1"] .fr-veu-btn.fr-sec:hover{
  border-color:rgba(232,105,10,.55); color:var(--text); }

/* a CONTAGEM — Montserrat 900 com o halo quente da marca */
.fr-conta{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  z-index:5; pointer-events:none; font-weight:800; font-size:min(22vh,120px); color:var(--sc-ink);
  opacity:0; }
.score-play-page[data-fr-on="1"] .fr-conta{
  font-family:var(--font-h); font-weight:900; color:#fff; font-size:min(30vh,150px);
  text-shadow:0 0 24px rgba(232,105,10,.95), 0 0 66px rgba(232,105,10,.55),
              0 8px 30px rgba(0,0,0,.75); }
.fr-conta.on{ animation:frConta .92s ease both; }
@keyframes frConta{ 0%{ opacity:0; transform:scale(1.18) } 12%{ opacity:.92; transform:scale(1.04) }
  100%{ opacity:0; transform:scale(.96) } }
@media (prefers-reduced-motion:reduce){ .fr-conta.on{ animation-duration:.01ms; } }

/* ---------------------------------------------------------------------------
 * O TECLADO (só na fita) — o piano ESCURO da tela de prática
 * ---------------------------------------------------------------------------
 * O piano branco é da PAUTA: papel branco pede tecla branca. Na fita ele era o
 * que mais gritava contra a constelação — vira o piano da Técnica (branca
 * #2A2A2A, preta #0E0E0E, divisão preta), que é o desenho que o aluno já
 * conhece de todos os exercícios. Escopado: a pauta segue com o piano branco.
 * ------------------------------------------------------------------------ */
/* [04/09 · AJUSTES DA FITA 04/09 · Luis: "nova ui de teclado, preto e branco e
 * nao mais 2d, com luz e sombra, 3d"] Os degrades vivem em <defs> dentro do
 * proprio SVG da fita (__frTeclado3D); a cor solta depois do url() e o
 * fallback de quem nao os tiver. Branca: marfim com a FACE DA FRENTE mais
 * escura nos ultimos 14 %; preta: lombo iluminado a 78 %. */
.score-play-page[data-fr-on="1"] .kbd-key.kbd-w{ fill:url(#frKW) #F2F2ED; stroke:#17171b; }
.score-play-page[data-fr-on="1"] .kbd-key.kbd-b{ fill:url(#frKB) #111114; stroke:#000; }
.score-play-page[data-fr-on="1"] .fr-ksomb, .score-play-page[data-fr-on="1"] .fr-klip{ pointer-events:none; }
/* a camada HTML sobre o teclado: C2…C7 na ponta dos dos e o numero do dedo */
.fr-kbd-ov{ position:absolute; inset:0; pointer-events:none; overflow:hidden; z-index:5;
  /* ↓ a LINHA DA INFORMAÇÃO — o comentário abaixo explica de onde sai cada parcela */
  --fr-linha-info:calc((37.5% + max(0px, env(safe-area-inset-bottom,0px) - 8px)) / 2); }
/* 🔴 [07/09 · Luis: "a barra nativa do iOS está tampando o teclado justamente
 * onde aparece o número da digitação. tem como remover completamente a barra da
 * tela horizontal de prática?"] NÃO SE REMOVE. O home indicator é desenhado
 * pelo próprio iOS por cima de qualquer página, e nem a PWA em standalone o
 * esconde: o `viewport-fit=cover` do <meta> autoriza DESENHAR por baixo dele,
 * nunca apagá-lo (a única API que o esconde, `prefersHomeIndicatorAutoHidden`,
 * é de app nativo e não existe para web).
 * O que se pode — e é o que resolve a queixa — é não deixar nada de LER debaixo
 * dele: o número do dedo e o nome da oitava sobem a altura exata da barra. A
 * tecla continua do mesmo tamanho e o toque continua a valer ali; o que fica
 * por baixo passa a ser a madeira da tecla, não a informação. Fora do iPhone o
 * `env()` vale 0 e nada muda.
 * ---------------------------------------------------------------------------
 * 🔴 [08/09 · Luis, com print: "eu quero a bolinha dos números exatamente no
 * meio, entre a linha da barra nativa e a linha que tangencia o fim das teclas
 * pretas. bem no meio. (atualmente está encostando nas teclas pretas)" · e logo
 * a seguir: "o mesmo vale para marcação de oitavas C1, C2… os 2 elementos na
 * mesma linha"] SUBIR NÃO ERA CENTRAR. O 271 tirou o número de debaixo da barra
 * empurrando-o `5px + env()` para cima — e isso pousou-o EM CIMA da fronteira
 * das pretas (medido no print: círculo de 18px centrado a 35px do fundo, que é
 * exatamente a linha das pretas). A faixa livre da tecla branca tem DUAS
 * margens, e as duas são medidas, não escolhidas:
 *   • em cima, o fim das pretas: 62,5% da altura do teclado — a MESMA constante
 *     do `.fr-dedo.b` logo abaixo —, ou seja 37,5% contados do fundo;
 *   • em baixo, o TOPO da barra do iOS, que NÃO é o `env()`: em paisagem o
 *     inset de segurança é 21px e o indicador é desenhado com 5px de altura e
 *     ~8px de folga por baixo. Medido no print do Luis (iPhone 14 Pro Max
 *     deitado, @3x: teclado 1004→1284, pretas acabam em 1181, a barra ocupa
 *     1245→1260 em px de aparelho) dá 13px do fundo = env() − 8px, cravado.
 * A linha é a MÉDIA das duas, e é UMA SÓ porque as duas informações — o número
 * do dedo e o C2…C7 — pousam nela: por isso a `--fr-linha-info` vive no
 * overlay, e a oitava ganha a mesma caixa de 18px do círculo para que os
 * CENTROS coincidam (com `line-height:1` o centro do texto seguia o tamanho da
 * fonte, que é `clamp()` — flutuava com a altura da tela).
 * Fora do iPhone o `env()` vale 0, o `max()` come o negativo e a linha passa a
 * ser o meio da faixa inteira — que é o mesmo desenho sem barra nenhuma. */
.fr-oct{ position:absolute; bottom:calc(var(--fr-linha-info) - 9px); transform:translateX(-50%);
  height:18px; font:700 clamp(8px,1.6vh,11px)/18px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  color:#8A8A85; letter-spacing:.02em; }
.fr-dedo{ position:absolute; transform:translate(-50%,0); min-width:18px; height:18px; padding:0 4px; border-radius:9px;
  background:var(--fr-cor-r); color:#fff; font:800 12px/18px -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  text-align:center; box-shadow:0 2px 6px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.35);
  animation:frDedoCai .36s cubic-bezier(.22,.8,.3,1) both; }
.fr-dedo.l{ background:var(--fr-cor-l); }
.fr-dedo.w{ bottom:calc(var(--fr-linha-info) - 9px); }
.fr-dedo.b{ top:calc(62.5% - 22px); }
.fr-dedo.sai{ animation:frDedoSai .2s ease-in forwards; }
@keyframes frDedoCai{ 0%{ opacity:0; transform:translate(-50%,-26px) scale(.7); } 55%{ opacity:1; transform:translate(-50%,3px) scale(1.1); }
  78%{ transform:translate(-50%,-1px) scale(.98); } 100%{ opacity:1; transform:translate(-50%,0) scale(1); } }
@keyframes frDedoSai{ to{ opacity:0; transform:translate(-50%,8px) scale(.8); } }
/* a tecla REQUISITADA (a nota bateu no teclado e nao foi tocada): SEMI-acesa na
 * cor da mao. As regras da tecla ACESA (`.fr-k-r/.fr-k-l`, abaixo) vem DEPOIS
 * com a mesma especificidade e !important — tocou, acende por inteiro. */
.score-play-page .kbd-key.fr-k-req-r.kbd-w{ fill:#C9B6F5 !important; filter:drop-shadow(0 0 6px rgba(124,58,237,.55)); }
.score-play-page .kbd-key.fr-k-req-r.kbd-b{ fill:#5E42A8 !important; filter:drop-shadow(0 0 6px rgba(124,58,237,.6)); }
.score-play-page .kbd-key.fr-k-req-l.kbd-w{ fill:#F7C9A2 !important; filter:drop-shadow(0 0 6px rgba(232,105,10,.55)); }
.score-play-page .kbd-key.fr-k-req-l.kbd-b{ fill:#A8581C !important; filter:drop-shadow(0 0 6px rgba(232,105,10,.6)); }
/* teclas acesas pela fita — MESMAS cores das mãos; neutro = "foi aqui que você
 * tocou" (150ms, cinza discreto — feedback, não punição) */
.score-play-page .kbd-key.fr-k-r{ fill:var(--fr-cor-r) !important; }
.score-play-page .kbd-key.fr-k-l{ fill:var(--fr-cor-l) !important; }
.score-play-page .kbd-key.fr-k-n{ fill:#9A9A9A !important; }
/* no palco escuro a tecla acesa ganha halo (o mesmo gesto do .kbd-playing da
 * Técnica). Só brilho — a cor e a semântica não mudam. */
.score-play-page[data-fr-on="1"] .kbd-key.fr-k-r{
  filter:drop-shadow(0 0 10px rgba(124,58,237,.9)) brightness(1.06); }
.score-play-page[data-fr-on="1"] .kbd-key.fr-k-l{
  filter:drop-shadow(0 0 10px rgba(232,105,10,.9)) brightness(1.06); }
.score-play-page[data-fr-on="1"] .kbd-key.fr-k-n{ fill:#565656 !important; }

/* ---------------------------------------------------------------------------
 * O TOPO (só na fita) — respiro menor e tipografia da casa
 * ---------------------------------------------------------------------------
 * O padding de 16/24 é da leitura de partitura (papel quer margem). Na fita
 * cada pixel vertical é área de fita: 16→9 no topo devolve ~14px de pista em
 * 390 de tela, que é exatamente o que faltava aos véus.
 * ------------------------------------------------------------------------ */
.score-play-page[data-fr-on="1"] .sc-top{
  padding:calc(10.35px + env(safe-area-inset-top)) calc(16px + env(safe-area-inset-right))
          5.75px calc(16px + env(safe-area-inset-left));
  border-bottom:1px solid rgba(255,255,255,.07); }
/* 🔴 [13/09 · APANHADO NO PRINT, NÃO NA RÉGUA] A CALHA DO ALTO-FALANTE MORRIA AQUI.
   O app reserva o vão do `#tt-loc-btn` (fixed, 38px, canto superior direito) com
   `.score-play-page .sc-top{ padding-right: calc(50px + …) }`, mais abaixo neste
   arquivo. A regra de cima é do mesmo elemento, tem MAIS especificidade (classe +
   atributo) e escreve `padding` ABREVIADO — e o abreviado não ajusta o topo: ele
   REESCREVE OS QUATRO LADOS. A calha de 50px voltava a 16px sem ninguém pedir, e o
   alto-falante caía em cima do último controle da barra.
   Estava no ar assim, a cortar o chip EDITAR, e a régua não via: ela media a CAIXA
   do botão (`right <= largura da tela`), e a caixa estava dentro — quem estava por
   cima era outro elemento, `position:fixed`, que a conta dela não conhecia. Com a
   edição na fita o botão cortado passou a ser o GRAVAR, que é a ação principal do
   modo: sair sem gravar por não conseguir tocar no gravar é a pior troca possível.
   A cura usa o mecanismo que já existe: `body.tt-loc-visivel`, escrita pelo
   `pinta()` do tt-loc.js no MESMO instante em que ele mostra o botão (o
   `body:has()` saiu em 04/09 por custo de recálculo — ver o bloco do #tt-loc-btn).
   Sem alto-falante na tela, o respiro de 16px da fita fica como estava. */
body.tt-loc-visivel .score-play-page[data-fr-on="1"] .sc-top{
  padding-right:calc(50px + max(var(--sal,0px), env(safe-area-inset-right, 0px))); }
.score-play-page[data-fr-on="1"] .sc-crumb{
  font-family:var(--font-h); font-weight:700; font-size:12.5px; letter-spacing:.01em;
  color:var(--text); opacity:.82; max-width:16vw; overflow:hidden; text-overflow:ellipsis; }
/* [22/08 · UMA LINHA] com a barra na mesma linha, o crumb e o primeiro a sair:
 * ele repete o que o aluno acabou de escolher; os controles nao tem substituto.
 * Mesma dupla de sinais ja registrada no bloco do Calibrar. */
@media (max-width:1100px){
  .score-play-page[data-fr-on="1"] .sc-crumb{ display:none; } }
@media (max-width:1300px){
  html.dt .score-play-page[data-fr-on="1"] .sc-crumb{ display:none; } }
/* o "voltar pra pauta": deixa de ser um bloco roxo chapado (a cor da MÃO
 * DIREITA — dizia a coisa errada) e vira a pílula fantasma da casa. */
.score-play-page[data-fr-on="1"] .fr-toggle[data-fr-vivo="1"]{
  font-family:var(--font-h); font-weight:800; text-transform:uppercase; letter-spacing:.12em;
  font-size:12.07px; padding:7.12px 15.17px;
  background:rgba(18,18,20,.78); border:1px solid var(--border2); color:var(--muted); }
.score-play-page[data-fr-on="1"] .fr-toggle[data-fr-vivo="1"]:hover{
  border-color:rgba(232,105,10,.55); color:var(--text); }
/* o aviso curto (oitava / sem MIDI) na pele do app */
.score-play-page[data-fr-on="1"] .fr-avisomidi{
  font-family:var(--font-b); background:rgba(15,15,17,.96);
  border:1px solid rgba(232,105,10,.42); box-shadow:0 10px 30px rgba(0,0,0,.6); }

/* contador de frame time (?frstats=1 — bancada, mesmo espírito do audiolog) */
.fr-stats{ position:absolute; right:8px; top:6px; z-index:40; background:rgba(20,20,20,.82);
  color:#fff; font:10.5px/1.5 monospace; padding:5px 8px; border-radius:8px; pointer-events:none;
  white-space:pre; }

/* ═══════════════════════════════════════════════════════════════════════════
   [13/09 · Luis] A EDIÇÃO DENTRO DA FITA (.fe-*)
   ═══════════════════════════════════════════════════════════════════════════
   Substitui o bloco `.pr-*` do editor de PIANO ROLL EM TELA PRÓPRIA, que
   morreu com ele (ordem: *"nao vai ter tela separada, a edicao vai ocorrer na
   mesma tela da fita"*).

   🔑 A DIFERENÇA DE DESENHO É TODA ESTA: o editor antigo era uma TELA — tinha
   área própria, pista própria, teclado próprio e uma barra de 16 controles que
   rolava. Aqui não há tela nenhuma: a pista é a pista da fita, o teclado é o
   teclado da fita, e a única coisa que troca é a BARRA — a `.fe-bar` toma o
   lugar da `.fr-bar` na MESMA linha e com a mesma altura, para que entrar na
   edição não mova um pixel do que está por baixo.

   O que a edição acrescenta ao CSS é só o que não existia: os chips dela, o
   menu da grade, a dica que se apaga sozinha e a lista de perdas dentro do véu
   de gravar. Véus, trilhos, pílulas e cores das mãos são os MESMOS da fita.

   🔑 A CONTA DE ALTURA É A DA FITA, e vale repetir: em 844×390 o conteúdo útil
   tem ~165px. Por isso a barra é UMA linha que rola, os véus têm
   `overflow:auto` como rede, e a lista de perdas tem altura máxima em `vh`.
   ═══════════════════════════════════════════════════════════════════════════ */

/* --- a barra --- */
.fe-bar{ display:none; }
.score-play-page[data-fe-on="1"] .fr-bar{ display:none !important; }
.score-play-page[data-fe-on="1"] .fe-bar{ display:flex; align-items:center; gap:8px;
  flex-wrap:nowrap; flex:1 1 auto; min-width:0; padding:2.3px 0;
  overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none;
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 16px),transparent);
  mask-image:linear-gradient(to right,#000 calc(100% - 16px),transparent); }
.fe-bar::-webkit-scrollbar{ display:none; }
.fe-espaco{ flex:1 1 auto; min-width:2px; }
.fe-bar .fr-chip[disabled]{ opacity:.3; pointer-events:none; }
.fe-bar .fr-chip.fe-ico{ min-width:30px; padding:6px 8px; display:inline-flex;
  align-items:center; justify-content:center; color:var(--text); }
.fe-bar .fr-chip.fe-ico svg{ width:14px; height:14px; }
.fe-bar .fr-chip-solo svg{ width:14px; height:14px; }
.fe-desc{ color:var(--muted); }
.fe-desc:hover{ color:var(--text); }
/* a grade mostra RÓTULO + VALOR: o valor é o que se lê de relance */
.fe-grade{ display:inline-flex; align-items:baseline; gap:6px; color:var(--text); }
.fe-grade .fe-grade-rot{ color:var(--muted); font-size:9.5px; letter-spacing:.10em; }
.fe-grade b{ font-weight:900; letter-spacing:.02em; font-variant-numeric:tabular-nums; }
/* as duas mãos carregam a PRÓPRIA cor — é a mesma legenda da fita, só que
   dentro do botão que a aplica. Elas nunca ficam mortas: sem nota escolhida
   continuam a valer como a COR DA PRÓXIMA nota criada. */
.fe-mao-r i, .fe-mao-l i{ display:inline-block; width:8px; height:8px; border-radius:3px;
  margin-right:5px; vertical-align:-1px; }
.fe-mao-r i{ background:var(--fr-cor-r); box-shadow:0 0 7px rgba(124,58,237,.75); }
.fe-mao-l i{ background:var(--fr-cor-l); box-shadow:0 0 7px rgba(232,105,10,.75); }
.fe-mao-r.on{ background:var(--fr-cor-r); border-color:var(--fr-cor-r); color:#fff;
  box-shadow:0 0 12px rgba(124,58,237,.45); }
.fe-mao-r.on i{ background:#fff; box-shadow:none; }
.fe-mao-l.on{ background:var(--fr-cor-l); border-color:var(--fr-cor-l); color:#fff; }
.fe-mao-l.on i{ background:#fff; box-shadow:none; }
.fe-del{ color:var(--text); }
/* GRAVAR é a ação principal: a pílula cheia da casa. Com edição pendente ela
   ganha o halo — é o único lugar da tela que diz "há trabalho não gravado". */
.fe-salvar{ flex:0 0 auto; font-family:var(--font-h); font-weight:800; text-transform:uppercase;
  font-size:11px; letter-spacing:.10em; padding:8px 16px; line-height:1; white-space:nowrap;
  border-radius:999px; border:1px solid var(--border2); background:rgba(18,18,20,.78);
  color:var(--muted); cursor:pointer; -webkit-tap-highlight-color:transparent;
  touch-action:manipulation; transition:background .16s, color .16s, box-shadow .16s; }
.fe-salvar.fe-sujo{ background:var(--primary); border-color:var(--primary); color:#fff;
  box-shadow:0 0 14px rgba(232,105,10,.5); }
.fe-salvar:active{ transform:scale(.97); }

/* --- a pista, em modo edição ---
   O grid e o véu que dá contraste a ele são pintados NO CANVAS (fr-edit.js), e
   não aqui: o fundo da página é a constelação do app e um véu em CSS por cima
   dela apagaria as estrelas na tela inteira, não só na pista. O que o CSS faz
   é só desligar o cursor de "arrastar a fita" — aqui o dedo edita. */
.score-play-page[data-fe-on="1"] .fr-canvas-wrap{ cursor:default; }

/* a DICA: aparece no ALTO da pista (embaixo está a linha, e é lá que o dedo
   trabalha) e apaga-se sozinha em 4,6 s. É o único texto de ajuda que sobrou —
   o tutorial de 12 telas do editor antigo saiu inteiro. */
.fe-dica{ position:absolute; left:50%; top:8px; transform:translateX(-50%); z-index:30;
  max-width:min(540px,93%); text-align:center; pointer-events:none;
  font-size:11px; font-weight:600; line-height:1.35; color:#fff;
  padding:6px 12px; border-radius:11px; background:rgba(15,15,17,.94);
  border:1px solid rgba(232,105,10,.40); box-shadow:0 10px 30px rgba(0,0,0,.55); }
.fe-dica[hidden]{ display:none; }

/* --- menu da grade --- */
.fe-menu{ position:absolute; left:10px; top:8px; z-index:32;
  display:grid; grid-template-columns:repeat(3, minmax(52px, 1fr)); gap:4px;
  padding:7px; border-radius:14px; border:1px solid rgba(232,105,10,.30);
  background:rgba(15,15,17,.98); box-shadow:0 18px 44px rgba(0,0,0,.6); }
.fe-menu[hidden]{ display:none; }
.fe-menu-it{ font-family:var(--font-h); font-weight:800; font-size:11px; letter-spacing:.04em;
  padding:8px 6px; border-radius:9px; border:1px solid transparent; background:rgba(255,255,255,.05);
  color:var(--muted); cursor:pointer; position:relative; -webkit-tap-highlight-color:transparent; }
.fe-menu-it:hover{ color:var(--text); background:rgba(255,255,255,.09); }
.fe-menu-it.on{ background:var(--primary); border-color:var(--primary); color:#fff; }
.fe-menu-it b{ font-weight:800; font-variant-numeric:tabular-nums; }
/* o "3" da quiáltera é sobrescrito — a notação que o músico já conhece */
.fe-menu-it i{ font-style:normal; font-size:8px; position:absolute; top:3px; right:5px;
  opacity:.75; }

/* --- os dois véus de confirmação: os mesmos .fr-veu da fita --- */
.score-play-page[data-fe-on="1"] .fe-veu{ z-index:34; }
/* 🔑 A MÁSCARA NA BASE não é enfeite: a lista rola, e sem ela a última linha
   fica cortada no meio da palavra parecendo defeito de layout. Com o
   esmaecimento, o corte vira convite a rolar. */
.fe-lista{ width:100%; max-width:560px; text-align:left;
  max-height:min(40vh, 168px); overflow:auto; -webkit-overflow-scrolling:touch;
  display:flex; flex-direction:column; gap:6px; padding-right:2px;
  -webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 22px),transparent);
  mask-image:linear-gradient(to bottom,#000 calc(100% - 22px),transparent); }
.fe-lista[hidden]{ display:none; }
.fe-lin{ display:flex; gap:9px; align-items:baseline;
  font-size:11.5px; line-height:1.4; color:var(--text); opacity:.86; }
.fe-lin b{ flex:0 0 auto; font-family:var(--font-h); font-weight:800; color:var(--primary);
  font-variant-numeric:tabular-nums; }
.fe-lin em{ font-style:normal; color:var(--muted); }

/* --- sinais estreitos: cada px que a barra devolve é px de pista --- */
@media (max-width:800px){
  .fe-bar{ gap:4px; }
  .fe-bar .fr-chip{ font-size:9.5px; padding:4px 6px; letter-spacing:.02em; }
  .fe-bar .fr-seg{ padding:1px; gap:1px; }
  .fe-salvar{ font-size:9.5px; padding:6px 11px; letter-spacing:.05em; }
  .fe-bar .fr-chip.fe-ico{ min-width:24px; padding:4px 5px; }
  .fe-grade .fe-grade-rot{ display:none; }
  .fe-mao-rot{ display:none; }
  .fe-mao-r i, .fe-mao-l i{ width:10px; height:10px; margin-right:0; vertical-align:0; }
  .fe-menu{ grid-template-columns:repeat(3, minmax(46px, 1fr)); }
  .fe-dica{ font-size:10px; padding:5px 10px; }
}
@media (max-width:1000px){
  html.dt .fe-bar{ gap:4px; }
  html.dt .fe-bar .fr-chip{ font-size:9.5px; padding:4px 6px; letter-spacing:.02em; }
  html.dt .fe-salvar{ font-size:9.5px; padding:6px 11px; letter-spacing:.05em; }
  html.dt .fe-bar .fr-chip.fe-ico{ min-width:24px; padding:4px 5px; }
  html.dt .fe-grade .fe-grade-rot{ display:none; }
  html.dt .fe-mao-rot{ display:none; }
  html.dt .fe-mao-r i, html.dt .fe-mao-l i{ width:10px; height:10px; margin-right:0; vertical-align:0; }
  html.dt .fe-menu{ grid-template-columns:repeat(3, minmax(46px, 1fr)); }
}
@media (max-height:430px){
  .fe-lista{ max-height:min(46vh, 140px); }
  .fe-dica{ top:5px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   [17/08 · VÍDEO DE NOTAS CAINDO] as três telas da 6ª porta.
   Tudo escopado em .mp-vmt-* e pendurado no .mp-recusa que já existe — com a
   flag TT_VMT desligada nada disto tem alvo no HTML, então é peso morto e não
   risco de regressão. Zero seletor tocado fora deste bloco.
   ══════════════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════════════
   [23/08 · Luis] BANHO DE ELEGÂNCIA nos cartões da 6ª porta.
   Ordens textuais:
     · "de um banho de elegancia na pop up instrutiva corte as 2 pontas. faca
        ela no mesmo padrao das outras pop ups do app, centralize o titulo
        corte as 2 pontas. aumente o tamanho da pop up (altura) deixe o titulo
        em Montserrat. tudo alinhado e simetricamente bem divido."
     · "o mesmo embelezamento deve ocorrer na pagina O teclado esta certo ?
        (quero titulo em montserat)"

   🔑 A CASCA NÃO MUDA — ela JÁ É o padrão da casa. Fundo #161616, borda
   rgba(255,255,255,.1), raio 16 e o × no canto são os mesmos do `.mp-pdf-card`
   (exportar PDF), do `.mp-imp-card` (importar arquivo) e do `.mp-duv-card`.
   Trocar a casca seria inventar um segundo sistema, não seguir o que existe.
   O que muda é o INTERIOR: respiro, divisão e hierarquia.

   🔴 POR QUE O TÍTULO PARECIA TORTO — e não era impressão. Ele herdava só o
   `.mp-imp-tit`, que traz `padding-right:30px` para não passar por baixo do ×.
   Num cartão com `text-align:center`, padding de UM LADO SÓ empurra o texto
   ~15 px para a esquerda: ele centraliza na caixa ENCOLHIDA, não no cartão.
   A cura não é tirar o respiro (o × continua lá) — é dar o MESMO respiro dos
   dois lados. Simetria aqui é uma conta, não um gosto.
   ══════════════════════════════════════════════════════════════════════════ */
.mp-vmt-card{ width:min(440px,94vw); text-align:center;
  /* a ALTURA que o Luis pediu vem daqui e das linhas: mais respiro e divisões
     de verdade, nunca uma altura mínima cega (que deixaria vão morto em
     alemão, onde o mesmo texto ocupa três linhas). */
  padding:16px; gap:12px; }

/* o CABEÇALHO. O fio embaixo não é enfeite: ele divide o cartão nas duas
   coisas que ele é — em cima o que a tela PERGUNTA, embaixo o que ela PEDE. */
.mp-vmt-tit{ display:block;
  font-family:var(--font-h); font-weight:800;
  font-size:clamp(16px, 4.5vw, 18px); line-height:1.24; letter-spacing:-.2px;
  color:#F6F1E7; text-align:center;
  /* 38 = a largura do × (32) + folga, DOS DOIS LADOS. É o par que centra. */
  padding:3px 38px 13px; margin:0 0 1px;
  border-bottom:1px solid rgba(246,241,231,.09); }

/* o × deixa de ser o anel cinza que o navegador desenha em `<button>` sem
   borda declarada e passa a ser desenhado por nós: mesmo canto, mesmo alvo de
   dedo (32 px), peso menor — ele não disputa com o título. */
.mp-vmt-card .mp-pdf-x{ top:11px; right:11px; width:32px; height:32px;
  font-size:20px; color:rgba(246,241,231,.55);
  background:rgba(246,241,231,.05);
  border:1px solid rgba(246,241,231,.11); }
.mp-vmt-card .mp-pdf-x:active{ background:rgba(246,241,231,.11); }

/* os BOTÕES do rodapé, iguais nos três cartões. `min-height` + centragem no
   eixo resolve a assimetria que a foto mostrou na calibração: "Está certo,
   continuar" quebra em duas linhas e "Não está certo" não, e sem isto as duas
   metades ficavam com pesos visuais diferentes. Agora quebrar não desalinha. */
.mp-vmt-card .mp-pdf-acts{ gap:10px; margin-top:2px; }
.mp-vmt-card .mp-pdf-act{ min-height:52px; padding:12px 10px; border-radius:13px;
  font-size:14.5px; line-height:1.25;
  display:flex; align-items:center; justify-content:center; }
/* a PRÉVIA DA CALIBRAÇÃO é a peça que o aluno precisa OLHAR: ela ganha a
   largura toda do cartão e fundo escuro para a grade ciano/vermelha aparecer. */
.mp-vmt-prev{ width:100%; border-radius:13px; overflow:hidden; background:#0b0b0c;
  border:1px solid rgba(255,255,255,.12);
  box-shadow:0 8px 24px rgba(0,0,0,.35); }
.mp-vmt-prev img{ display:block; width:100%; height:auto; }
.mp-vmt-info{ font:12px/1.4 monospace; color:rgba(246,241,231,.7);
  font-variant-numeric:tabular-nums; }
/* padrão de pretas fraco = a grade provavelmente saiu torta. Não recusa —
   só chama o olho do aluno, que é o oráculo desta tela. */
.mp-vmt-info.is-duvida{ color:#E8B34A; font-weight:700; }
.mp-vmt-barra{ width:100%; height:8px; border-radius:99px; overflow:hidden;
  background:rgba(255,255,255,.1); }
.mp-vmt-barra > i{ display:block; height:100%; width:0;
  background:linear-gradient(90deg,#E8785A,#E8B34A);
  transition:width .25s linear; }
/* o TEMPO QUE FALTA em vez de girador: a varredura é presa ao tempo real
   (R1 medido) e esconder isso seria mentir para o aluno por dois minutos. */
.mp-vmt-falta{ font-size:13px; font-weight:700; color:#F6F1E7;
  font-variant-numeric:tabular-nums; min-height:17px; }
.mp-vmt-amostra{ display:flex; gap:10px; justify-content:center; }
.mp-vmt-amostra > span{ width:34px; height:34px; border-radius:9px;
  border:1px solid rgba(255,255,255,.22); background:transparent; }
[data-mp-vmt-cores-img]{ cursor:crosshair; }

/* [17/08 · conserto] controle de OITAVA na calibração. A oitava é o único
   número que a geometria não consegue determinar (o Synthesia dá zoom), então
   ela vira decisão do aluno — com os nomes desenhados na prévia ao lado. */
/* [23/08] os três pedaços (−, o intervalo, +) viram UM controle: a moldura
   comum é o que diz que mexer num muda o outro. `align-self:center` porque o
   cartão é `flex-direction:column` — sem isso a pílula esticaria de ponta a
   ponta e o "centralizado" seria só o texto dentro dela. */
.mp-vmt-oit{ display:flex; align-items:center; justify-content:center; gap:0;
  align-self:center; padding:5px; border-radius:15px;
  background:rgba(246,241,231,.045);
  border:1px solid rgba(246,241,231,.08); }
.mp-vmt-oit > button{ width:40px; height:40px; border-radius:11px; font-size:20px;
  font-weight:800; color:#F6F1E7; background:rgba(255,255,255,.07);
  border:1px solid rgba(255,255,255,.14); }
.mp-vmt-oit > button:active{ background:rgba(255,255,255,.14); }
.mp-vmt-oit > span{ min-width:118px; font:700 14.5px/1 monospace; color:#F6F1E7;
  font-variant-numeric:tabular-nums; }
.mp-vmt-oit-dica{ font-size:12px; line-height:1.5; opacity:.75;
  padding:0 4px; }

/* ══════════════════════════════════════════════════════════════════════════
   [22/08 · TELA DE ESPERA do transcritor de notas caindo] .mp-esp-*

   A espera desta porta é a mais longa do app inteiro: a varredura é presa ao
   tempo real do vídeo, então 4 min de vídeo custam ~2 min de tela parada. Ela
   deixou de ser um cartãozinho com uma barra e virou uma TELA — anel de
   progresso no meio, número real, tempo que falta e uma dica de cada vez.

   🔴 A REGRA QUE MANDA AQUI NÃO É ESTÉTICA, É ORÇAMENTO DE QUADRO.
   Enquanto esta tela está no ar, o motor faz UM drawImage + UM getImageData
   por quadro de vídeo e a guarda do `avalia()` REPROVA a leitura se a mediana
   das difs de mediaTime passar de 45 ms (tt-vmt: vigiaLento derruba para
   playbackRate 0,5). Ou seja: pixel gasto nesta tela sai do orçamento da
   TRANSCRIÇÃO — enfeite caro aqui não deixa a espera feia, deixa a partitura
   errada. Por isso, e cada uma destas é deliberada:
     · ZERO animação contínua. Nada de girador, nada de brilho correndo, nada
       de @keyframes rodando o tempo todo. O que se mexe é o que MUDOU de
       verdade: o número, o anel, o tempo que falta e a troca de dica.
       (A única exceção é o pulso da fase FINAL — ver .is-fim lá embaixo —,
       que só existe depois que a varredura ACABOU.)
     · ZERO backdrop-filter. O `.mp-recusa` tem blur(2px) de tela cheia, que é
       recomposição a cada quadro; esta tela é OPACA e o blur nem chega a ser
       pedido. Ela cobre a tabbar (z 260 > 200) de propósito: sair da tela
       durante a leitura mata a leitura.
     · O anel anda por `stroke-dashoffset` (pintura de uma faixa fina) e o
       teclado por `clip-path: inset()` (composição), NUNCA por `width`, que
       obrigaria layout. E quem escreve é um relógio de ~6 Hz, não os 30 Hz do
       `onProgresso` — ver __mpEspPinta no index.
   ══════════════════════════════════════════════════════════════════════════ */
.mp-esp{
  position:fixed; inset:0; z-index:260;
  display:flex; align-items:center; justify-content:center;
  /* o brilho da marca é um gradiente ESTÁTICO: pinta uma vez, custa zero
     depois. É ele que dá profundidade sem ligar um único quadro de animação. */
  background:
    radial-gradient(118% 62% at 50% 20%, rgba(232,105,10,.15), rgba(8,8,10,0) 60%),
    radial-gradient(90% 55% at 50% 108%, rgba(232,179,74,.07), rgba(8,8,10,0) 62%),
    linear-gradient(180deg, rgba(8,8,10,.72), rgba(8,8,10,.58) 46%, rgba(8,8,10,.8));
  padding:
    max(18px, env(safe-area-inset-top))
    max(18px, var(--sar, 0px), env(safe-area-inset-right))
    max(18px, env(safe-area-inset-bottom))
    max(18px, var(--sal, 0px), env(safe-area-inset-left));
  overflow-y:auto;
}
.mp-esp[hidden]{ display:none; }
.mp-esp-in{
  display:flex; flex-direction:column; align-items:center;
  gap:clamp(12px, 2.4vh, 22px);
  width:min(420px, 100%); margin:auto; text-align:center;
}
/* tudo o que NÃO é o anel mora aqui: é este bloco que vira a 2ª coluna quando
   o celular deita (ver o @media do fim). */
.mp-esp-lado{
  display:flex; flex-direction:column; align-items:center;
  gap:clamp(10px, 1.9vh, 17px); width:100%; min-width:0;
}

/* ---- o ANEL ------------------------------------------------------------
   O tamanho sai de três limites ao mesmo tempo (px, largura e ALTURA), porque
   o aparelho apertado desta tela não é o estreito, é o BAIXO: num 320×568 com
   dica e botão, o anel preso só à largura empurrava o rodapé para fora. */
.mp-esp-anel{
  position:relative;
  width:min(212px, 53vw, 26.5vh); height:min(212px, 53vw, 26.5vh);
  display:flex; flex-direction:column; align-items:center; justify-content:center;
}
.mp-esp-svg{ position:absolute; inset:0; width:100%; height:100%;
  transform:rotate(-90deg); }          /* a volta começa às 12h, não às 3h */
.mp-esp-trilho{ fill:none; stroke:rgba(246,241,231,.085); stroke-width:9; }
/* o HALO é o mesmo arco, mais grosso e quase transparente: dá o "aceso" de um
   glow sem filter: blur(), que é passe de composição por quadro. */
.mp-esp-halo{ fill:none; stroke:url(#mpEspGrad); stroke-width:18; opacity:.13;
  stroke-linecap:round; }
.mp-esp-arco{ fill:none; stroke:url(#mpEspGrad); stroke-width:9;
  stroke-linecap:round; }
/* 🔴 SEM TRANSITION NO ANEL, E ISTO FOI MEDIDO, NÃO SUPOSTO.
   A 1ª versão tinha `transition:stroke-dashoffset .16s` aqui e `.16s` no
   clip-path do teclado — o que, com o pintor escrevendo a cada 160 ms,
   encadeia uma transição na seguinte e vira ANIMAÇÃO CONTÍNUA a 60 Hz em dois
   traços SVG e num recorte, exatamente o que o cabeçalho deste bloco proíbe.
   Medido na corrida com dan.mp4 (87,6 s, 2.560 quadros): 72 tarefas longas /
   7.790 ms contra 32 / 4.052 ms do ar. Sem as transições, o passo do anel é de
   0,33° por escrita — invisível — e o custo volta para o do ar. */

.mp-esp-num{
  font-family:var(--font-h); font-weight:800; color:#F6F1E7;
  font-size:calc(min(212px, 53vw, 26.5vh) * .30); line-height:1;
  letter-spacing:-.02em; font-variant-numeric:tabular-nums;
  display:flex; align-items:baseline; gap:1px;
}
/* o "%" não compete com o número: metade do corpo, cor de apoio */
.mp-esp-num > i{ font-style:normal; font-size:.42em; font-weight:800;
  color:rgba(246,241,231,.5); }
/* fase + tempo formam UM bloco (gap curto entre eles, gap normal para o
   resto): são as duas metades da mesma frase — o que está acontecendo e
   quanto ainda dura. */
.mp-esp-cab{ display:flex; flex-direction:column; align-items:center; gap:5px;
  width:100%; }
.mp-esp-fase{
  max-width:100%;
  font-size:10.5px; font-weight:800; text-transform:uppercase;
  letter-spacing:.13em; line-height:1.4;
  color:rgba(246,241,231,.52);
}

/* ---- o TEMPO QUE FALTA -------------------------------------------------
   Fica FORA do anel de propósito: dentro, ele disputava com o número, e o
   número é a resposta imediata ("quanto já foi"); o tempo é a segunda leitura
   ("quanto ainda dura"). `min-height` fixa a linha para a tela não pular
   quando o texto some na fase final. */
.mp-esp-falta{
  min-height:19px; font-size:14.5px; font-weight:700; color:#F6F1E7;
  font-variant-numeric:tabular-nums; letter-spacing:.01em;
}

/* ---- o TECLADO que enche ----------------------------------------------
   A identidade do app é tecla acesa — é assim que a prática, a fita e o
   editor falam. Aqui ele é a segunda leitura do MESMO número: o que acendeu
   já foi lido. Duas camadas idênticas; a de cima é recortada por clip-path
   (composição pura, sem layout). */
.mp-esp-kbd{ position:relative;
  width:100%; height:clamp(20px, 2.7vh, 26px);
  border-radius:2px 2px 6px 6px; overflow:hidden; }
.mp-esp-kbd svg{ position:absolute; inset:0; width:100%; height:100%; display:block; }
.mp-esp-kbd .b-off{ fill:rgba(246,241,231,.17); }
.mp-esp-kbd .p-off{ fill:#08080A; }
.mp-esp-kbd-on{ position:absolute; inset:0;
  clip-path:inset(0 calc(100% - var(--esp-p, 0%)) 0 0); }
/* a parte ACESA é a marca, mas em voz baixa: cheia demais ela virava um bloco
   laranja que roubava o anel — e o anel é quem responde a pergunta. */
.mp-esp-kbd .b-on{ fill:url(#mpEspGrad); opacity:.82; }
.mp-esp-kbd .p-on{ fill:#140A02; }

/* ---- a DICA ------------------------------------------------------------
   Uma por vez, trocada pelo relógio do __mpEspDicas. A altura mínima segura a
   caixa: sem ela, uma dica de 2 linhas seguida de uma de 4 fazia a tela
   inteira saltar a cada troca. */
/* 🔴 `[hidden]{display:none}` do navegador é 0,0,1 e PERDE para `.mp-esp-dica`
   (0,1,0): sem esta linha, esconder a dica na calibração deixava o cartão
   VAZIO na tela — foi exatamente o que a foto da fase 1 pegou. */
.mp-esp-dica[hidden]{ display:none; }
.mp-esp-dica{
  position:relative; width:100%; text-align:center;
  display:flex; flex-direction:column; justify-content:center;
  min-height:104px;              /* cabe a dica mais longa das oito línguas */
  padding:2px 4px;
  /* 🔴 SEM cartão, SEM borda, SEM barrinha e SEM etiqueta — e agora SEM
     TRANSIÇÃO NENHUMA (ordem do Luis, 22/08: "remova cada coisa que tiver
     custo... sem nenhum tipo de animacao"). A troca de frase é INSTANTÂNEA:
     um `textContent` a cada 7 s, que é o evento mais barato que existe nesta
     tela. Aqui não sobrou nada para o compositor animar. */
  background:none; border:0;
}

/* ---- 🔴 A FLUTUAÇÃO LETRA A LETRA MORREU (ordem do Luis, 22/08) --------
   Ela existiu por meia tarde e foi REPROVADA PELA MEDIDA. Fica registrada
   aqui porque o número é a lição, e a lição vale para a próxima ideia bonita
   que alguém tiver nesta tela.

   Eu escrevi, com todas as letras, que ela era segura "porque é de COMPOSITOR
   PURO: transform:translateY e mais nada". Estava errado. O preço de uma
   animação não está no TIPO da propriedade — está em QUANTAS CAMADAS o
   compositor tem de compor por quadro. Uma dica tem ~100 letras; ~100 camadas
   vivas ao lado de um drawImage + getImageData de 1920×97 por quadro de vídeo
   atrasam a sincronização GPU→CPU, que é o gargalo desta rota.

   MEDIDO (`custo_espera.cjs`, 6 condições × 4 repetições INTERCALADAS, cada
   uma contando quantas leituras de 1920×97 o motor consegue em 12 s):
       tela limpa (sem espera) .... 614 leituras · quadro p95 41,0 ms · 21 tarefas longas
       ar (staging-143) ........... 714 leituras · quadro p95 18,3 ms ·  6
       com a flutuação ............ 595 leituras · quadro p95 41,1 ms · 21   ⇒ −16,7%
       SEM a flutuação ............ 707 leituras · quadro p95 20,0 ms ·  5   ⇒  −1,0%
       fundo opaco (flutuação on) . 583 leituras · quadro p95 50,6 ms · 25
   Bisseção limpa: a flutuação era o custo INTEIRO da tela nova, e ela
   TRIPLICAVA o quadro p95 — que é exatamente o que o `quadrosPulados` do
   `avalia()` conta (difs > 1,5× a mediana). O véu translúcido que deixa o céu
   estrelado atravessar custa ~nada (583 contra 595 é ruído).

   🔑 E o que valida a régua: a tela LIMPA (614) é PIOR que a espera do ar
   (714), porque sem a espera de pé a constelação não congela e roda seu laço
   O(N²) a 60 Hz. Esta tela AJUDA a transcrição — desde que não anime nada.

   O que sobrou no lugar: a frase em Montserrat, parada. Ver o bloco abaixo.
   🔴 Quem quiser trazer movimento de volta roda o `custo_espera.cjs` ANTES e
   mostra o número. ---------------------------------------------------------- */

/* 🔴 OS DOIS <p> DESTA TELA PRECISAM DE ESPECIFICIDADE 0,2,0, e isto não é
   preciosismo: a página da folha tem `class="special"`, e o app já traz
   `.special p{ color:var(--text); font-size:14px; margin-bottom:12px }` e
   `.special p,.special li{ font-size:13.5px }` — 0,1,1, que GANHA de um
   `.mp-esp-aviso` sozinho. Medido: o aviso saía 13,5px BRANCO em vez de 11px
   a 36%, e a dica ganhava 12px de margem embaixo. Os outros elementos desta
   tela são div/span/button e não têm intruso nenhum (conferido um a um pelo
   CSS.getMatchedStylesForNode). */
.mp-esp .mp-esp-dica-txt{
  /* [22/08 · Luis] "deixe a dica de piano em montserrat (sem escrito dica de
     piano) sem nenhum tipo de animacao". Montserrat é o `--font-h` do app.
     🔴 O `text-shadow` saiu junto: sombra de texto é passe de pintura extra a
     cada vez que a frase troca, e a ordem foi remover TUDO que tem custo. A
     legibilidade sobre o céu vem do véu do fundo, que já é opaco o bastante
     (medido: o véu custa ~nada). */
  margin:0; font-family:var(--font-h);
  font-size:14.5px; line-height:1.6; font-weight:600;
  letter-spacing:.005em; color:rgba(246,241,231,.9);
}

/* ---- rodapé: o aviso e a saída ---------------------------------------- */
.mp-esp .mp-esp-aviso{
  margin:0; max-width:33ch;
  font-size:11px; line-height:1.45; font-weight:500;
  color:rgba(246,241,231,.36);
}
.mp-esp-cancel{
  padding:11px 26px; border-radius:12px;
  font-size:13px; font-weight:700; color:rgba(246,241,231,.62);
  background:transparent; border:1px solid rgba(246,241,231,.16);
}
.mp-esp-cancel:active{ background:rgba(246,241,231,.06); }

/* ---- a FASE FINAL ------------------------------------------------------
   Depois de `ended` a varredura ACABOU: o vídeo não roda mais, nenhum quadro
   está sendo lido e o orçamento voltou a ser meu. Só aqui existe animação
   contínua — e ela existe porque nesta fase o número REALMENTE não tem o que
   dizer (montar o RI e escrever a pauta não têm fração medível). O anel fica
   nos 100% que ele de fato alcançou e quem respira é o rótulo, que é o que
   está falando a verdade do momento. */
@keyframes mpEspRespira{ 0%,100%{ opacity:.4 } 50%{ opacity:.92 } }
.mp-esp.is-fim .mp-esp-fase{ animation:mpEspRespira 1.7s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce){
  .mp-esp.is-fim .mp-esp-fase{ animation:none; opacity:.75; }
}

/* ---- tela BAIXA --------------------------------------------------------
   Medido nas cinco réguas (320×568, 375×667, 390×844, 430×932, 1280×800): o
   320×568 é o piso — ali a dica ainda entra porque o anel já encolheu pelo
   limite de ALTURA (26,5vh). Abaixo de ~540px de altura útil ela não entra
   mais sem espremer o rodapé, e então sai: o número, o tempo e a saída são o
   que a tela DEVE. */
@media (max-height: 539px){
  .mp-esp-dica{ display:none; }
  .mp-esp-in{ gap:12px; }
}
/* na horizontal do celular (baixa e larga) o empilhamento vira duas colunas:
   anel de um lado, texto do outro — senão nada cabe. */
@media (max-height: 500px) and (min-width: 640px){
  .mp-esp-in{ flex-direction:row; width:min(760px,100%); gap:30px;
    align-items:center; text-align:left; }
  .mp-esp-anel{ flex:0 0 auto; width:min(180px,40vh); height:min(180px,40vh); }
  .mp-esp-num{ font-size:calc(min(180px,40vh) * .295); }
  .mp-esp-lado{ align-items:flex-start; gap:12px; flex:1 1 auto; }
  .mp-esp-cab{ align-items:flex-start; }
  /* deitado o cartão é largo: a altura mínima que segura o salto no retrato
     aqui só deixaria um vão embaixo da dica. */
  .mp-esp-dica{ display:flex; min-height:0; text-align:left; }
  .mp-esp-aviso{ max-width:none; }
}
/* ---- 🔴 O CÉU FICA, OS ELEMENTOS SOMEM ---------------------------------
   Ordem do Luis, ao pé da letra: "o fundo estrelado se mantém e os ELEMENTOS
   somem e aparecem na transição entre páginas".

   O véu translúcido sozinho NÃO faz isso. Ele deixa passar tudo o que está
   atrás — e atrás não há só o céu: há a página. Na primeira foto desta tela
   dava para LER "Minhas Partituras", "Nova partitura" e "Importar arquivo"
   por trás do anel de progresso. 🔑 Nenhuma asserção minha pegou isso: a
   suíte cobrava "o fundo não é opaco", e ele não era. Quem pegou foi a FOTO.
   (`feedback_registro_nao_prova_aparencia`, mais uma vez.)

   Quem some são os IRMÃOS da tela cheia, dentro da própria página, mais a
   tabbar. O céu mora num canvas ATRÁS de tudo (`#c`, z-index:-1) e por isso
   não é alcançado por nenhuma destas regras — ele continua lá, congelado.

   🔑 `visibility:hidden` e NÃO `display:none`, e a diferença é de orçamento:
   `display` refaria o layout da página inteira ao acender E ao apagar a
   espera — dois reflows de um DOM de 1,7 MB no exato instante em que a
   varredura começa. Com `visibility` o layout fica de pé e o navegador só
   PULA a pintura desses ramos: é trabalho a MENOS por quadro, não a mais.

   🔴 As telas cheias precisam voltar a `visible` explicitamente: `visibility`
   é herdada, e elas são FILHAS da página que acabou de ser escondida. */
body.mp-ceu-on .page[data-page="mp-folha"] > *{ visibility:hidden; }
body.mp-ceu-on .page[data-page="mp-folha"] > .mp-esp,
body.mp-ceu-on .page[data-page="mp-folha"] > .mp-cort{ visibility:visible; }
body.mp-ceu-on .tabbar{ visibility:hidden; }

/* ═══════════ FIM DO BLOCO DA ESPERA ═══════════
   (sentinela: a suíte lê daqui para trás para cobrar a lei do orçamento de
    quadro só DENTRO deste bloco — sem ela, a varredura por `transition`
    pegaria o app inteiro e daria vermelho por CSS que não é meu.) */

/* ══════════════════════════════════════════════════════════════════════════
   [22/08 · Luis] O GUIA e o EDITOR DE CORTE da 6ª porta.

   Ordem textual: "ao selecionar video, abrir editor de video simples que pode
   cortar as 2 pontas do video (estilo editor instagram stories), antes de
   abrir essa tela, pop up instrutivo que informa que o inicio do video tem que
   estar tela ja na tela de piano tela cheia, cortando a parte do video que
   navega ate o youtube, clica no video, pula anuncio."

   🔑 POR QUE O CORTE É FUNCIONAL, NÃO ENFEITE: a calibração tira 60 amostras
   distribuídas pelo vídeo INTEIRO. Se os primeiros 20 s são a interface do
   YouTube e um anúncio, essas amostras entram no pente de periodicidade como
   se fossem teclado — e a varredura ainda paga o tempo real desses 20 s, que
   é o recurso mais caro desta rota. Cortar as duas pontas é, ao mesmo tempo,
   a cura da geometria e a economia do relógio.

   O fundo destas duas telas é o MESMO véu translúcido da espera: o céu
   estrelado do app nunca é apagado por troca de tela (ordem do Luis).
   ══════════════════════════════════════════════════════════════════════════ */

/* ---- o GUIA: 3 linhas, nada mais -------------------------------------- */
.mp-vmt-guia-lista{ display:flex; flex-direction:column; gap:9px;
  text-align:left; margin:2px 0 3px; }
/* 🔑 "TUDO ALINHADO E SIMETRICAMENTE BEM DIVIDIDO": cada ideia ganha a MESMA
   caixa, com o mesmo respiro dos quatro lados. Antes eram três textos soltos
   de alturas diferentes encostados numa coluna de ícones — alinhados no papel,
   desalinhados no olho. Três blocos iguais é o que faz a divisão aparecer.
   O ícone passa a centrar no eixo do bloco (era `flex-start` + um empurrão de
   3 px no texto): com uma, duas ou três linhas, ele fica sempre no meio. */
.mp-vmt-guia-lin{ display:flex; gap:12px; align-items:center;
  padding:12px 13px; border-radius:13px;
  background:rgba(246,241,231,.045);
  border:1px solid rgba(246,241,231,.075); }
/* a 1ª linha é o que o vídeo TEM DE SER; as outras duas são o que SAI FORA.
   Essa diferença já vivia no ícone — agora ela também é a moldura, que é o
   que o olho lê antes de ler a palavra. */
.mp-vmt-guia-lin:first-child{ background:rgba(232,105,10,.075);
  border-color:rgba(232,105,10,.24); }
.mp-vmt-guia-ic{ flex:0 0 auto; width:32px; height:32px; border-radius:10px;
  display:flex; align-items:center; justify-content:center; font-size:15px;
  line-height:1; background:rgba(232,105,10,.18);
  border:1px solid rgba(232,105,10,.45); }
.mp-vmt-guia-lin.is-nao .mp-vmt-guia-ic{ background:rgba(246,241,231,.07);
  border-color:rgba(246,241,231,.17); }
.mp-vmt-guia-tx{ margin:0; padding:0; font-size:13.5px; line-height:1.5;
  color:rgba(246,241,231,.88); }
.mp-vmt-guia-tx b{ color:#FF9040; font-weight:800; }

/* ---- o EDITOR DE CORTE ------------------------------------------------- */
.mp-cort{
  position:fixed; inset:0; z-index:262;
  display:flex; flex-direction:column; align-items:center;
  gap:clamp(9px, 1.8vh, 16px);
  background:
    radial-gradient(118% 62% at 50% 18%, rgba(232,105,10,.14), rgba(8,8,10,0) 60%),
    linear-gradient(180deg, rgba(8,8,10,.74), rgba(8,8,10,.6) 46%, rgba(8,8,10,.82));
  /* 🔴 [23/08 · Luis] 16 → 30 px NAS LATERAIS, e isto é CONSERTO, não gosto.
     A alça de início mora na ponta esquerda da fita; com 16 px de padding ela
     nascia dentro da faixa em que o navegador escuta o GESTO DE VOLTAR, e o
     dedo que ia pegá-la pegava o gesto: a tela inteira deslizava para a
     direita e entrava a página anterior, vazia. Empurrar o conteúdo para
     dentro é a metade estrutural da cura — a outra metade é o
     `__mpVmtBorda` no index, que come o toque que ainda começar na faixa.
     O preço, declarado: 28 px a menos de palco a cada lado. */
  padding:
    max(14px, env(safe-area-inset-top))
    max(30px, var(--sar, 0px), env(safe-area-inset-right))
    max(14px, env(safe-area-inset-bottom))
    max(30px, var(--sal, 0px), env(safe-area-inset-left));
  /* na horizontal do celular a altura útil cai para ~330 px e o palco tem
     piso de 110: sem isto o botão de confirmar sairia da tela. Mesma saída
     que a .mp-esp já usa. */
  overflow-y:auto;
}
.mp-cort[hidden]{ display:none; }
.mp-cort-in{ display:flex; flex-direction:column; align-items:stretch;
  gap:clamp(9px, 1.8vh, 16px); width:min(460px,100%); height:100%; margin:auto; }
.mp-cort-top{ display:flex; align-items:center; gap:10px; flex:0 0 auto; }
.mp-cort-x{ width:34px; height:34px; border-radius:10px; flex:0 0 auto;
  font-size:19px; font-weight:700; color:rgba(246,241,231,.72);
  background:rgba(246,241,231,.07); border:1px solid rgba(246,241,231,.14); }
/* o título CENTRADO: `flex:1` sozinho o centraria no espaço que sobra depois
   do ×, ou seja 17 px à direita do meio. O vão invisível do outro lado tem a
   largura exata do × — é o que faz o meio do texto cair no meio da tela sem
   tirar o título do fluxo (posicionar absoluto deixaria a barra sem altura). */
.mp-cort-tit{ flex:1 1 auto; min-width:0; text-align:center;
  font-family:var(--font-h); font-size:clamp(15px,4vw,17px); font-weight:800;
  color:#F6F1E7; letter-spacing:-.1px; }
.mp-cort-esp{ flex:0 0 auto; width:34px; height:34px; }
/* [31/08 · AND-3d] o AVISO da gravação de tela: a mesma laranja do guia
   (232,105,10 / #FF9040), mas em MOLDURA cheia — fundo, borda, halo e peso 800.
   É o único bloco colorido da tela de corte de propósito: o olho cai nele
   antes de cair nas alças. */
.mp-cort .mp-cort-aviso{ margin:0; flex:0 0 auto; display:flex; align-items:center;
  gap:11px; padding:11px 13px; border-radius:13px; text-align:left;
  background:rgba(232,105,10,.17); border:1.5px solid rgba(255,144,64,.78);
  box-shadow:0 0 0 3px rgba(232,105,10,.12), 0 6px 18px rgba(0,0,0,.25);
  color:#FFB27A; font-size:clamp(13px,3.6vw,15px); line-height:1.35; font-weight:800;
  letter-spacing:-.1px; }
.mp-cort-aviso-ic{ flex:0 0 auto; width:30px; height:30px; border-radius:9px;
  display:flex; align-items:center; justify-content:center; font-size:15px;
  line-height:1; color:#F6F1E7;
  background:rgba(232,105,10,.30); border:1px solid rgba(232,105,10,.62); }
.mp-cort-aviso-tx{ flex:1 1 auto; min-width:0; }

/* o PALCO ocupa o que sobra: é o único elemento elástico da tela.
   🔴 A MOLDURA ABRAÇA O VÍDEO, NÃO A CAIXA. A primeira versão pintava fundo e
   borda no palco — e como o palco é elástico e o vídeo é 16:9, sobrava um
   retângulo preto enorme em cima e embaixo do quadro, com a borda desenhada
   no vazio. A foto mostrou; nenhuma asserção mostraria. Agora o palco é só o
   ESPAÇO (transparente, deixa o céu passar) e quem tem moldura, cantos e
   fundo é o <video>. Serve igual para vídeo deitado e para gravação de tela
   de celular, que é retrato. */
.mp-cort-palco{ position:relative; flex:1 1 auto; min-height:110px;
  background:none; border:0;
  display:flex; align-items:center; justify-content:center; }
.mp-cort-palco video{ display:block; max-width:100%; max-height:100%;
  border-radius:12px; background:#000;
  border:1px solid rgba(246,241,231,.12);
  box-shadow:0 10px 34px rgba(0,0,0,.45); }
.mp-cort-play{ position:absolute; inset:0; width:100%; height:100%;
  background:transparent; border:0; display:flex;
  align-items:center; justify-content:center; }
.mp-cort-play > i{ font-style:normal; width:54px; height:54px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-size:20px; color:#08080A; background:rgba(246,241,231,.9);
  box-shadow:0 6px 20px rgba(0,0,0,.45); }
.mp-cort.is-tocando .mp-cort-play > i{ opacity:0; }

/* os TRÊS NÚMEROS: começo, duração escolhida, fim */
.mp-cort-tempos{ display:flex; align-items:baseline; justify-content:space-between;
  gap:10px; flex:0 0 auto;
  font-variant-numeric:tabular-nums; font-size:12.5px; font-weight:700;
  color:rgba(246,241,231,.55); }
.mp-cort-tempos > b{ font-size:15px; color:#F6F1E7; font-weight:800; }
.mp-cort-tempos > b.is-teto{ color:#E8B34A; }

/* a FITA de miniaturas com a janela arrastável (estilo stories) */
.mp-cort-fita{ position:relative; flex:0 0 auto;
  width:100%; height:clamp(52px, 8.4vh, 64px);
  border-radius:11px; overflow:hidden;
  background:rgba(246,241,231,.055);
  border:1px solid rgba(246,241,231,.1);
  touch-action:none; user-select:none; -webkit-user-select:none; }
.mp-cort-fita canvas{ position:absolute; inset:0; width:100%; height:100%;
  display:block; }
.mp-cort-som{ position:absolute; top:0; bottom:0; background:rgba(8,8,10,.72);
  pointer-events:none; }
.mp-cort-jan{ position:absolute; top:0; bottom:0; pointer-events:none;
  border-top:2px solid #FF7B1A; border-bottom:2px solid #FF7B1A; }
.mp-cort-alca{ position:absolute; top:-2px; bottom:-2px; width:19px;
  background:#FF7B1A; display:flex; align-items:center; justify-content:center; }
.mp-cort-alca.is-esq{ left:0;  border-radius:9px 0 0 9px; }
.mp-cort-alca.is-dir{ right:0; border-radius:0 9px 9px 0; }
.mp-cort-alca::before{ content:''; width:2px; height:17px; border-radius:2px;
  background:rgba(8,8,10,.55); }
.mp-cort-agulha{ position:absolute; top:0; bottom:0; width:2px;
  background:#F6F1E7; box-shadow:0 0 6px rgba(0,0,0,.6);
  opacity:0; pointer-events:none; }
.mp-cort.is-tocando .mp-cort-agulha{ opacity:.95; }

.mp-cort .mp-cort-dica{ margin:0; flex:0 0 auto; text-align:center;
  font-size:11.5px; line-height:1.45; font-weight:500;
  color:rgba(246,241,231,.42); }
.mp-cort-acts{ display:flex; gap:10px; flex:0 0 auto; }
.mp-cort-acts > button{ flex:1 1 auto; }

/* ---- o NOME DA MÚSICA (obrigatório) na calibração --------------------- */
.mp-vmt-nome{ display:flex; flex-direction:column; gap:7px; text-align:left;
  margin-top:1px; }
.mp-vmt-nome > label{ font-size:10.5px; font-weight:800; text-transform:uppercase;
  letter-spacing:.12em; color:rgba(246,241,231,.5); }
.mp-vmt-nome > input{ width:100%; box-sizing:border-box;
  padding:11px 13px; border-radius:11px;
  font-family:var(--font-b); font-size:15px; font-weight:600; color:#F6F1E7;
  background:rgba(246,241,231,.06);
  border:1px solid rgba(246,241,231,.16); }
.mp-vmt-nome > input::placeholder{ color:rgba(246,241,231,.3); font-weight:500; }
.mp-vmt-nome > input:focus{ outline:none; border-color:#FF7B1A;
  background:rgba(246,241,231,.09); }
/* 🔴 o botao de seguir NASCE desabilitado: o nome e obrigatorio (ordem do
   Luis). O estado desabilitado tem de ser VISIVEL, senao o aluno acha que o
   app travou em vez de entender que falta preencher. */
.mp-vmt-card .mp-pdf-act[disabled]{ opacity:.42; }


/* ═══════════════════════════════════════════════════════════════════════════
   [28/08 · LOTE 27 · GRUPO B · Luis] casca, cards de ação e piano roll
   ═══════════════════════════════════════════════════════════════════════════
   🔴 ESTE BLOCO MORA NO FIM DO ARQUIVO DE PROPÓSITO. As peças abaixo REESCREVEM
   regras da pele do app (.mp-folha-pauta, .fe-bar, .tabbar-label, .sc-lesson) e
   a pele é escrita com dois ou três seletores. Regra nova de UMA classe perde
   por especificidade, calada — foi assim que o pop-up do editor "não estava
   Montserrat". Aqui cada regra ou empata em especificidade e vence pela ORDEM,
   ou declara a própria font-family em vez de herdar.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---------------------------------------------------------------------------
   [04] UMA PARTITURA, UM SÓ BALÃO BRANCO
   ---------------------------------------------------------------------------
   "cada pauta fica em um balao branco separado, com fundo preto atras. faca com
   que todas as pautas e toda a partitura saia em um unico balao branco sem
   separacoes."

   O preto entre as pautas era o fundo da PÁGINA aparecendo pelo `gap:12px`; a
   moldura era o `border` de cada botão. Então: some o vão, some a moldura, e o
   papel sobe um nível — quem tem fundo claro passa a ser o container.

   🔑 Cada pauta continua sendo um <button> inteiro, com o número e o alvo de
   toque da linha toda. O que ela perde é a CASCA, não o clique: por isso o
   estado de pressão não sumiu, mudou de linguagem (era `scale` + borda laranja,
   agora é um lavado laranja dentro do papel — encolher uma fatia de um balão
   contínuo deixaria um buraco).
   ------------------------------------------------------------------------ */
.mp-folha-pautas{ gap:0; padding:6px 0 8px; margin-bottom:12px;
  background:var(--mp-paper,#F6F1E7); border-radius:16px; overflow:hidden;
  box-shadow:0 14px 40px rgba(0,0,0,.45), 0 0 0 1px rgba(255,255,255,.07); }
.mp-folha-pautas .mp-folha-pauta{ background:transparent; border:0; border-radius:0;
  padding:5px 12px; transition:background .16s; }
.mp-folha-pautas .mp-folha-pauta:active{ transform:none; background:rgba(232,105,10,.14); }
@media (hover:hover) and (pointer:fine){
  .mp-folha-pautas .mp-folha-pauta:hover{ background:rgba(232,105,10,.08); }
}

/* ---------------------------------------------------------------------------
   [08] BARRA ÚNICA NO PIANO ROLL — 🗄️ RESOLVIDO POR ESTRUTURA em 13/09
   ---------------------------------------------------------------------------
   O que este item pedia (*"a primeira linha da tela contem apenas a nav tag
   retroceder… vamos somar a altura das 2 linhas, e fazer uma unica linha com
   botoes bem maiores"*) era o conserto de um problema que a EDIÇÃO NA FITA
   apagou: o editor de tela própria tinha barra própria, e por isso havia duas
   linhas — a `.sc-top` (só com o voltar) e a `.pr-bar`.

   Desde 13/09 a edição não tem tela: a `.fe-bar` VIVE DENTRO da `.sc-top`, no
   lugar exato da `.fr-bar`, ao lado do voltar. Há uma linha só porque nunca
   houve duas — não porque se somou a altura de duas. As 30 regras que este
   item tinha (esconder a `.sc-top`, esticar a `.pr-bar` para 106px, a escada de
   compressão de 1100px para caber nos 667 do SE deitado) saíram com o alvo
   delas; o que ficou de vivo desta lição é o que a `.fe-bar` já herda da
   `.fr-bar`: uma linha, que rola, com a escada de compressão em 800/1000px.
   ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
   [10] "INVERSÕES INTELIGENTES" NUMA BARRA DE CINCO ABAS
   ---------------------------------------------------------------------------
   MEDIDO: com `flex:1 1 0` o slot é a largura da tela dividida por 5 — 62px no
   iPhone SE — e o rótulo pede ~145px numa linha. Havia duas saídas óbvias e as
   duas são ruins: encolher a fonte (precisaria de ~6px, ilegível) ou abreviar
   (o Luis proibiu). A terceira é LAYOUT, e é esta:

    · `flex:1 1 auto` — a base do flex passa a ser o CONTEÚDO. A aba longa pede
      mais largura e as vizinhas cedem proporcionalmente, em vez de todas as
      cinco insistirem em ser iguais enquanto uma trunca;
    · `max-width` no rótulo — é ele que FORÇA a quebra no espaço, em vez de
      deixar a aba ficar três vezes mais larga que as outras;
    · duas linhas, com `-webkit-line-clamp:2` como última defesa (o
      `text-overflow:ellipsis` não vale em texto de várias linhas).

   No desktop a barra é uma coluna de 88px e o rótulo desce de 10px para 9px:
   "INTELIGENTES" é uma palavra só, não tem onde quebrar, e a 10px ela mede
   ~84px contra os 84px úteis da coluna — empate é truncar.
   ------------------------------------------------------------------------ */
.tabbar-item{ flex:1 1 auto; }
.tabbar-label{ white-space:normal; word-break:normal; overflow-wrap:normal;
  line-height:1.05; max-width:84px;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; }
html.dt .tabbar-label{ font-size:9px; letter-spacing:.02em; max-width:100%; }

/* 🗑️ [16/09 · Luis] AQUI MORAVA O BLOCO [12] — "BANHO DE ELEGÂNCIA NOS QUATRO
   CARDS DE AÇÃO", 122 linhas, pedido por ele em 29/08 com estas palavras:
     *"os cards nova partitura, importar arquivo, escanear partitura e enviar
       video notas caindo devem tomar um banho de elegancia, cada um com sua
       propria cor, contorno neon, faca do seu jeito."*
   Tinha quatro acentos (laranja = a música nasce aqui · azul = um ficheiro que
   já existe · violeta = a foto de um papel · verde = vídeo), o trilho aceso de
   3 px na esquerda com bloom, o selo âmbar "EM TESTE" do scanner (31/08) e os
   estados de toque e de rato.

   SAI INTEIRO por ordem de 16/09:
     *"transforme os botoes de entrada de musica (notas caindo, escanear, nova
       partitura etc) deixe eles exatamente iguais e seguindo a mesma logica dos
       botoes da pagina da musica (exportar pdf, midi, etc)."*
   As quatro portas passaram a ser `.mp-util-btn` dentro de `.mp-folha-util` —
   as MESMAS peças do painel da folha — e todo o seletor deste bloco (que era
   `.mp-hub .mp-item[data-mp-…]`) deixou de casar, porque elas já não são
   `.mp-item`. Fica a lápide e não o código: regra que não casa com nada não
   avisa quando envelhece, e a próxima pessoa a ler perguntaria porquê.
   🔑 O que a cor dizia — a FONTE da música — passou a ser dito pelo ícone e
   pelo rótulo; o selo do scanner mudou de forma mas não de existência (entrou
   entre parênteses no próprio rótulo, index.html:__mpListaRender).
   ⚠️ O `.mp-hub .mp-item-nativa` NÃO está aqui: a Für Elise é uma MÚSICA, e as
   músicas continuam cards, que é a outra metade da ordem dele. */

/* ---------------------------------------------------------------------------
   [06] O TERCEIRO CARD DA RAIZ — ENVIAR VÍDEO DE NOTAS CAINDO
   ---------------------------------------------------------------------------
   "Esse card sera verde, e bem maior, coloque uma descricao um pouco mais
   extensa explicando a funcao."

   BEM MAIOR é hierarquia, não ênfase solta: é o card que abre a porta que
   ninguém procura sozinho (quem tem um vídeo de Synthesia na mão não imagina
   que ele vira partitura).

   🔑 [29/08 · ordem do Luis] "apenas o card em si fica maior; tamanho da fonte
   permanece o mesmo; use o maior espaço para inserir uma descrição da função."
   Então o ÚNICO degrau é o RESPIRO (padding) — ícone, título e corpo voltaram
   ao tamanho exato dos outros dois cards, e quem ocupa o espaço ganho é a
   DESCRIÇÃO, não a tipografia. A borda de 2px segura o peso do bloco maior.
   O verde é o mesmo `37,224,138` dos cards de ação (tarefa 12).
   ------------------------------------------------------------------------ */
.ms-like .ms-card--vid{
  border-width:2px; border-color:rgba(37,224,138,.5);
  background:radial-gradient(ellipse 100% 130% at 18% 18%,rgba(37,224,138,.20) 0%,rgba(37,224,138,.06) 50%,transparent 74%),rgba(6,17,12,.6);
  box-shadow:0 18px 50px rgba(0,0,0,.55), 0 0 34px rgba(37,224,138,.13);
  padding:clamp(26px,6.2vw,38px) clamp(20px,5vw,30px);
}
.ms-like .ms-card--vid .ms-ic{ color:#25E08A; }   /* tamanho = o dos outros */
.ms-like .ms-card--vid .ms-card-title{ color:#25E08A; }   /* fonte = a dos outros */
.ms-like .ms-card--vid .ms-card-body{ color:rgba(255,255,255,.86); }   /* fonte = a dos outros */
@media (hover:hover) and (pointer:fine){
  .ms-like .ms-card--vid:hover{ transform:translateY(-3px); border-color:rgba(37,224,138,.8);
    box-shadow:0 22px 54px rgba(0,0,0,.6),0 0 40px rgba(37,224,138,.24); }
}
@media (min-width:900px){
  html.dt .sc-hub-page .ms-cards{ align-items:stretch; }
  html.dt .sc-hub-page .ms-card--vid{ flex:1.35 1 0; }
}

/* ===== [LOTE 3 · SUPORTE 30/08] modal de suporte / sugestao ================
   Reusa o vocabulario visual do modal de CONTA (mesma caixa, mesma borda, o
   mesmo laranja) de proposito: e a mesma classe de tela — algo que se abre por
   cima do app e se fecha. Classes proprias (sup-*) para nao mexer numa linha do
   modal de conta, que ja esta no ar. z-index UM acima do dele: a porta de hoje
   sai de dentro dele, entao o suporte tem de vir por cima. */
.sup-modal{position:fixed;inset:0;z-index:2147483501;display:none;align-items:center;justify-content:center;padding:20px}
.sup-modal[aria-hidden="false"]{display:flex}
.sup-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.62);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px)}
.sup-card{position:relative;width:min(400px,94vw);max-height:88vh;overflow:auto;background:var(--card);border:1px solid var(--border);border-radius:16px;padding:26px 22px 20px;box-shadow:0 20px 60px rgba(0,0,0,.5);text-align:left}
.sup-close{position:absolute;top:6px;right:10px;background:none;border:none;color:var(--muted);font-size:26px;line-height:1;cursor:pointer;padding:4px 8px}
.sup-close:hover{color:var(--text)}
.sup-title{font-family:var(--font-h);font-size:19px;margin:0 0 6px;color:var(--text);font-weight:700}
.sup-sub{font-size:13.5px;color:var(--muted);margin:0 0 14px;line-height:1.5}
.sup-tipos{display:flex;gap:8px;margin:0 0 12px}
.sup-tipo{flex:1;padding:9px 10px;border-radius:10px;background:transparent;color:var(--muted);border:1px solid var(--border2);font-family:var(--font-b);font-size:13.5px;font-weight:600;cursor:pointer;transition:border-color .15s,color .15s,background .15s}
.sup-tipo.is-on{color:var(--text);border-color:var(--primary);background:rgba(232,105,10,.10)}
.sup-input,.sup-msg{width:100%;box-sizing:border-box;padding:12px 14px;border-radius:12px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.06);color:var(--text);font-family:var(--font-b);font-size:16px;outline:none;transition:border-color .12s}
.sup-input::placeholder,.sup-msg::placeholder{color:var(--muted);opacity:.75}
.sup-input:focus,.sup-msg:focus{border-color:var(--primary)}
.sup-msg{margin-top:10px;min-height:120px;resize:vertical;line-height:1.5}
/* 🔴 O honeypot sai da TELA, nao do LAYOUT: display:none e o primeiro lugar
   onde um robo decente para de preencher. Continua preenchivel para ele e
   invisivel para o aluno; tabindex=-1 tira do caminho de quem usa teclado. */
.sup-hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;opacity:0;pointer-events:none}
.sup-send{margin-top:12px;width:100%;padding:12px 16px;border-radius:10px;border:0;cursor:pointer;background:linear-gradient(135deg,#F2790E 0%,#E8690A 55%,#C9550A 100%);color:#fff;font-family:var(--font-h);font-weight:800;font-size:14.5px;letter-spacing:.2px;box-shadow:0 8px 24px rgba(232,105,10,.35)}
.sup-send[disabled]{opacity:.55;cursor:default;box-shadow:none}
.sup-out{margin:10px 0 0;font-size:13.5px;color:#22C55E;text-align:center;line-height:1.45}
.sup-out.is-err{color:#F87171}
.sup-nota{margin:12px 0 0;font-size:11.5px;color:var(--muted);line-height:1.45;opacity:.85}
.account-sup-link{margin-top:14px;width:100%;padding:9px 12px;border-radius:10px;background:transparent;color:var(--muted);border:1px dashed var(--border2);font-family:var(--font-b);font-size:13px;font-weight:600;cursor:pointer;transition:border-color .15s,color .15s}
.account-sup-link:hover{border-color:var(--primary);color:var(--primary)}

/* [LOTE 3 · SUPORTE 30/08 · ordem do Luis] O ICONE DE CONTA NAS 5 TELAS
   PRINCIPAIS. Ele ja aparecia em songs (Inversoes), games e harmony; faltavam
   `toc` (Tecnica) e `score` (Partitura, que e a HOME do app). Motivo: a porta do
   suporte mora DENTRO do modal de conta, e suporte que so se acha em tres abas
   de cinco nao e suporte.
   🔑 Regra NOVA no fim do arquivo, em vez de editar a linha do ar: a guarda (h)
   do deploy exige que o app.css do payload COMECE com o do ar byte a byte. Mesma
   especificidade da regra que ja existe, e vem depois ⇒ ganha. Os dois `display:
   none!important` (html.intro-mode e body.mode-print) seguem ganhando desta. */
body[data-active-page="toc"] .account-btn,
body[data-active-page="score"] .account-btn{display:flex}

/* ==========================================================================
   [LOTE 1 · locucoes de primeira entrada] Os TRES gestos da coreografia, o
   veu do portao e o alto-falante do cabecalho.

   Os gestos NAO sao desenho novo: sao .hl-pulse e .beckon de
   intro-redesign/experiencia.html, portados para ca com prefixo tt-loc-.
   Foram copiados com os MESMOS numeros (1,25 s do pulso, 1,8 s do anel,
   2,4 s da lamina) — o que o Luis aprovou em 05/08 e o que toca aqui.
   O anel dourado da ABA e o unico desenho novo (ordem dele, 30/08), e e
   deliberadamente OUTRA coisa que o .dia0-hi laranja com veu que ele mandou
   tirar naquele mesmo dia: dourado, no ICONE da aba, e sem veu escurecendo.
   ========================================================================== */

/* --- realce: "a voz citou este elemento" (= .hl-pulse) ------------------- */
.tt-loc-realce{ animation:ttLocPulso 1.25s cubic-bezier(.4,0,.2,1); }
@keyframes ttLocPulso{
  0%{ transform:none; filter:none }
  28%{ transform:translateY(-2px) scale(1.03); filter:drop-shadow(0 0 16px rgba(232,105,10,.6)) brightness(1.14) }
  100%{ transform:none; filter:none } }

/* --- isca: "e aqui que se toca, quando a fala acabar" (= .beckon) -------- */
.tt-loc-isca{ animation:ttLocIsca 1.8s ease-in-out infinite; }
@keyframes ttLocIsca{
  0%,100%{ box-shadow:0 0 0 1.5px rgba(255,170,60,.45), 0 0 14px rgba(255,170,60,.24) }
  50%{ box-shadow:0 0 0 2px rgba(255,170,60,.92), 0 0 30px rgba(255,170,60,.55) } }
/* a lamina so entra onde o motor DONA o posicionamento (o alvo era static).
   Num alvo ja posicionado — a .fr-veu e absolute — forcar position:relative
   quebraria o layout dele; ali fica o anel sozinho, que e exatamente a
   variante de movimento reduzido que o documento ja previa. */
.tt-loc-isca-rel{ position:relative; overflow:hidden; }
.tt-loc-isca-rel::after{ content:""; position:absolute; top:0; bottom:0; left:0; width:60%;
  pointer-events:none; border-radius:inherit;
  background:linear-gradient(100deg,transparent 0%,rgba(255,255,255,0) 30%,rgba(255,255,255,.5) 50%,rgba(255,255,255,0) 70%,transparent 100%);
  transform:translateX(-180%) skewX(-18deg); animation:ttLocLamina 2.4s ease-in-out .3s infinite; }
@keyframes ttLocLamina{ 0%{ transform:translateX(-180%) skewX(-18deg) } 55%,100%{ transform:translateX(320%) skewX(-18deg) } }

/* --- o anel dourado no icone da aba (raiz de aba: e a isca no 1) --------- */
/* [31/08 · item 07 · Luis] "faca um retangulo simetrico que engloba exatamente
   cada icone de aba, sem sobreposicoes."
   MEDIDO no ar: o anel era um CIRCULO de 38x38 ancorado em `top:6px` do botao,
   enquanto o icone mede 22x22 e comeca 11,8px abaixo do topo — grande demais e
   2,2px fora de eixo na vertical. Agora o gesto mora no PROPRIO <svg> do icone:
   `outline` + `outline-offset:3px` desenham um retangulo arredondado com respiro
   IGUAL nos quatro lados, por construcao. Outline nao ocupa espaco no layout
   (nao empurra o rotulo) e o gesto inteiro mede 22 + 2*(3+2,5) = 33px contra os
   64,5px da aba mais estreita medida — sem encostar na vizinha. */
#tabbar .tabbar-item.tt-loc-aba svg{
  border-radius:7px; outline:2px solid rgba(255,170,60,.9); outline-offset:3px;
  animation:ttLocAba 1.5s ease-in-out infinite; }
@keyframes ttLocAba{
  0%,100%{ outline-width:1.5px; outline-color:rgba(255,170,60,.55);
           filter:drop-shadow(0 0 5px rgba(255,170,60,.30)) }
  50%    { outline-width:2.5px; outline-color:rgba(255,170,60,1);
           filter:drop-shadow(0 0 11px rgba(255,170,60,.75)) } }

/* --- o veu: APOSENTADO (ordem do Luis, 01/09) --------------------------- */
/* "permita os cliques durante a locucao". Ele existia para capturar o toque
   enquanto a voz falava; hoje o tt-loc.js nunca mais o levanta e, se algum
   caminho o levantar, ele nao pega mais nada: pointer-events:none no lugar do
   touch-action:none. Duas travas em arquivos diferentes, porque um deles pode
   chegar de cache velho. O que da causa visivel a fala e o alto-falante com o
   progresso, logo abaixo. */
#tt-loc-veu{ position:fixed; inset:0; z-index:300; background:transparent;
  -webkit-tap-highlight-color:transparent; pointer-events:none; cursor:default; }
#tt-loc-veu[hidden]{ display:none; }

/* --- o alto-falante do cabecalho ---------------------------------------- */
#tt-loc-btn{ position:fixed; top:calc(12px + env(safe-area-inset-top));
  right:calc(12px + max(var(--sal,0px), env(safe-area-inset-right, 0px)));
  width:38px; height:38px; border-radius:9px; z-index:310;
  background:rgba(20,20,20,.92); border:1px solid var(--border2); color:var(--text);
  cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0;
  transition:background .15s,border-color .15s,border-radius .18s; -webkit-tap-highlight-color:transparent; }
#tt-loc-btn[hidden]{ display:none; }
#tt-loc-btn:active,#tt-loc-btn:hover{ background:var(--primary); border-color:var(--primary); }
#tt-loc-btn .tt-loc-alto{ width:20px; height:20px; }
/* [31/08 · item 02 · Luis] O ANEL ESTAVA 4 px A ESQUERDA DO ICONE.
   `inset:-4px` + `calc(100% + 8px)` deixa a caixa SOBRE-RESTRITA (left, right E
   width declarados); medido no navegador, a largura USADA saia 36px e a altura
   44px — caixa retangular, centro em x=355 contra os x=359 do botao. Geometria
   explicita e quadrada resolve: -1px cobre a borda de 1px, e 38x38 e a caixa de
   borda do botao, entao o circulo nasce CONCENTRICO ao quadrado.
   🔴 O `max-width:none` NAO E ENFEITE — e a CAUSA RAIZ. A regra `svg{max-width:100%;
   height:auto}` do bloco de telefone (app.css ~3993) grampeava a LARGURA do anel na
   caixa de 36px e deixava a ALTURA passar: caixa 36x44, que o `rotate(-90deg)` vira
   um retangulo 44x36 com o centro 4px a esquerda. Era isso, e so isso. Sem esta
   linha o conserto sai igualzinho ao defeito — medido pela regua. */
#tt-loc-btn .tt-loc-anel{ position:absolute; left:-1px; top:-1px; right:auto; bottom:auto;
  width:38px; height:38px; max-width:none; max-height:none; box-sizing:border-box;
  transform:rotate(-90deg); opacity:0; transition:opacity .2s; pointer-events:none; }
#tt-loc-btn.tt-loc-btn-on .tt-loc-anel{ opacity:1; }
/* [31/08 · Luis] "quando a fala esta rolando e o icone de alto falante esta
   rodando, tire o quadrado cintilante de isca dele, nao precisa, deixe apenas o
   circulo girando e acompanhando a fala."
   O quadrado E A CAIXA DO BOTAO — 38x38, radius 9, borda de 1px que acendia em
   laranja (a mesma cor da isca, dai a leitura de "isca"). Enquanto fala: borda
   TRANSPARENTE e raio 50 %, entao o fundo escuro vira o DISCO inscrito no anel
   e o unico desenho que sobra e o circulo do progresso. So com .tt-loc-btn-on:
   parado, o quadrado e a unica affordance do "Ouvir de novo" e continua ali.
   O :hover/:active generico (background+border em var(--primary)) redesenharia
   o quadrado no toque de silenciar — por isso ele e neutralizado aqui. */
#tt-loc-btn.tt-loc-btn-on{ border-color:transparent; border-radius:50%; }
#tt-loc-btn.tt-loc-btn-on:active,#tt-loc-btn.tt-loc-btn-on:hover{
  background:rgba(20,20,20,.92); border-color:transparent; }
.tt-loc-anel circle{ fill:none; stroke-width:2.4; }
.tt-loc-anel-b{ stroke:rgba(255,255,255,.16); }
/* [item 02] perimetro do raio NOVO (16,5): 2*pi*16,5 = 103.6726. O mesmo numero
   vive no `progresso()` do tt-loc.js — mudar aqui sem mudar la deixa a barra
   parando antes do fim. */
.tt-loc-anel-p{ stroke:#FFAA3C; stroke-linecap:round; stroke-dasharray:103.6726; stroke-dashoffset:103.6726; }
/* enquanto fala, o alto-falante e o botao de SILENCIO — as ondas pulsam */
#tt-loc-btn.tt-loc-btn-on .tt-loc-onda{ animation:ttLocOnda 1.4s ease-in-out infinite; }
@keyframes ttLocOnda{ 0%,100%{ opacity:.35 } 50%{ opacity:1 } }
/* na paisagem da pratica o menu-btn some; o alto-falante acompanha */
body.in-practice #tt-loc-btn{ top:calc(8px + env(safe-area-inset-top)); }
/* [31/08 · PAUTA-1 · Luis] "adeque o simbolo de auto falante aos icones da tela
   de partitura, atualmente ele esta maior que os outros e sobrepondo o icone de
   play/pause." Na .sc-top os controles sao 30x30 com icone de 15; o alto-falante
   e 38x38 com icone de 20 e mora em position:fixed no canto — por isso cai POR
   CIMA do play/pause. Aqui ele fica do tamanho dos vizinhos e a barra reserva o
   vao dele (30 do botao + 12 da margem + 8 de folga), entao nada mais se toca.
   🔑 O anel NAO precisa de conta nova: o svg tem viewBox="0 0 36 36", logo r=16,5
   e o dasharray 103.6726 sao unidades de USUARIO e escalam com a caixa — o
   tt-loc.js fica intocado. Em navegador sem :has() nada muda. */
/* [FLUIDEZ_PRATICA_04_09] `body:has(...)` SAIU. Medido: com ele a pratica roda a 45,6 fps
   (p95 83,3 ms, 40 quadros perdidos em 12 s, 3284 ms de recalculo de estilo);
   sem ele, 59,9 fps (p95 16,7 ms, 0 perdidos, 373 ms). O motivo e que
   `body:has()` faz o Blink reavaliar o BODY a cada mutacao do DOM, e invalidar
   o body varre os ~68 000 elementos vivos do app. A condicao e a MESMA — quem
   a avalia e que passou a ser o JS, uma vez por navegacao (__ttSyncLocUi). */
body.tt-fr-partitura #tt-loc-btn{ width:30px; height:30px; border-radius:8px; }
body.tt-fr-partitura #tt-loc-btn .tt-loc-alto{ width:15px; height:15px; }
body.tt-fr-partitura #tt-loc-btn .tt-loc-anel{ width:30px; height:30px; }
.score-play-page .sc-top{ padding-right:calc(50px + max(var(--sal,0px), env(safe-area-inset-right, 0px))); }

/* [SPEAKER-ICON-FIX] o alto-falante do cabecalho (#tt-loc-btn) e o botao de
   conta (.account-btn) usam o MESMO top/right/38x38 — cada um foi desenhado
   isolado (LOTE 1 e LOTE 3), sem saber do outro. Nas 5 abas onde os dois
   podem aparecer juntos (toc/songs/games/harmony/score) toda locucao dessa
   aba (loc-toc, loc-songs, loc-games, loc-harmony, loc-welcome) desenhava o
   circulo do alto-falante EM CIMA do icone de conta, pixel a pixel (medido:
   os dois retangulos sao identicos, {top:12,left:343,w:38,h:38}). Fix: abre
   espaco pro account-btn andar pra esquerda so enquanto o alto-falante esta
   visivel (nao hidden) — mesma logica de "reservar o vao dele" da PAUTA-1,
   so que aqui e outro botao fixed que precisa ceder, nao uma toolbar. */
/* [FLUIDEZ_PRATICA_04_09] idem: a classe vem do proprio `pinta()` do tt-loc.js, que ja
   escrevia `b.hidden` so quando MUDA — a classe do body anda na mesma linha. */
body.tt-loc-visivel .account-btn{
  right:calc(12px + 38px + 10px + max(var(--sar), env(safe-area-inset-right, 0px))); }

@media (prefers-reduced-motion:reduce){
  /* movimento reduzido, como o documento define para cada gesto:
     realce = halo estatico; isca = anel aceso fixo, sem lamina */
  .tt-loc-realce{ animation:none; filter:drop-shadow(0 0 16px rgba(232,105,10,.6)); }
  .tt-loc-isca{ animation:none; box-shadow:0 0 0 2px rgba(255,170,60,.92), 0 0 24px rgba(255,170,60,.45); }
  .tt-loc-isca-rel::after{ animation:none; opacity:0; }
  #tabbar .tabbar-item.tt-loc-aba svg{ animation:none; }   /* [item 07] o anel agora e do icone */
  #tt-loc-btn.tt-loc-btn-on .tt-loc-onda{ animation:none; opacity:1; }
}

/* [02/09 · PORTA DO TECLADO] PORTA_DO_TECLADO_02_09 — a janela lida ("Vou ler de…")
   na tela "O teclado está certo?" */
.mp-vmt-janela{ display:flex; align-items:center; justify-content:center; gap:10px; flex-wrap:wrap;
  margin:2px 0 6px; font:12.5px/1.4 monospace; color:rgba(246,241,231,.78); }

/* ============================================================================
 * [03/09 · PAUTA_QUE_DESLIZA_03_09] A VISTA PAUTA da fita rolante
 * ----------------------------------------------------------------------------
 * A mesma <section>, o mesmo wrap, os mesmos veus e gestos: o que muda e QUAL
 * canvas aparece. Tudo escopado em [data-fr-on="1"][data-fr-vista="pauta"] — a
 * fita de blocos e a pauta do curso ficam byte a byte como estao.
 * ========================================================================= */
.fr-pauta{ position:absolute; inset:0; width:100%; height:100%; display:none; }
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .fr-pauta{ display:block; }
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .fr-canvas{ display:none; }

/* ---------------------------------------------------------------------------
 * [21/09 · Luis] O LOOP — o escolhedor de compassos
 * ---------------------------------------------------------------------------
 * "loop section, um novo icone na barra que torna a grade mais visivel igual no
 *  modo editar (...) ele apenas arrasta, e nao seleciona tempos, seleciona
 *  compassos inteiros."
 *
 * O escolhedor e o TERCEIRO canvas do mesmo wrap e toma o palco das duas vistas
 * ao mesmo tempo (blocos E pauta) — porque a pergunta que ele faz e a mesma nas
 * duas, e a resposta (o trecho) tambem. Enquanto ele esta aberto os outros dois
 * saem: dois donos no mesmo palco seria um a apagar o outro por quadro, que e a
 * mesma lei que o modo editar ja cumpre.
 *
 * `touch-action:none` porque o gesto e um arrasto horizontal DENTRO do canvas:
 * sem isto o navegador interpreta-o como rolagem da pagina e o dedo escolhe meio
 * compasso antes de o browser roubar o gesto.
 * ------------------------------------------------------------------------ */
/* 🔴 [21/09 · Luis] *"ao apertar o botao deve se manter na mesma tela de
   pratica, nao deve se mudar a tela, as notas que estavam passando ali,
   continuam paradas ali"*. A 1a versao ESCONDIA a pista e punha outra no lugar
   (as duas linhas que estavam aqui faziam `display:none` no `.fr-canvas` e no
   `.fr-pauta`). Saiu: o escolhedor e uma CAMADA por cima, transparente, e a
   vista que estava no ar continua a desenhar-se por baixo dela — congelada,
   porque o transporte esta em pausa. */
.fl-canvas{ position:absolute; inset:0; width:100%; height:100%; display:none;
  z-index:2; background:transparent;
  touch-action:none; user-select:none; -webkit-user-select:none; }
.score-play-page[data-fr-on="1"][data-fl="1"] .fl-canvas{ display:block; }
/* [21/09 · Luis] "remover aviso de loop arraste sobre os compassos que vc quer
   repetir (remover aviso que aparece acima do teclado)". A dica saiu inteira —
   markup, CSS e a chave `fita.loopDica` deixou de ter consumidor na tela (fica
   no dicionario: e ela que o `aria-label` do chip usaria se voltasse a ser
   precisa, e arrancar uma chave dos 8 idiomas por causa de um elemento que saiu
   e mexer em 8 ficheiros para nao ganhar nada). Quem passou a disparar a locucao
   e a PROPRIA pagina em modo loop — ver o REG do tt-loc.js. */
/* o chip aceso ja e laranja pela regra do `.fr-chip.on`; o que falta e o
   rotulo do trecho ("3–6") nao ficar em versalete gigante ao lado do icone */
.fr-chip-loop.on .fr-chip-rot{ letter-spacing:.04em; font-variant-numeric:tabular-nums; }
/* 🔴 [21/09 · Luis] "substitua o blur ao redor do botao de loop quando ele esta
   acionado por simplesmente o botao ficar laranja, igual acontece com os outros
   botoes."
   E ELE ESTAVA A DESCREVER O DEFEITO COM PRECISAO: o chip do loop aceso **nunca
   chegava a ficar laranja**. Sobrava-lhe so o halo.
   O mecanismo esta a 1600 linhas daqui e e de CASCATA: `.fr-chip.on` pinta o
   fundo de `--primary`, mas `.fr-chip.fr-chip-solo` vem DEPOIS no ficheiro com a
   MESMA especificidade (duas classes) e repoe `background:rgba(18,18,20,.78)` e
   `border-color:var(--border2)`. Do `.on` sobrevivem so o `color:#fff` e o
   `box-shadow` — ou seja, um botao escuro com um halo laranja a volta.
   🔑 A CASA JA TINHA TROPECADO NISTO e resolveu-o para UM caso: a regra
   `.fr-chip.fr-chip-metro.on` (logo acima) existe exactamente para devolver o
   fundo ao chip do metronomo. Nunca foi generalizada, entao todo chip solo novo
   nasce com o mesmo defeito — e o do loop nasceu.
   🔴 E A MEDICAO QUASE ME ENGANOU NO SENTIDO CONTRARIO: a minha primeira sonda
   leu `rgb(232,105,10)` no metronomo (que tem a regra) e o chip do loop APAGADO
   (que e escuro por desenho), e eu li aquilo como "a cor ja la esta, o problema
   e so o halo". Era o especime errado. So o mutante — pedir a cor com o chip
   ACESO — e que respondeu a pergunta.
   Aqui vai a mesma cura do metronomo, sem o halo, que e o que ele pediu. O halo
   dos outros chips fica como esta no ar: ele nao pediu para mexer neles. */
.fr-chip.fr-chip-loop.on{ background:var(--primary); border-color:var(--primary);
  color:#fff; box-shadow:none; }
/* o papel creme e desenhado no canvas; por baixo fica a constelacao com um veu
 * mais uniforme (a curva da fita de blocos escurece o RODAPE, onde na pauta
 * nao ha nada para julgar) */
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .fr-canvas-wrap{
  background:
    radial-gradient(ellipse 120% 85% at 50% 0%, rgba(232,105,10,.10) 0%, transparent 62%),
    linear-gradient(180deg, rgba(7,7,9,.22) 0%, rgba(7,7,9,.34) 100%); }
/* os chips da VISTA: icone + rotulo, no trilho de sempre */
.fr-seg-tela .fr-chip{ display:inline-flex; align-items:center; gap:6px; }
/* [04/09 · Luis: "da pra transformar o icone de editar que e apenas um lapis
   em um botao escrito editar"] O EDITAR VOLTA A TER NOME. O rotulo sempre
   existiu no markup; quem o apagava era o CSS — e a regra que o apagava a
   partir de 1100 px foi escrita quando a barra tinha TAMBEM o metronomo.
   Tirado o chip, o EDITAR saiu DAQUELA regra e a palavra volta.
   🔑 E SO DAQUELA. As regras dos sinais ESTREITOS (`.fr-chip-solo
   .fr-chip-rot` em <=800 px e em `html.dt` <=1000) continuam de pe, porque a
   MEDIDA disse que ali a palavra nao cabe: no iPhone SE deitado a barra ja
   rolava 72 px NO AR, e com o nome em todo sinal rolaria 82 — pior. Do jeito
   que ficou, o SE so PERDE o chip do metronomo e o nome aparece de 844 px
   para cima, onde a barra fecha em UMA LINHA (medido: 844x390 764/764 e
   926x428 846/846, com a palavra). O `gap` volta porque o chip solo o zera
   em <=1100 e sem ele o lapis encostaria na palavra. */
.fr-chip.fr-chip-edit{ gap:6px; }
.fr-seg-tela .fr-chip svg{ width:14px; height:14px; flex:0 0 auto; }
/* [05/09 · Luis: "restaure na partitura"] A PILULA DO METRONOMO VOLTA — e
   volta ACESA, porque ela e ESTADO (ligado/desligado), nao acao avulsa. Quem a
   esconde na vista de BLOCOS e o __frChipsSync (display inline), nao o CSS: a
   barra tem de perder a LARGURA do chip la, e `visibility` nao a devolveria. */
.fr-chip.fr-chip-metro.on{ background:var(--primary); border-color:var(--primary); color:#fff;
  box-shadow:0 0 12px rgba(232,105,10,.45); }
/* [03/09] A BARRA CABE EM UMA LINHA no iPhone deitado (844x390 → 677 px de barra;
 * 926x428 → 759). Medido ANTES deste lote: 701 px de conteudo em 677 — o EDITAR
 * de 25/08 ja estourava, e a barra rolava (o scroll e a rede, nao o desenho).
 * Com a vista e o metronomo entram 2 pilulas; a conta fecha assim, MEDIDA:
 * os 4 rotulos de acao viram icone abaixo de 1100 px (Editar, Pauta, Blocos,
 * Metronomo — o nome fica no aria-label/title) e as pilulas dos trilhos ficam
 * mais justas (fonte 11,3 px, padding 6,6 px, trilho 2 px, vao 4 px). */
@media (max-width:1100px){
  /* [04/09] o `.fr-chip-metro` saiu (o chip nao existe mais) e o `.fr-chip-edit`
     saiu daqui DE PROPOSITO: o lapis volta a ser um botao ESCRITO "Editar" —
     ver o bloco do `.fr-chip.fr-chip-edit`, logo acima, que conta a medida. Nos
     sinais ESTREITOS (<=800 e html.dt <=1000) o nome continua saindo, pela
     `.fr-chip-solo .fr-chip-rot`: la ele nao cabe, e isso foi MEDIDO. */
  .fr-seg-tela .fr-chip-rot{ display:none; }
  /* [05/09] o METRONOMO volta a esta regra (era dela ate 04/09): abaixo de
     1100 px ele e SO ICONE, com o nome no aria-label/title. E a mesma conta de
     03/09 — o chip so aparece na vista pauta, mas quando aparece tem de caber
     na mesma barra de 677 px do iPhone deitado. O EDITAR continua FORA daqui
     (bloco acima): a palavra dele foi medida e cabe de 844 px para cima. */
  .fr-chip-metro .fr-chip-rot{ display:none; }
  .score-play-page[data-fr-on="1"] .fr-bar{ gap:4px; }
  .fr-chip{ font-size:11.3px; padding:6px 6.9px; letter-spacing:.03em; }
  .fr-seg{ padding:2px; gap:1px; }
  .fr-chip.fr-chip-solo{ padding:6.4px 7.6px; gap:0; }
  .fr-chip.fr-bpm-btn{ min-width:26px; padding-left:7px; padding-right:7px; }
  .fr-bpm-val{ min-width:60px; font-size:11.3px; }
}
@media (max-width:1000px){
  html.dt .fr-seg-tela .fr-chip-rot, html.dt .fr-chip-metro .fr-chip-rot{ display:none; }
}
/* ============================================================================
 * [03/09 · lote 2 · PAUTA_LIMPA_03_09] OS AJUSTES DA PAUTA QUE DESLIZA
 * ----------------------------------------------------------------------------
 * (1) O TECLADO. O piano ESCURO (#2A2A2A/#0E0E0E) e da fita de BLOCOS, que vive
 *     na constelacao — e a propria regra de 25/08 diz "a pauta segue com o piano
 *     branco". Faltava a vista para escopar: agora, na vista pauta, o teclado e
 *     o branco-e-preto da leitura, com as MESMAS dimensoes (so a cor muda).
 * (2) A BARRINHA LARANJA que separa o teclado da area de partitura mora na
 *     BORDA DE BAIXO DA .fr-area, nao no teclado: assim os 3 px saem da pista
 *     (flex:1) e a altura das teclas fica intacta.
 * (3) O BOTAO "PAUTA" (.fr-toggle) MORREU. Ele levava ao motor de partitura
 *     ANTIGO. Ja devia estar escondido em Minhas Partituras desde 08/08 — o
 *     __frSyncToggle poe `tg.hidden = true` para origem 'mp' — mas
 *     `.fr-toggle{ display:inline-flex }` tem especificidade MAIOR que a regra
 *     `[hidden]{ display:none }` do navegador, e o atributo nunca escondeu
 *     nada. A 1a linha devolve o poder ao `hidden`; a 2a o proibe na vista
 *     pauta, onde a chave Pauta|Blocos ja faz a escolha e o botao dizia o
 *     contrario da tela.
 * ========================================================================= */
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .kbd-key.kbd-w{ fill:#FFFFFF; stroke:#101010; }
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .kbd-key.kbd-b{ fill:#101010; stroke:#101010; }
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .kbd-key.fr-k-n{ fill:#9A9A9A !important; }
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .fr-area{ border-bottom:3px solid var(--primary); }
.fr-toggle[hidden]{ display:none; }

/* ============================================================================
 * [03/09 · CURSO_MOTOR_NOVO_03_09] O PALCO DA LICAO DO CURSO
 * ----------------------------------------------------------------------------
 * A fita fica MONTADA a licao inteira; o que troca por fase e qual palco
 * aparece. `data-sc-palco="papel"` devolve a pauta parada (onde as marcas da
 * coreografia das locucoes tem alvo) e esconde a fita; sem o atributo, tudo
 * segue exatamente como antes deste lote.
 * ========================================================================= */
.score-play-page[data-fr-on="1"][data-sc-palco="papel"] .fr-area{ display:none; }
.score-play-page[data-fr-on="1"][data-sc-palco="papel"] .sc-paper{ display:flex; }
/* 🔑 [05/09 · Luis: "a tela que deveria ser branca esta toda preta. visual
 * completamente inutilizado"] O PAPEL VOLTA PARA O PAPEL.
 * Quem pintava o creme era a PAGINA (`.score-play-page{background:var(--sc-paper)}`),
 * e a fita a poe transparente de proposito para nao apagar a constelacao. Este
 * bloco de 03/09 devolveu o `.sc-paper` ao curso sem devolver o fundo dele:
 * com a fita ligada e o palco no papel, a folha reaparecia por cima do preto —
 * linhas da pauta em tinta clara (herdadas do `color:var(--text)` da fita) e
 * cabecas pretas invisiveis. Pintar AQUI, e nao na pagina, mantem a barra e o
 * topo escuros que o proprio lote 03/09 escolheu: o que troca por fase e a
 * FOLHA, nao a tela inteira. */
.score-play-page[data-fr-on="1"][data-sc-palco="papel"] .sc-paper{
  background:var(--sc-paper); color:var(--sc-ink); }
/* a tecla que o aluno ERROU (nota sem par no juiz): 260 ms, so no curso */
.score-play-page .kbd-key.fr-k-e{ fill:#EF4444 !important; }
/* 🔑 A BARRA CONTINUA SENDO A DA FITA — ela e que foi desenhada para o palco
 * escuro (as pilulas do papel usam tinta clara sobre creme e sumiam aqui;
 * medido na bancada, retrato em /tmp/curso-motor.png). O que muda e PARA ONDE
 * os chips falam: no curso eles falam com o TAPE, nao com `_fr` (ver o
 * delegate de clique). A chave Pauta|Blocos sai: o curso E a pauta. */
.score-play-page[data-fr-on="1"][data-sc-palco] .fr-seg-tela{ display:none; }
/* o recado da contagem sobre a fita ("ouca" x "repita em 1,2,3,4") */
.fr-msg{ position:absolute; left:50%; top:8px; transform:translateX(-50%); z-index:26;
  font-family:var(--font-h); font-weight:800; text-transform:uppercase; letter-spacing:.12em;
  font-size:12px; color:#fff; background:rgba(18,18,20,.82); border:1px solid var(--border2);
  padding:6px 14px; border-radius:999px; white-space:nowrap; pointer-events:none;
  opacity:0; transition:opacity .18s; }
.fr-msg.on{ opacity:1; }
.score-play-page[data-fr-on="1"][data-fr-vista="pauta"] .fr-toggle{ display:none; }


/* ═══════════════════════════════════════════════════════════════════════════
   [05/09 · Luis] A TELA ABRE NA POSIÇÃO EM QUE ELA TEM DE ABRIR
   ---------------------------------------------------------------------------
   Ordem, depois de a "vire o celular" sair do app: *"as telas de prática, as
   telas que só fazem sentido na horizontal, estão todas abrindo na vertical,
   fita esticada toda distorcida, partitura na vertical"* — e, sobre o que
   fazer: *"as telas sempre abrem na posição que tiverem que abrir"*.

   Então, com o aparelho EM PÉ, uma página que só existe deitada é DESENHADA
   deitada: o app inteiro roda 90° e o que se vê é a prática em paisagem dentro
   da tela vertical. Girar o aparelho tira a rotação (a media query descasa) e
   a MESMA tela fica de pé — sem salto, sem remontar e sem tela de aviso.

   🔑 POR QUE O BODY, e não só a página. Metade da prática mora FORA da página:
   menu de tonalidade, sidebar, toasts, cartão do microfone e balão do tutorial
   são filhos do <body> com position:fixed. Rodando só a .page eles ficariam de
   pé por cima de uma prática deitada. Rodando o body, o transform ainda o
   torna o BLOCO DE CONTENÇÃO de todo `position:fixed` descendente — cada um
   passa a medir-se pela caixa deitada, que é exatamente o que se quer.

   🔑 O QUE NÃO GIRA SOZINHO: as unidades de viewport. `100vw` continua a valer
   a largura REAL do aparelho (390) mesmo dentro do quadro deitado, onde a
   largura é 844. Por isso este bloco troca, uma a uma, as medidas do desenho
   (vw↔dvh, vh↔vw) e repete as media queries de paisagem com os limiares
   trocados (max-width↔max-height). Quem lê pixels em JS pergunta ao
   `__ttCaixa`/`__ttMoldura`/`__ttPt` (index.html), que desfazem a rotação.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (orientation:portrait){
  html.tt-deitado{ overflow:hidden; }
  body.tt-deitado{
    position:fixed; top:0; left:0; margin:0;
    width:100dvh; height:100vw;
    min-width:0; min-height:0; max-width:none; max-height:none;
    overflow:hidden;
    transform-origin:0 0;
    /* rotate e DEPOIS translate: o translate roda junto, então o canto (0,0)
       do conteúdo cai no canto superior direito da tela e a caixa deitada
       cobre o viewport inteiro — sem margem e sem sobra. */
    transform:rotate(90deg) translateY(-100%);
    /* A FAIXA SEGURA GIRA JUNTO: o notch, que em pé está EM CIMA, passa a ser a
       ESQUERDA do conteúdo deitado; o indicador de casa, a direita. O app já lê
       a esquerda e a direita por `--sal`/`--sar`, então basta redefini-las.
       🔑 O valor passa por `--tt-sa-*` (padrão = o próprio `env`) porque `env()`
       NÃO se pode simular: sem esta indireção o recorte do iPhone não teria como
       ser costurado na bancada, e a prova mediria sempre zero. */
    --sal:var(--tt-sa-top, env(safe-area-inset-top));
    --sar:var(--tt-sa-bottom, env(safe-area-inset-bottom));
  }
  /* ---- as medidas ESTRUTURAIS, uma a uma (vh→vw, vw→dvh) ---- */
  body.tt-deitado .main,
  body.tt-deitado .page{ min-height:100vw; }
  body.tt-deitado .sidebar{ height:100vw; }
  body.tt-deitado .practice-page{
    height:100vw; max-height:100vw;
    width:100dvh;  max-width:100dvh;
  }
  body.mode-interactive.tt-deitado .practice-page.visible{
    --pw: min(92dvh, calc(86vw * 1.7742));
  }
  body.tt-deitado .practice-bar{ min-height:clamp(140px, 35vw, 220px); }
  body.tt-deitado .score-play-page[data-fr-on="1"]{ --sc-kbd-h:min(21.8025vw,139.29375px); }
  body.tt-deitado #guided-hint{ max-width:calc(100dvh - 120px); }
  /* ---- e a FAIXA SEGURA gira com o resto ----------------------------------
     `--sal`/`--sar` (acima) já viram o notch e o indicador de casa. Falta o que
     lê `env(safe-area-inset-top)` DIRETO para se afastar do topo: no quadro
     deitado o "topo" do conteúdo é a borda DIREITA do aparelho, onde em pé não
     há recorte nenhum. Sem isto, a pilha ☰ / voltar / modo nascia ~50 px mais
     abaixo num iPhone com notch (a bancada não vê: lá os insets são 0). */
  body.tt-deitado{ --tt-sa-cima: var(--tt-sa-right, env(safe-area-inset-right, 0px)); }
  body.tt-deitado .menu-btn,
  body.tt-deitado .account-btn,
  body.tt-deitado #tt-loc-btn{ top:calc(12px + var(--tt-sa-cima)); }
  body.tt-deitado .practice-page #practicePrev.nav-tag.is-pb-nav{
    top:calc(12px + var(--tt-sa-cima) + 38px + 8px);
  }
  body.tt-deitado .practice-page .pb-mode-btn{
    top:calc(12px + var(--tt-sa-cima) + 38px + 8px + 38px + 8px);
  }
  /* a página que SAI em retrato continua escondida (regra de 05/09) — mas só
     quando NÃO estamos deitados: deitados ela sai desenhada certa. */
  body.tt-deitado .page.page-leaving[data-tom]{ visibility:visible!important; }
  /* ---- tipografia e vãos que escalam com o viewport (mesma troca) ---- */
  body.tt-deitado .practice-bar{ gap:clamp(12px, 2dvh, 24px); }
  body.tt-deitado .pb-tom{ font-size:clamp(26px, 4dvh, 38px); }
  body.tt-deitado .pb-info-section{ font-size:clamp(9.5px, 1.15dvh, 11.5px); }
  body.tt-deitado .pb-info-title{ font-size:clamp(14px, 1.9dvh, 18px); }
  body.tt-deitado .pb-info-formula{ font-size:clamp(11px, 1.4dvh, 14px); }
  body.tt-deitado .pb-info-progression{ font-size:clamp(10.5px, 1.25dvh, 12.5px); }
  body.tt-deitado .pb-time{ font-size:clamp(18px, 2.5dvh, 26px); }
  body.tt-deitado .pb-progress{ width:min(280px, 28dvh); }
  body.tt-deitado .kbd-label-w{ bottom:clamp(16px, 3vw, 28px); font-size:clamp(13px, 1.7dvh, 18px); }
  body.tt-deitado .kbd-label-b{ font-size:clamp(10px, 1.3dvh, 14px); }
  body.tt-deitado .sc-count{ font-size:clamp(64px, 16vw, 110px); }
  body.tt-deitado .sc-conta-msg{ font-size:clamp(13px, 2.6vw, 18px); }
  body.tt-deitado .sc-crumb{ max-width:34dvh; }
  body.tt-deitado .score-play-page[data-fr-on="1"] .sc-crumb{ max-width:16dvh; }
  body.tt-deitado .fr-conta{ font-size:min(22vw, 120px); }
  body.tt-deitado .score-play-page[data-fr-on="1"] .fr-conta{ font-size:min(30vw, 150px); }
  body.tt-deitado .score-play-page[data-fr-on="1"] .fr-placar{ font-size:clamp(18px, 4.4vw, 26px); }
  body.tt-deitado .fr-veu-card{ gap:clamp(5px, 1.5vw, 11px); padding:clamp(10px, 2.4vw, 20px) clamp(16px, 3dvh, 28px); }
  body.tt-deitado .fr-veu-acoes{ margin-top:clamp(1px, .6vw, 5px); }
  body.tt-deitado .score-play-page[data-fr-on="1"] .fr-veu-t{ font-size:clamp(9.5px, 1.6vw, 12px); }
  body.tt-deitado .score-play-page[data-fr-on="1"] .fr-veu-sub{ font-size:clamp(10.5px, 1.8vw, 13.5px); }
  body.tt-deitado .score-play-page[data-fr-on="1"] .fr-veu-btn{ font-size:clamp(12px, 2vw, 15px); padding:clamp(9px, 1.8vw, 14px) clamp(18px, 3.2dvh, 28px); }
  body.tt-deitado .score-play-page[data-fr-on="1"] .fr-veu-btn.fr-sec{ font-size:clamp(10.5px, 1.6vw, 12.5px); padding:clamp(7px, 1.4vw, 11px) clamp(13px, 2.4dvh, 20px); }
  body.tt-deitado .fr-oct{ font-size:clamp(8px, 1.6vw, 11px); }
  body.tt-deitado .fr-avisomidi{ max-width:min(420px, 86dvh); }
}
/* ---- as media queries de PAISAGEM, repetidas com os limiares trocados ----
   O quadro deitado tem largura = altura do aparelho e altura = largura dele.
   Então `(orientation:landscape) and (max-width:N)` vira, aqui,
   `(orientation:portrait) and (max-height:N)` — mesmo aparelho, mesma conta. */
@media (orientation:portrait) and (max-height:900px){
  body.tt-deitado .practice-bar{
    gap:12px;
    padding: 8px max(12px, calc(max(var(--sar), env(safe-area-inset-bottom, 0px)) + 8px))
             8px calc(max(var(--sal), env(safe-area-inset-top, 0px)) + 60px);
    min-height:clamp(120px, 35vw, 180px); max-height:none;
  }
  body.tt-deitado .pb-tom{ font-size:clamp(26px, 4dvh, 34px); padding:6px 13px; }
  body.tt-deitado .pb-info-title{ font-size:clamp(13px, 1.7dvh, 16px); }
  body.tt-deitado .pb-time{ font-size:clamp(20px, 3dvh, 28px); }
  body.tt-deitado .pb-time-row{ gap:12px; }
  body.tt-deitado .practice-bar{ --pb-btn:40px; --pb-btn-fz:15px; }
  body.tt-deitado .pb-stage{ padding:7px 14px; font-size:12px; }
  body.tt-deitado .pb-bpm-btn{ width:30px; height:30px; font-size:15px; }
  body.tt-deitado .pb-bpm > [data-mt-bpm-val]{ font-size:16px; min-width:34px; }
  body.tt-deitado .pb-meter{ width:36px; height:36px; font-size:16px; }
  body.tt-deitado .pb-progress{ width:min(280px, 28dvh); height:4px; }
  body.tt-deitado .pb-zone-right{ gap:10px; }
  body.tt-deitado .pb-zone-left{ gap:12px; justify-content:flex-start; }
  body.tt-deitado .pb-info-formula{ max-width:clamp(140px, 22dvh, 200px); }
}
@media (orientation:portrait) and (max-height:720px){
  body.tt-deitado .practice-bar{ gap:10px; padding:6px 12px; min-height:clamp(76px, 20vw, 100px); }
  body.tt-deitado .pb-tom{ font-size:18px; padding:3px 8px; }
  body.tt-deitado .pb-info-title{ font-size:12px; }
  body.tt-deitado .practice-bar{ --pb-btn:40px; --pb-btn-fz:15px; }
  body.tt-deitado .pb-stage{ padding:6px 12px; font-size:11.5px; }
  body.tt-deitado .pb-meter{ width:32px; height:32px; font-size:14px; }
  body.tt-deitado .pb-progress{ width:min(220px, 22dvh); }
  body.tt-deitado .pb-zone-right{ gap:8px; }
  body.tt-deitado .pb-info-formula{ max-width:clamp(110px, 20dvh, 160px); }
  body.tt-deitado .practice-bar .nav-tag.is-pb-nav svg{ width:13px; height:13px; }
  body.tt-deitado .practice-page #practicePrev.nav-tag.is-pb-nav{
    width:34px; height:34px; top:calc(12px + env(safe-area-inset-right, 0px) + 34px + 6px);
  }
  body.tt-deitado .practice-page .pb-mode-btn{
    width:34px; height:34px;
    top:calc(12px + env(safe-area-inset-right, 0px) + 34px + 6px + 34px + 6px);
  }
  body.tt-deitado .practice-page.mt-completed #practiceNext.nav-tag.is-pb-nav{ width:42px; height:42px; }
}
@media (orientation:portrait) and (max-width:560px){
  body.tt-deitado .sc-fim-modal .ccm-card{ max-height:92vw; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   [05/09 · Luis] A VERSÃO HORIZONTAL PARA COMPUTADOR (Mac / Windows)
   ---------------------------------------------------------------------------
   "estabeleça bons critérios de boa visibilidade e navegação nas telas do
   computador". Só `html.dt` (≥900px + ponteiro fino): o celular não vê nada
   disto. Os CRITÉRIOS, que a régua validate_desktop.cjs mede tela a tela:

   C1 OCUPAÇÃO   nenhuma tela é a coluna do celular solta no canto: o conteúdo
                 ocupa ≥ 55 % da largura útil (à direita do trilho) e fica
                 centrado numa coluna de leitura (--dt-col) ou em grelha.
   C2 ESCALA     texto de corpo ≥ 15 px, menor texto visível ≥ 12 px, título da
                 tela ≥ 40 px; alvos de clique ≥ 40 px de altura.
   C3 DOBRA      a ação principal (Praticar / Continuar / Nova música / os
                 cartões de um hub) é visível SEM rolar a 1280×720 e 1440×900.
   C4 NAVEGAÇÃO  o trilho à esquerda é a navegação persistente (ícone + rótulo
                 legível, item ativo marcado); toda tela-filha tem o ← no topo;
                 nada rola na horizontal; hover em tudo o que age.
   C5 LARGURA    grelhas crescem com a janela (auto-fit) até --dt-max; blocos de
                 leitura param em --dt-col; a partitura da folha nunca passa de
                 ~840 px (senão vira gravura gigante).
   ═══════════════════════════════════════════════════════════════════════════ */
@media (min-width:600px){
  html.dt{ --dt-col:1180px; --dt-narrow:900px; }
  /* C4 · o trilho: rótulo legível, item ativo evidente, ponteiro de mão */
  /* o trilho largo do monitor leva a MESMA folga do entalhe — senão a cura de
     cima morria exactamente onde o `dc` vive (o telemóvel deitado tem ≥600 px
     de largura, logo cai neste bloco). */
  html.dt body.tabbar-on .tabbar{ width:calc(96px + max(var(--sal), env(safe-area-inset-left, 0px))); }
  html.dt body.tabbar-on #app{ padding-left:calc(96px + max(var(--sal), env(safe-area-inset-left, 0px))); }
  html.dt body.tabbar-on .nav-tags{ left:calc(96px + max(var(--sal), env(safe-area-inset-left, 0px))); }
  html.dt .tabbar-item{ gap:6px; min-height:74px; }
  html.dt .tabbar-item svg{ width:26px; height:26px; }
  html.dt .tabbar-label{ font-size:11.5px; letter-spacing:.06em; }
  html.dt .tabbar-item:hover{ background:rgba(255,255,255,.05); }
  /* C1 · a coluna de leitura de TODAS as telas-especiais (tabroot, quiz, editor) */
  html.dt body.mode-interactive .page.tabroot-page.visible:not(.toc-page):not(.mode-select-page),
  html.dt body.mode-interactive .page.quiz-page.visible,
  html.dt body.mode-interactive .page.mp-edit-page.visible{
    max-width:var(--dt-col); margin:0 auto; padding:56px var(--dt-pad) 96px;
  }
  /* C2 · escala de texto das telas-especiais */
  html.dt .page.special{ font-size:15px; }
  html.dt .special .lead, html.dt .toc-hero .lead{ font-size:18px; line-height:1.55; max-width:64ch; }
  html.dt .se-label{ font-size:12.5px; letter-spacing:.16em; margin-bottom:10px; }
  html.dt .nav-tag.is-round{ width:42px; height:42px; }
  html.dt .nav-tag.is-round svg{ width:16px; height:16px; }
  html.dt .se-head{ margin-bottom:22px; }
  html.dt .sc-sub-hero{ margin-bottom:26px; }
  /* cabeçalho das telas-hub: título à esquerda, lead logo abaixo (não centrado num mar) */
  html.dt .tabroot-page .toc-hero:not(.sc-sub-hero){ text-align:left; margin:0 0 34px; }
  html.dt .tabroot-page .toc-mega-title{ font-size:clamp(46px,4vw,64px); }
  html.dt .special .lead{ margin-left:0; }
  html.dt .toc-page .toc-mega-title{ font-size:clamp(40px,3.1vw,46px); }

  /* ---- JOGOS / TREINO DE OUVIDO (.gm-hub): 3 colunas, cartões de monitor ---- */
  html.dt .gm-hub{ max-width:none; grid-template-columns:repeat(auto-fit,minmax(320px,1fr)); gap:16px; }
  html.dt .gm-card{ padding:22px 22px; gap:18px; border-radius:16px; min-height:104px;
    transition:transform .16s ease, box-shadow .16s ease, border-color .16s ease; }
  html.dt .gm-card:hover{ transform:translateY(-3px); box-shadow:0 14px 34px rgba(0,0,0,.5); }
  html.dt .gm-card strong{ font-size:19px; }
  html.dt .gm-card em{ font-size:15px; line-height:1.4; }
  html.dt .gm-ico{ width:54px; height:54px; border-radius:14px; }
  html.dt .gm-ico svg{ width:28px; height:28px; }
  html.dt .gm-lock{ width:34px; height:34px; }

  /* ---- MONTAR AS INVERSÕES (game-setup): chips e piano de monitor ---- */
  html.dt .hm-chip{ font-size:14px; min-height:44px; padding:0 18px; }
  html.dt .gm-preview{ max-width:760px; }
  html.dt .gm-bench .audio-btn{ font-size:14px; padding:12px 22px; }
  /* C3 · em laptop pequeno o piano + CTA caíam abaixo da dobra: controles à esquerda,
     piano + ouvir + Treinar à direita, colados ao topo */
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="game-setup"]{
    max-width:var(--dt-max); display:grid; grid-template-columns:minmax(0,1.1fr) minmax(420px,.9fr);
    column-gap:clamp(32px,4vw,64px); row-gap:0; align-content:start; grid-auto-rows:min-content;
  }
  html.dt .page[data-page="game-setup"] > *{ grid-column:1; }
  html.dt .page[data-page="game-setup"] > .se-head{ grid-column:1 / -1; }
  html.dt .page[data-page="game-setup"] > .gm-preview{ grid-column:2; grid-row:2; margin:0; max-width:none; align-self:start; }
  html.dt .page[data-page="game-setup"] > .gm-bench{ grid-column:2; grid-row:3; }
  html.dt .page[data-page="game-setup"] > .practice-cta{ grid-column:2; grid-row:4; align-self:start; }
  html.dt .page[data-page="game-setup"] > .hm-controls{ grid-row:2 / span 3; }

  /* ---- CAMPO HARMÔNICO: usa a largura, cartões maiores ---- */
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="harmony"]{ max-width:var(--dt-max); }
  /* C3 · carrosséis (tom / escala / complexidade) empilhados à ESQUERDA, como no celular
     (o JS que dimensiona os cartões assume uma faixa por linha), e os acordes numa segunda
     coluna à direita: a ação fica acima da dobra mesmo em laptop pequeno */
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="harmony"]{
    display:grid; grid-template-columns:minmax(420px,500px) minmax(0,1fr);
    column-gap:clamp(32px,4vw,64px); row-gap:0; align-content:start; grid-auto-rows:min-content;
  }
  html.dt .page[data-page="harmony"] > *{ grid-column:1; }
  html.dt .page[data-page="harmony"] > .toc-hero{ grid-column:1 / -1; }
/* [16/09 · LOTE 4] regra de `.hm-grid` RETIRADA: a classe saiu do DOM com a grelha dos sete (ver a lápide dos blocos 18-18e). */
  html.dt .page[data-page="harmony"] .hm-dock-wrap{ --pgpad:0px; }
/* [16/09 · LOTE 4] regra de `.hm-grid` RETIRADA: a classe saiu do DOM com a grelha dos sete (ver a lápide dos blocos 18-18e). */
  html.dt .hm-card{ padding:20px; min-height:118px; }
  html.dt .hm-cifra{ font-size:32px; }
  html.dt .hm-nome{ font-size:14px; }
  html.dt .hm-roman{ font-size:13px; }
  /* [16/09 · LOTE 4] `html.dt .hm-prat` retirada: a pílula saiu do DOM. */

  /* ---- INVERSÕES (songs): lista em grelha + CTA à largura da coluna ---- */
  html.dt .songs-list{ max-width:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(340px,1fr)); gap:14px; }
  html.dt .songs-list .tabroot-soon-card{ grid-column:1 / -1; max-width:720px; }
  html.dt .songs-list .tabroot-soon-card p{ font-size:16px; }
  html.dt .practice-cta.songs-new{ max-width:720px; }
  html.dt .practice-cta{ min-height:76px; }
  html.dt .practice-cta-title{ font-size:20px; }
  html.dt .practice-cta-sub{ font-size:14px; }

  /* ---- EDITOR DA MÚSICA (song-edit): formulário à esquerda, modos à direita ---- */
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="song-edit"]{
    max-width:var(--dt-col); display:grid; grid-template-columns:minmax(0,1.05fr) minmax(360px,.95fr);
    column-gap:clamp(36px,4vw,64px); row-gap:0; align-content:start; grid-auto-rows:min-content;
  }
  html.dt .page[data-page="song-edit"] > *{ grid-column:1; }
  html.dt .page[data-page="song-edit"] > .se-modos{ grid-column:2; grid-row:1 / span 6; align-self:start; margin-top:8px; }
  html.dt .page[data-page="song-edit"] > .se-actions{ grid-column:1 / -1; margin-top:30px; }
  html.dt .se-name{ font-size:34px; }
  html.dt .se-chords{ font-size:18px; min-height:96px; }
  html.dt .se-modo{ padding:18px 20px; gap:16px; }
  html.dt .se-modo strong{ font-size:17px; }
  html.dt .se-modo em{ font-size:14px; }

  /* ---- VER / MONTAR HARMONIA (song-view, song-build): coluna de leitura ---- */
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="song-view"],
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="song-build"]{ max-width:var(--dt-narrow); }
  html.dt .sv-lista{ gap:22px; }
  html.dt .se-cards{ gap:12px; }

  /* ---- QUIZ: coluna de leitura mais larga, tudo maior ---- */
  html.dt .qz-body{ max-width:840px; }
  html.dt .qz-title{ font-size:30px; }
  html.dt .qz-prompt{ font-size:22px; }
  html.dt .qz-option{ min-height:56px; font-size:17px; }

  /* ---- MÚSICAS (hub) ---- */
  html.dt .sc-hub-page .ms-cards{ max-width:1080px; gap:24px; }

  /* ---- CURSO DE PARTITURA (sc-lista): módulos em 2 colunas, tipografia de monitor ---- */
  html.dt .sc-hub{ max-width:none; padding:0 0 28px; display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:14px; align-items:start; }
  html.dt .sc-module-head{ padding:18px 18px; gap:14px; }
  html.dt .sc-module-t{ font-size:18px; }
  html.dt .sc-module-tag{ font-size:12px; }
  html.dt .sc-module-prog{ font-size:14px; }
  html.dt .sc-lesson{ padding:14px 16px; }
  html.dt .sc-lesson-t{ font-size:15.5px; }

  /* ---- MINHAS MÚSICAS: hub em grelha; a FOLHA em duas colunas (pautas | painel) ---- */
  html.dt .mp-hub .sc-lesson, html.dt .mp-hub .sc-module{ font-size:16px; }
  html.dt .mp-hub{ display:grid; grid-template-columns:repeat(auto-fit,minmax(360px,1fr)); gap:14px; margin-top:22px; }
  html.dt .mp-hub > *{ margin:0; }
  html.dt .mp-folha{ display:grid; grid-template-columns:minmax(0,1fr) minmax(320px,380px);
    column-gap:clamp(32px,3.6vw,56px); row-gap:0; align-items:start; }
  html.dt .mp-folha > *{ grid-column:1; }
  html.dt .mp-folha > .mp-folha-foot{ grid-column:2; grid-row:1 / span 4; position:sticky; top:56px; bottom:auto;
    padding:0; background:none; align-self:start; }
  html.dt .mp-folha-hint{ font-size:16px; }
  html.dt .mp-folha-pautas{ max-width:840px; gap:14px; }
  html.dt .mp-folha-util{ flex-wrap:wrap; gap:10px; }
  html.dt .mp-util-btn{ flex:1 1 44%; min-height:64px; font-size:12px; }
  html.dt .mp-folha-foot .practice-cta{ margin-top:14px; }
  html.dt .mp-tom-row{ max-width:840px; }
  html.dt .mp-tom-chip em{ font-size:12px; }
  html.dt .mp-tom-lbl{ font-size:16px; }
  /* C2 · rótulos de 10–11 px do celular sobem para o piso de 12 px */
  html.dt .toc-phase-pill{ font-size:12px; }
  html.dt .sc-module-tag, html.dt .sc-lesson-n{ font-size:12px; }
}
/* janela de computador estreita (900–1100): as duas colunas viram uma, sem perder a escala */
@media (min-width:600px) and (max-width:1100px){
  html.dt .mp-folha{ grid-template-columns:minmax(0,1fr); }
  html.dt .mp-folha > .mp-folha-foot{ grid-column:1; grid-row:auto; position:sticky; top:auto; bottom:0; padding:12px 20px 10px; }
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="song-edit"]{ grid-template-columns:minmax(0,1fr); }
  html.dt .page[data-page="song-edit"] > .se-modos{ grid-column:1; grid-row:auto; }
  html.dt .sc-hub{ grid-template-columns:minmax(0,1fr); }
  html.dt body.mode-interactive .page.tabroot-page.visible[data-page="game-setup"]{ grid-template-columns:minmax(0,1fr); }
  html.dt .page[data-page="game-setup"] > .gm-preview, html.dt .page[data-page="game-setup"] > .gm-bench,
  html.dt .page[data-page="game-setup"] > .practice-cta{ grid-column:1; grid-row:auto; }
  html.dt .page[data-page="game-setup"] > .hm-controls{ grid-row:auto; }
}


/* 🗑️ [13/09 · Luis: "remover botao de ativar acesso da pagina musicas. deixar
 * apenas no menu do perfil do usuario e no paywall"] O BLOCO `.home-ativar`
 * (a faixa "Ativar acesso" da home, 11/09) SAIU INTEIRO — markup, delegate,
 * chave i18n e estas regras. As portas que ficam: o modal da conta
 * (`#account-btn`) e o rodape da carta (`.pw-ativar`). */

/* ===================================================================== SUPCHAT
 * [SUPCHAT 12/09] O sino e a conversa. Regras NO FIM do arquivo de proposito:
 * a guarda (h) do deploy exige que o payload COMECE com o arquivo do ar byte a
 * byte, entao tudo o que e novo entra por baixo.
 * ========================================================================== */

/* 🔑 O SINO E OUTRO ELEMENTO, nao o .account-dot — que ja e a OFENSIVA
 * (regra la em cima, .account-btn.has-streak). Duas noticias diferentes nao
 * cabem no mesmo pixel: uma acende, a outra some, e o aluno nao sabe qual
 * esta a falar. Por isso a ofensiva fica no canto de CIMA e o recado no de
 * BAIXO, e os dois podem estar acesos ao mesmo tempo. */
.account-btn .sup-sino{
  position:absolute; bottom:-3px; right:-3px; min-width:18px; height:18px;
  padding:0 4px; border-radius:9px; background:#E5484D; color:#fff;
  font:700 11px/18px system-ui,-apple-system,sans-serif; text-align:center;
  border:2px solid var(--bg,#0f1115); pointer-events:none;
}

/* A conversa dentro do modal de suporte. Some enquanto nao ha nenhuma: quem
 * escreve pela primeira vez ve o formulario de sempre, sem caixa vazia. */
.sup-conversa{
  display:flex; flex-direction:column; gap:8px;
  max-height:min(46vh,320px); overflow-y:auto; overscroll-behavior:contain;
  margin:0 0 14px; padding:2px;
}
.sup-bolha{
  max-width:82%; padding:9px 13px; border-radius:14px;
  white-space:pre-wrap; word-break:break-word; font-size:14px; line-height:1.45;
}
.sup-minha{ align-self:flex-end;   background:var(--card,#2a3346); border-bottom-right-radius:4px; }
.sup-deles{ align-self:flex-start; background:rgba(80,200,120,.14);  border-bottom-left-radius:4px; }

/* Em conversa, o formulario encolhe: nao se escolhe "suporte ou sugestao" no
 * meio de um assunto que ja comecou, e o e-mail ja se sabe. */
.sup-modal[data-conversa="1"] .sup-tipos,
.sup-modal[data-conversa="1"] .sup-input,
.sup-modal[data-conversa="1"] .sup-nota{ display:none; }
.sup-modal[data-conversa="1"] .sup-msg{ min-height:0; }

/* ═══════════════════════════════════════════════════════════════════════════
   O TABLET E A PAISAGEM UNIVERSAL — [15/09 · ordem do Luis]
   "num tablet de 10 polegadas o app tem botões ridiculamente pequenos, nomes,
   letras, textos totalmente desproporcionais — mantém o mesmo tamanho do
   celular, espalhado na tela inteira" + "num fluxo de prática normal o usuário
   tem que remover o dispositivo do tripé dezenas de vezes seguidas".

   TRÊS CLASSES, POSTAS PELO MESMO BLOCO DE JS (index.html, junto ao `dt`):
     html.dt  GEOMETRIA — "a caixa é larga". Deixou de perguntar pelo rato: o
              portão era `(min-width:900px) and (pointer:fine)` e um tablet
              responde `coarse`, logo as 253 regras de layout largo NUNCA lhe
              chegavam. Agora a régua é o LADO CURTO (≥600 no toque), que é
              exatamente o que os dois avisos desta casa pediam — app.css:7884
              ("um tablet em pé tem largura de sobra") e app.css:8148 ("a
              largura da JANELA mente sobre a caixa": o Pro Max deitado tem
              janela de 932 e container de 651; lado curto 430, fica de fora).
     html.tb  INPUT — "largo E dedo" (`any-pointer:coarse`). É esta camada, e
              só ela, que engorda alvo e tipografia. `any-pointer` e não
              `pointer` porque o iPad com teclado Magic responde `pointer:fine`
              e continua a ser um ecrã que se toca.
     html.dc  FORMA CURTA — deitado e baixo (≤500 px de altura): o telemóvel no
              tripé. Só ENCOLHE o eixo vertical; não inventa layout.

   🔑 ESTE BLOCO SÓ ACRESCENTA. Vive no FIM do ficheiro de propósito: `html.tb X`
   e `html.dt X` pesam o mesmo (0,2,0), e quem desempata é a ordem. Assim a
   camada de toque vence o bloco de computador sem um único `!important` — menos
   os DOIS que o `[class*="grid"]…!important` de app.css:4269 obriga.

   Escape sem deploy: `?tb=0` (o portão de hoje, ao byte) e `?tb=1` (força).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · OS TOKENS ──────────────────────────────────────────────────────────
   A casa não tem escala: 4 962 declarações em px, ZERO `rem`, e o
   `html{font-size:14px}` (app.css:33) não tem um único consumidor — mexer nele
   não move nada, e converter 4 962 declarações é o "500 consertos depois" que
   o Luis vetou. A escala entra como o `--pb-btn` (app.css:1780) já faz há
   meses: um token regravado por contexto.
   Os valores de `:root` são os do TELEMÓVEL DE HOJE. Nada muda sem `.tb`. */
:root{
  --tt-tap:44px;
  --tt-fz-micro:11px; --tt-fz-body:14px; --tt-fz-lead:15px; --tt-fz-h2:22px;
  --tt-sp-1:8px; --tt-sp-2:14px; --tt-sp-3:22px;
}
html.tb{
  --tt-tap:48px;
  --tt-fz-micro:14px;
  --tt-fz-body:clamp(17px, 1.7vmin + 13px, 19px);
  --tt-fz-lead:clamp(19px, 2vmin + 14px, 22px);
  --tt-fz-h2:28px;
  --tt-sp-1:12px; --tt-sp-2:20px; --tt-sp-3:30px;
}

/* ── 2 · O CONTRA-BLOCO DO "RESPONSIVE MOBILE" ──────────────────────────────
   `@media(max-width:900px)` (app.css:4228) está ATIVO num tablet em pé de 810
   px: ele trata um tablet como um telemóvel de 390. Não se lhe toca — o
   telemóvel depende dele. Desfaz-se aqui, e só o que faz mal. Medido: o
   `[class*="grid"]:not(.no-stack){…1fr!important}` atinge UMA classe viva no
   DOM inteiro: `.hm-grid`. (`.toc-grid` tem regra em app.css:4425 mas a palavra
   não aparece uma única vez no index.html — regra morta; não se escreve
   contra-regra para ela.) É por isso que este bloco tem 8 declarações e não 25,
   e um único `!important`. */
/* 🔴 [15/09] AQUI ESTAVA UMA REGRA MORTA, e fica a lápide em vez dela.
   `html.tb .special p, html.tb .special li{ font-size:var(--tt-fz-body) }`
   nasceu como contra-bloco do RESPONSIVE MOBILE (`max-width:900`), que põe
   13,5 px em `.special p/li` e apanha a caixa B (810 px) — o tablet em pé.
   MEDIDO com o censo (que grava x, y, w, h e font-size de cada elemento):
   tirá-la não muda NADA em `songs`, `score`, `games` nem `harmony`, que são
   as quatro telas do app com `p`/`li` dentro de `.special`. Zero diferenças.
   A razão é simples: cada `p` dessas telas traz a sua própria classe com
   font-size próprio (`practice-cta-sub`, `lead`...), e essa ganha por
   especificidade. O perigo dos 13,5 px nunca chegava a acontecer.
   Uma regra que não muda um pixel mas promete proteger o corpo do texto é
   a mesma coisa que um critério escrito no plano e ausente do código: dá
   confiança sem dar defesa. Sai a regra e sai o mutante M11 com ela. */
html.tb .special h3{ font-size:var(--tt-fz-h2); }
html.tb .callout p{ font-size:var(--tt-fz-body); }
html.tb .special h1{ font-size:clamp(34px, 4.4vw, 46px); }
html.tb .sidebar{ width:min(46vw, 360px); }
/* [16/09 · LOTE 4] regra de `.hm-grid` RETIRADA: a classe saiu do DOM com a grelha dos sete (ver a lápide dos blocos 18-18e). */

/* ── 3 · O ALVO DE TOQUE ────────────────────────────────────────────────────
   Piso de 48 px nos alvos primários e 44 nos restantes. O `.dt` já sobe alguns
   (tabbar-item 74, practice-cta 76); aqui ficam os que ele deixou de rato:
   o ← redondo é 42 px no computador, e 42 é menos que um dedo. */
html.tb .nav-tag.is-round{ width:var(--tt-tap); height:var(--tt-tap); }
html.tb .nav-tag.is-round svg{ width:18px; height:18px; }
html.tb .tabbar-item{ min-height:var(--tt-tap); }
html.tb .hm-chip{ min-height:var(--tt-tap); font-size:var(--tt-fz-body); }
html.tb .mp-util-btn{ min-height:64px; font-size:var(--tt-fz-micro); }
html.tb .audio-btn{ min-height:var(--tt-tap); }
html.tb .sc-lesson{ min-height:var(--tt-tap); }
html.tb .qz-option{ min-height:56px; }
html.tb .mp-tom-row{ min-height:var(--tt-tap); }
html.tb .fr-chip, html.tb .pb-tom{ min-height:var(--tt-tap); }

/* ── 4 · AS TARJAS ─────────────────────────────────────────────────────────
   `.practice-cta{width:100%}` (app.css:3561) numa tela de 1080 px é uma tarja
   laranja de ponta a ponta. Um botão não atravessa a tela. */
html.tb .practice-cta{ max-width:620px; }
html.tb .practice-cta.songs-new{ max-width:620px; }

/* ── 5 · OS RÓTULOS DE 9–11 px ──────────────────────────────────────────────
   O `.dt` já sobe vários ao piso de 12 (app.css:10120-10122). Num tablet o
   piso é 14: um `ellipsis` ATIVO num rótulo de tabbar não é elegância, é texto
   cortado — e é o critério T7b que o cobra. */
html.tb .tabbar-label{ font-size:12.5px; letter-spacing:.04em; }
html.tb .toc-phase-pill{ font-size:var(--tt-fz-micro); }
html.tb .sc-module-tag, html.tb .sc-lesson-n, html.tb .u-tag{ font-size:var(--tt-fz-micro); }
html.tb .toc-phase-title{ font-size:var(--tt-fz-lead); }
html.tb .toc-phase-sub{ font-size:var(--tt-fz-body); }

/* ── 6 · O TELEMÓVEL DEITADO (html.dc) ─────────────────────────────────────
   844×390: a mesma grelha do `.dt`, com o orçamento vertical comprimido. O
   `.dt` gasta 152 px de padding em 390 de altura — 39 % do ecrã antes de se
   escrever uma letra. Aqui não há layout novo: há um modificador. */
html.dc body.mode-interactive .page.tabroot-page.visible,
html.dc body.mode-interactive .toc-page.visible,
html.dc body.mode-interactive .unit-intro.visible{ padding-top:14px; padding-bottom:16px; }
html.dc .toc-mega-title, html.dc .tabroot-page .toc-mega-title{ font-size:24px; letter-spacing:-.8px; }
html.dc .unit-intro h1{ font-size:26px; }
html.dc .toc-hero{ margin:0 0 12px; }
html.dc .unit-intro .character{ margin:10px 0 6px; font-size:16px; }

/* ── 7 · A INSTRUÇÃO CERTA ─────────────────────────────────────────────────
   🔴 DEFEITO QUE ESTÁ NO AR HOJE: uma página de RETRATO vista com o aparelho
   DEITADO acende `#rotation-overlay` — "Vire o celular · O exercício abre na
   horizontal" — que é a instrução CONTRÁRIA à que a pessoa precisa. O irmão
   certo existe a 30 linhas de distância (`#rotation-overlay-portrait`, "Gire
   para a vertical") mas só é aceso por CSS para quatro véus (app.css:1268), e
   perde por especificidade (`#id{display:none}` contra `body.x .classe`).
   Acontece no caminho mais comum de todos: sair da prática por TOQUE.
   O JS passa a distinguir o SENTIDO do desacordo; aqui escolhe-se o véu.
   Isto é independente da conversão das telas e cura sozinho — e é o que deixa
   um lote subir com metade das telas convertidas sem mentir ao aluno. */
body.rotation-locked-portrait #rotation-overlay{ display:none!important; }
body.rotation-locked-portrait #rotation-overlay-portrait{
  display:flex!important; position:fixed; inset:0; z-index:300;
  flex-direction:column; align-items:center; justify-content:center;
}

/* ── 8 · O TABLET EM PÉ VOLTA À COLUNA ─────────────────────────────────────
   🔴 MEDIDO no lote 1, e a própria casa já o tinha escrito em app.css:7884:
   "um tablet em pé tem largura de sobra e deve seguir em coluna". Dar-lhe o
   layout de computador inteiro a 810 px de largura produziu três defeitos de
   verdade, todos na caixa B (810×1080):
     · harmony    rolagem horizontal de 106 px (`minmax(420px,500px)` + a grelha
                  dos acordes não cabem em 810)
     · score      o cartão do hub em fila fica com 195 px de largura e 1148 de
                  ALTURA — a ação principal sai da janela de 1080
     · toc        o título da fase entra em `ellipsis` (278 contra 324 px)
   O bloco estreito que já existia (app.css:10125) cobre mp-folha, song-edit,
   sc-hub e game-setup; faltavam estas três. A condição é a ORIENTAÇÃO, não a
   largura — um iPad Pro em pé tem 1024 px e o defeito é o mesmo. */
@media (orientation:portrait){
  html.tb body.mode-interactive .page.tabroot-page.visible[data-page="harmony"]{ grid-template-columns:minmax(0,1fr); }
/* [16/09 · LOTE 4] regra de `.hm-grid` RETIRADA: a classe saiu do DOM com a grelha dos sete (ver a lápide dos blocos 18-18e). */
  html.tb .sc-hub-page .ms-cards{ flex-direction:column; max-width:none; }
  html.tb .ms-like .ms-cards{ flex-direction:column; }
  html.tb .toc-page .toc-phases{ grid-template-columns:repeat(auto-fit,minmax(340px,1fr)); }
}

/* ── 9 · A HARMONIA, CONTIDA (não redesenhada) ─────────────────────────────
   🔴 MEDIDO: com o trilho de 96 px a página da harmonia passou a empurrar o
   carrossel dos tons 6 px para fora — `scrollWidth` 816 numa janela de 810. O
   carrossel já clipa por si (`.hm-dock{overflow-x:auto}`, app.css:5965); o que
   transborda é o contentor dele, porque o `--cw` da faixa é calculado sobre
   `100vw` (app.css:5955) e `100vw` não sabe do trilho — é o caso que o próprio
   app.css:8148 descreve: "a largura da JANELA mente sobre a caixa".
   Aqui CONTÉM-SE, não se redesenha: a harmonia é tela do LOTE 3 e o desenho
   dela em paisagem tem gate próprio. `clip` e não `hidden` de propósito — não
   cria contexto de rolagem e não quebra os `sticky`/`fixed` de dentro. */
html.tb .page[data-page="harmony"]{ overflow-x:clip; }
/* E, já que a contenção sozinha deixava a faixa a encostar na borda direita
   (vão 40 à esquerda, 0 à direita), a harmonia fica com o desenho do TELEMÓVEL
   no toque até o lote 3 lhe dar o dela. Mesma especificidade do bloco `dt`
   (0,4,2 + atributo) e mais abaixo no ficheiro — ganha pela ordem, sem
   `!important`. É a regra desta casa aplicada a si mesma: não se entrega meia
   tela; ou está desenhada, ou fica como estava. */
html.tb body.mode-interactive .page.tabroot-page.visible[data-page="harmony"]{
  display:block; max-width:none;
}
/* [16/09 · LOTE 4] regra de `.hm-grid` RETIRADA: a classe saiu do DOM com a grelha dos sete (ver a lápide dos blocos 18-18e). */

/* ── 10 · O QUE A MEDIDA COBROU DEPOIS DO BLOCO 3 ──────────────────────────
   Alvos que sobraram abaixo do piso, um por um, medidos e não estimados:
     · o ☰ e o botão da conta vivem em `position:fixed` a 38×38 desde sempre —
       são os dois alvos mais usados do app e os menores da tela (app.css:484/508)
     · `.sc-sub-back`, o ← das telas-filhas, também 38
     · `.mp-modo-btn`, a chave partitura⇄fita, 34 px de alto
   E o `.mp-tom-row`, que a 840 px vira uma tarja de ponta a ponta. */
html.tb .menu-btn, html.tb .account-btn{ width:var(--tt-tap); height:var(--tt-tap); }
html.tb .nav-tag.is-round, html.tb .sc-sub-back{ width:var(--tt-tap); height:var(--tt-tap); }
html.tb .mp-modo-btn{ min-height:var(--tt-tap); }
/* 🔴 [19/09] e a CHAVE cresce junto, senão este piso de 48 transborda dela —
   ver o bloco do `.mp-modo`. Ordem: `tb` depois de `dt` porque o tablet acende
   as duas classes e a receita dele é a do rótulo de 14 px. */
html.dt .mp-modo{ min-height:64px; }
html.tb .mp-modo{ min-height:67.24px; }
html.tb .mp-tom-row{ max-width:620px; }
html.tb .mp-folha-hint{ font-size:var(--tt-fz-body); }

/* ── 11 · O TELEMÓVEL DEITADO, COMPRIMIDO ──────────────────────────────────
   O `dc` herda a grelha do `dt` (ver o comentário no index.html) e aqui paga-se
   o preço dela num quadro de 390 px de altura: o herói de monitor não cabe, e a
   coluna de texto a 99 caracteres por linha não se lê. */
html.dc .unit-intro h1{ font-size:26px!important; }
html.dc .toc-page .toc-mega-title, html.dc .tabroot-page .toc-mega-title{ font-size:24px!important; }
html.dc body.mode-interactive .toc-page.visible,
html.dc body.mode-interactive .unit-intro.visible,
html.dc body.mode-interactive .page.tabroot-page.visible{ padding-top:14px!important; padding-bottom:16px!important; }
html.dc .unit-intro .context{ max-width:52ch; font-size:13px; }
html.dc .unit-intro .character{ margin:8px 0 4px!important; font-size:15px; }
html.dc .toc-page .toc-hero{ top:14px; }

/* ── 12 · O QUE A 2.ª MEDIDA COBROU ────────────────────────────────────────
   · o `context` que eu próprio pus a 13 px no bloco 11 — abaixo do piso de 14
     que eu próprio escrevi. A régua apanhou-me a mim, que é para o que serve.
   · o CTA da unit-intro no telemóvel deitado caía aos 374 px num quadro de 390:
     os vãos de `clamp(…,4.8vh,50px)` da pilha somam mais do que o quadro tem.
   · o `<em>` da dica da folha e o herói da capa da música, cortado por 4 px. */
html.dc .unit-intro .context{ font-size:14px; }
html.dc .unit-intro .intro-stack{ margin-top:10px!important; }
html.dc .unit-intro .audio-btn-row{ margin-top:8px!important; }
html.dc .unit-intro .kbd-wrap{ margin-top:6px!important; }
html.dc .unit-intro .formulae{ margin:8px 0 4px!important; }
html.dc .practice-cta{ margin-top:10px!important; padding:12px 18px; }
html.tb .mp-folha-hint em, html.tb .mp-folha-hint b{ font-size:var(--tt-fz-body); }
html.tb .toc-hero.sc-sub-hero{ overflow:visible; }

/* ── 13 · OS ÚLTIMOS DEZ, medidos um a um ──────────────────────────────────
   · o ← das telas-filhas ficou em 42 no telemóvel deitado (o `dt` põe 42, o
     piso do dedo é 44): faltavam 2 px
   · a linha de tonalidade e o CTA da folha viravam tarja a 756 px no Pro Max
     deitado — as regras do bloco 10 eram só `tb`, e o telemóvel deitado é `dc`
   · o título da fase entrava em `ellipsis` no tablet em pé: num tablet há
     altura de sobra, então ele QUEBRA em vez de cortar (cortar texto num ecrã
     com espaço é o defeito, não a elegância)
   · o `<em>` da dica da folha ficou nos 12 px de origem
   · e o CTA da unit-intro no telemóvel deitado parava 9 px abaixo da dobra */
html.dc .nav-tag.is-round, html.dc .sc-sub-back{ width:44px; height:44px; }
html.dc .mp-tom-row, html.dc .practice-cta.mp-folha-praticar{ max-width:620px; }
html.tb .toc-phase-title, html.tb .toc-phase-sub{ white-space:normal; overflow:visible; text-overflow:clip; }
html.tb .mp-folha em, html.tb .mp-folha b, html.tb .mp-folha small{ font-size:var(--tt-fz-body); }
html.dc .unit-intro .u-tag{ margin-bottom:2px; }
html.dc .unit-intro .kbd-wrap{ margin-top:2px!important; }
html.dc .practice-cta{ margin-top:6px!important; padding:10px 18px; }

/* ── 14 · os sete últimos ──────────────────────────────────────────────────
   a chave partitura⇄fita (34 px) e o número da pauta (13 px) no toque, e os
   últimos 5 px que separavam o CTA da unit-intro da dobra em 844×390. */
html.dc .mp-modo-btn{ min-height:44px; }
html.tb .mp-folha-pauta-n{ font-size:var(--tt-fz-micro); }
html.dc .unit-intro .character{ margin:4px 0 2px!important; }
html.dc .unit-intro .formulae{ margin:6px 0 2px!important; }
html.dc .unit-intro .intro-stack{ margin-top:6px!important; gap:10px; }
/* os rótulos "Partitura" / "Fita" dentro da chave segmentada da folha. */
html.tb .mp-modo-btn span{ font-size:var(--tt-fz-micro); }

/* ── 15 · A CAPA DA MÚSICA EM PAISAGEM É DUAS COLUNAS ──────────────────────
   🔴 REPROVADA PELO LUIS, e com razão: "itens como o botão praticar assimétricos,
   espaços vazios não aproveitados, botões sobrepostos com visual claramente
   bugado". A régua tinha dado 174/0 — porque o critério da SOBREPOSIÇÃO estava
   escrito no plano e não estava implementado. Depois de o construir: 10 pares
   sobrepostos no telemóvel deitado e 9 no tablet, o maior com 51 751 px² (o CTA
   "Praticar partitura" em cima da pauta).

   A CAUSA: o `.mp-folha-foot` é uma barra `position:sticky; bottom:0` — desenho
   de telemóvel EM PÉ, onde a lista de pautas é longa e rola por baixo dela. Em
   paisagem sobram 390 px de altura e a barra passa a ser um tapa-olhos.

   A CURA é o arquétipo que o plano já nomeou (ARQ-3, "objeto + painel"): as
   pautas ocupam a coluna larga e rolam; exportar, tonalidade, a chave
   partitura⇄fita e o Praticar passam a ser um PAINEL à direita, que fica. É o
   mesmo desenho que o computador já tem desde julho (app.css:10107) — só que o
   bloco estreito de 600-1100 o desmanchava justamente nas caixas de tablet. */
@media (orientation:landscape){
  html.tb .mp-folha, html.dc .mp-folha{
    display:grid; grid-template-columns:minmax(0,1fr) minmax(272px,332px);
    column-gap:clamp(18px,2.6vw,40px); row-gap:0; align-items:start;
  }
  html.tb .mp-folha > *, html.dc .mp-folha > *{ grid-column:1; }
  html.tb .mp-folha > .mp-folha-foot, html.dc .mp-folha > .mp-folha-foot{
    grid-column:2; grid-row:1 / span 8; align-self:start;
    position:sticky; top:8px; bottom:auto;
    padding:0; background:none;
  }
  html.tb .mp-folha-util, html.dc .mp-folha-util{ flex-wrap:wrap; gap:8px; }
  html.tb .mp-util-btn, html.dc .mp-util-btn{ flex:1 1 44%; }
  html.tb .mp-folha-praticar, html.dc .mp-folha-praticar{ max-width:none; margin-top:12px; }
  html.tb .mp-tom-row, html.dc .mp-tom-row{ max-width:none; }
}
/* em PÉ o painel não tem onde caber ao lado: fica coluna única, mas o rodapé
   deixa de PAIRAR — numa tela de 1080 px de altura há espaço para ele fluir, e
   uma barra a tapar a pauta é o mesmo defeito, só que mais alto. */
@media (orientation:portrait){
  html.tb .mp-folha > .mp-folha-foot{ position:static; background:none; padding-top:18px; }
}
/* em pé, o rodapé volta a ser a barra do telemóvel (opaca, por cima do que rola
   — o desenho que o Luis já valida), e as pautas reservam o espaço dela para a
   última não ficar inalcançável. Em D2 a linha de tonalidade chegava a 732 px. */
@media (orientation:portrait){
  html.tb .mp-folha > .mp-folha-foot{
    position:sticky; bottom:0; padding:12px 20px calc(10px + env(safe-area-inset-bottom));
    background:linear-gradient(to top, var(--bg,#070707) 62%, transparent);
  }
  html.tb .mp-folha-pautas{ padding-bottom:180px; }
}
@media (orientation:landscape){
  html.tb .mp-tom-row, html.dc .mp-tom-row{ max-width:700px; }
}

/* ── 16 · A PRÁTICA NÃO ENTRA NO LAYOUT LARGO PELO TELEMÓVEL DEITADO ───────
   🔴 REGRESSÃO MINHA, medida pela régua da casa: `validate_barra_pratica` caiu
   de **126/0 para 63/63** — exatamente metade, que é uma janela inteira: a do
   Pro Max deitado (932×430). Quando fiz o `dc` herdar a grelha do `dt` (ARQ-5,
   para pôr o CTA da unit-intro na dobra), arrastei com ela o painel proporcional
   da prática (`--pw`) e os botões de 46/52 px — num quadro de 430 px de altura
   isso põe `pb-tom`, `pb-stages`, `pb-time-row` e os ícones uns por cima dos
   outros.

   E eu tinha PROMETIDO que isto não aconteceria: o plano dizia que o Pro Max
   deitado ficava fora do layout largo pela régua do lado curto (430 < 600). A
   promessa era verdadeira até eu própria a quebrar duas horas depois.

   A prática em paisagem é a tela que o Luis diz ser perfeita. Aqui ela volta,
   ao valor, ao que é sem `dt` (app.css:1753-1766 e 1780). O resto do `dc` fica:
   a Jornada e a página da unidade deitadas continuam com a grelha, que é o que
   ele validou. */
html.dc body.mode-interactive .practice-page.visible{
  inset:0; margin:0;
  max-width:100vw; max-height:100dvh;
  border:0; border-radius:0;
}
html.dc .practice-bar{ --pb-btn:40px; --pb-btn-fz:15px; }
html.dc .practice-page.visible .practice-bar{ min-height:clamp(140px, 35vh, 220px); }

/* ── 17 · O EDITOR DE UMA PAUTA EM PAISAGEM (ARQ-3) · LOTE 2 ────────────────
   🔴 A ÚLTIMA VIRADA DO TRIPÉ. O `fluxo.cjs` mede 26 passos com a caixa presa
   deitada e exige 2 viradas — as DUAS são esta tela, uma por caixa (C e A1).
   `mp-edit` nasceu `data-orient="portrait"` em 29/07 ("vertical only" foi
   pedido explícito), e a ordem de 15/09 revoga-o para TODAS as só-retrato:
   "todas as só-retrato passam a ter versão otimizada de paisagem".

   O MOLDE É O ARQ-3 do bloco 15 — objeto + painel: a pauta (o objeto) e as
   ações ficam na coluna larga; o anel de figuras (a paleta) vira painel na
   estreita. O `.mp-ed-rolo` dissolve-se em `display:contents` porque as três
   peças do miolo têm de ser itens da MESMA grelha que o piano — e a ordem do
   DOM não muda, que é o que o `validate_lote_29jul` cobra (`ordem` e
   `ordemMiolo` continuam a ler os mesmos filhos).

   🔑 O PIANO TEM DOIS DESTINOS, e é MEDIDA, não gosto:
     · no TABLET (`tb`) atravessa as duas colunas — há altura de sobra (810 e
       1024 px) e a tecla branca passa a ~60 px, que é o dedo à vontade;
     · no TELEMÓVEL DEITADO (`dc`) fica na coluna da pauta. Medido: com o piano
       a atravessar, sobram 210 px de altura para o anel num quadro de 390 —
       e a 210 px o aro interno não chega a 44 px sem entrar por cima das
       figuras. Na coluna, o anel recebe os 336 px inteiros. A tecla fica a
       ~31 px, que é a MESMA do telemóvel EM PÉ que o Luis já validou: nada
       piora, o anel melhora.

   🔑 E O ANEL DEIXA DE SE MEDIR PELO ECRÃ. `min(20vw, 9vh, 84px)` é o tamanho
   da figura hoje: em RETRATO o `9vh` é generoso, DEITADO vale 35 px e o alvo
   morre. A cura não é outro teto em `vh` — é a percentagem, que é a régua que
   o próprio anel já usa para POSICIONAR (`left:50+38*sin`). A figura passa a
   ser 17 % do anel e acompanha-o em qualquer caixa.

   🔑 A ARITMÉTICA DO ARO INTERNO, porque ela é que manda no tamanho do alvo.
   As figuras moram a 38 % do lado do anel (S) e os quatro modificadores a
   21 %. A folga entre o modificador e a figura que está no mesmo raio é
      folga = 0,17·S − F/2 − T/2
   com F = tamanho da figura e T = o alvo do modificador. Com F = 0,17·S:
      T ≤ 0,17·S  →  S ≥ T / 0,17·… ⇒ 48 px pede S ≥ 320 · 44 px pede S ≥ 260.
   É POR ISSO que o anel ganha teto de 480 px no toque em vez dos 340 de hoje:
   a 340 px com figura de 84 px o aro interno não passa de 31 px — e os 33 px
   de hoje já encostam na figura (0,7 px, que o V3 só não acusa porque exige
   2 px). Crescer o anel é o que PAGA o alvo de 48.

   Escape: `?tb=0` (tira `tb`/`dc` e devolve `data-orient` ao que o ar tem). */

/* ── 17a · O ANEL NO TOQUE (as duas orientações) ────────────────────────────
   Isto é dívida do anel, não da paisagem: na base a régua já reprovava o
   `mp-edit` nas caixas B e D1 (tablet EM PÉ) por E4 e E5 — `mp-tog` a 33 px,
   o pill "2,3…" a 63×26 com texto de 9,5 px, e as ações a 41 px de altura. */
html.tb .mp-roda-grande, html.dc .mp-roda-grande{
  /* o aro interno desenha-se com `scale(1.45)` e o pill com `scale(1.12)`:
     a base é o alvo DIVIDIDO pela escala, senão o que se lê na tela não é o
     que se escreveu aqui. */
  --mp-tog-b:calc(var(--tt-tap) / 1.45);
  --mp-tup-b:calc(var(--tt-tap) / 1.12);
}
/* 🔴 O ANEL TEM DE SER QUADRADO, E `max-width`+`max-height` NÃO O GARANTEM.
   Medido depois da 1.ª volta: C deu 386×480 e D2 436×480 — ELIPSE. A causa é
   a receita de origem (`height:100%` + `width:auto` + `aspect-ratio:1/1`): com
   a altura DEFINIDA, o `aspect-ratio` dá a largura, mas quando o `max-width`
   da coluna a corta a altura NÃO volta atrás. Em retrato os dois tetos valiam
   o mesmo (`min(88vw,340px)`) e o quadrado saía por acaso; numa grelha de duas
   colunas deixam de valer.
   E a roda posiciona TUDO em percentagem (`left:50+38*sin`), logo uma caixa
   que não é quadrada não encolhe o anel: deforma-o, e os raios de que depende
   a folga do aro interno deixam de ser os que a aritmética deste bloco supõe.
   A cura pede a medida que falta — o MENOR lado da caixa que hospeda o anel —
   e isso é o que `cqmin` dá. Container query é a ferramenta que a casa já usa
   (a folha do PDF, app.css:7156). */
html.tb .mp-ed-anel, html.dc .mp-ed-anel{ container-type:size; }
html.tb .mp-roda-grande, html.dc .mp-roda-grande{
  width:min(100cqmin, 480px); height:min(100cqmin, 480px);
  max-width:none; max-height:none;
}
/* a figura é FRAÇÃO do anel (17 %), nunca do ecrã */
html.tb .mp-roda-grande .mp-fig, html.dc .mp-roda-grande .mp-fig{
  width:17%; height:17%; margin:-8.5% 0 0 -8.5%;
}
html.tb .mp-roda-grande .mp-fig svg, html.dc .mp-roda-grande .mp-fig svg,
html.tb .mp-roda-grande .mp-fig-pausa svg, html.dc .mp-roda-grande .mp-fig-pausa svg{
  width:52%; height:68%;
}
html.tb .mp-roda-grande .mp-tog, html.dc .mp-roda-grande .mp-tog{
  width:var(--mp-tog-b); height:var(--mp-tog-b);
  margin:calc(var(--mp-tog-b) / -2) 0 0 calc(var(--mp-tog-b) / -2);
}
html.tb .mp-roda-grande .mp-tog svg, html.dc .mp-roda-grande .mp-tog svg{
  width:calc(var(--mp-tog-b) * .52); height:calc(var(--mp-tog-b) * .65);
}
/* o pill "2,3…": a altura é o alvo, a largura 1,5× (é texto, não glifo), e o
   corpo sobe ao piso de micro — 9,5 px é o menor texto do app inteiro. */
html.tb .mp-roda-grande .mp-tog-tup, html.dc .mp-roda-grande .mp-tog-tup{
  width:calc(var(--mp-tup-b) * 1.5); height:var(--mp-tup-b);
  margin:calc(var(--mp-tup-b) / -2) 0 0 calc(var(--mp-tup-b) * -.75);
  font-size:var(--tt-fz-micro); border-radius:calc(var(--mp-tup-b) / 2);
}
/* 🔴 O REGISTO DO PIANO (◀ Dó4–Dó6 ▶) E O NÚMERO DA PAUTA. Estes dois já
   estavam abaixo do piso ANTES deste lote — na base a régua contava 9 alvos
   curtos na caixa B e só mostra 6, e o `mp-oct-btn` era um dos escondidos.
   Ficaram à vista quando os outros sete foram curados, que é como uma lista
   truncada devolve dívida: por camadas.
     · `.mp-oct-btn{height:min(32px,3.8vh)}` dá 15 px num quadro de 390 — outra
       peça medida pelo ECRÃ em vez de pela própria caixa;
     · `.mp-ed-pauta-n` ("Pauta 2 de 4") fica a 11,5 px, abaixo do piso de 14
       do tablet. No telemóvel deitado o piso é 11, e lá ele já passa — por isso
       só sobe no `tb`: não se mexe no que está no número. */
html.tb .mp-oct-btn, html.dc .mp-oct-btn{
  width:var(--tt-tap); height:var(--tt-tap);
}
/* 🔑 A LINHA DA OITAVA INTEIRA, de uma vez. O E4 devolveu esta dívida por
   CAMADAS — 9,5 px, depois 11,5, depois 12,5 — porque cada cura destapava o
   texto seguinte. Três voltas a curar um elemento de cada vez é a régua a
   escolher o trabalho; então sondou-se o computado de TODO o texto da tela nas
   três caixas e a lista fechou em três nomes, os desta linha. No telemóvel
   deitado (piso 11) não há nenhum: por isso a linha só sobe no `tb`. */
html.tb .mp-oct-btn, html.tb .mp-oct-lbl{ font-size:var(--tt-fz-micro); }
html.tb .mp-ed-pauta-n{ font-size:var(--tt-fz-micro); }

/* as ações do editor e a zona quente da clave, medidas a 41 px */
html.tb .mp-ed-acts .mp-act, html.dc .mp-ed-acts .mp-act{
  min-height:var(--tt-tap); font-size:var(--tt-fz-body);
}
html.tb .mp-clef-hot, html.dc .mp-clef-hot{
  min-width:var(--tt-tap); min-height:var(--tt-tap);
}

/* ── 17b · A PAISAGEM DO EDITOR — SAIU · LÁPIDE ────────────────────────────
   🔴 [16/09 · LOTE 4 · ORDEM DO LUIS: "editor de partitura será vertical only"]
   Aqui viviam as 59 linhas que davam paisagem ao `mp-edit`: o orçamento vertical
   (fora o respiro de monitor), a largura inteira no tablet e a grelha ARQ-3 do
   telemóvel deitado. Saíram com a mesma ordem que pôs `data-orient="portrait"`
   de volta no `index.html` — com o lock a exigir retrato, esta @media NUNCA
   casa, e CSS que não pode casar é promessa a envelhecer em silêncio.
   Fica a lápide, e não o silêncio, porque este atributo já teve TRÊS valores em
   dois dias: quem voltar aqui tem de ler a ordem antes de re-desenhar a tela.
   O que NÃO saiu, porque não é da paisagem: o bloco **17a** (o anel no toque —
   alvos de 48 px, o anel quadrado por `cqmin`, a linha da oitava) vale nas DUAS
   orientações e cura dívida que a régua já acusava no tablet EM PÉ; e o **18i**
   (o anel a descontar a fileira das vozes), que é aritmética do 17a.
   Recuperar: `git show HEAD:app.css` não serve — isto nasceu no lote 2 e nunca
   foi commitado; o exemplar está em `TABLET-BANCADA/bloco17.css`. */


/* ── 18 · 18b · 18c · 18d · 18e — A HARMONIA DO LOTE 3 · LÁPIDE ───────────
   🔴 [16/09 · LOTE 4] Os cinco blocos desenhavam uma coisa que deixou de existir:
   a GRELHA DE SETE CARTÕES de acorde. O Luis reprovou-a ("nao gostei das telas de
   campo harmonico") e mandou outra tela — os sete viram uma FILEIRA GIRÁVEL de
   blocos quadrados, e o `.hm-grid` saiu do DOM. Rodapé por rodapé:
     18  · o orçamento vertical em paisagem e a grelha ARQ-3 do telemóvel deitado
     18b · os pisos de alvo/letra do cartão-linha
     18c · o cartão a virar COLUNA para o nome do acorde deixar de ter largura 0
     18d · quatro colunas cravadas, para sete não fechar em 5+2
     18e · a coluna dos slots a não comer a tela no telemóvel deitado
   Não é desenho que se corrige: é desenho que o dono trocou. Refazer por cima
   com regras a vencer regras dava uma tela feita de duas ideias.

   🔑 O QUE DELES SOBREVIVEU, e está pago no bloco 19e com outro corpo:
     · o piso de letra do `.hm-roman` (era 13 px; o piso do tablet é 14) — 18b;
     · o alvo do bloco ≥ `--tt-tap` — 18b;
     · o mini-teclado medido pela CAIXA e não pelo ecrã — 18e;
     · "caber numa tela", que era o pedido de 15/09 e continua a ser medido.
   O que NÃO sobreviveu porque perdeu o objecto: tudo o que fala de colunas,
   de `flex-wrap` do texto do cartão e do piso de min-content da LINHA.
   Exemplares: `TABLET-BANCADA/bloco18.css` e `.antes-harmonia/`. */


/* ── 18f · O PISO DE TEXTO DO TABLET, onde o corpo do texto REALMENTE mora ───
   A dívida estava escrita com número desde o lote 1: "nas telas com `p`/`li`
   dentro de `.special` — `songs`, `score`, `games` — o corpo de texto está
   abaixo do piso do tablet (14-16 px contra 17)". O lote 1 tentou curá-la com
   uma regra de TAG (`html.tb .special p, html.tb .special li`) e ela era INERTE:
   está a lápide no app.css:10296, e a razão é que cada parágrafo destas telas
   traz a sua própria CLASSE com font-size próprio, que ganha por especificidade.
   Escrever outra regra de tag seria repetir o M11.

   🔑 Então sondei o COMPUTADO de todo o nó de texto das quatro caixas de tablet,
   de uma assentada, e deixei a lista fechar sozinha — a lição do E4 do lote 2,
   onde curar um elemento de cada vez deu três voltas porque cada cura destapava
   a seguinte. A lista veio com a cadeia de ascendentes, e é esta:

     15px  EM < SPAN.gm-txt < BUTTON.gm-card      games (6) · ear-menu (5)
     16px  SPAN.ms-card-body < SPAN.ms-card-text  score (3)
     16px  P < DIV.tabroot-soon-card              songs (1)
     14px  SPAN.practice-cta-sub                  songs · game-setup · u1-intro · mp-folha
     12.5px .se-label                             game-setup (3) · song-edit
     12px  .audio-btn                             song-edit

   🔑 E o critério só cobra o que se LÊ como corpo: o E4 conta como "corpo" a
   folha de texto com **≥25 caracteres** (validate_tablet.cjs:208). Por isso o
   `.practice-cta-sub` reprova em `songs` ("Digite os acordes da música", 27) e
   não em `u1-intro` ("Iniciar com C (Dó)", 18) nem em `mp-folha` ("Pauta
   rolando", 13) — a mesma classe, o mesmo tamanho, e só uma delas é corpo.
   Sobe-se a classe inteira na mesma: uma frase e um rótulo curto não devem ter
   pisos diferentes por acaso do comprimento. */
html.tb .gm-card .gm-txt em{ font-size:var(--tt-fz-body); }
html.tb .ms-like .ms-card-body{ font-size:var(--tt-fz-body); }
/* 🔴 ESPECIFICIDADE outra vez, e desta vez apanhei-a pela MEDIDA e não pela
   leitura: escrevi `html.tb .tabroot-soon-card p` (0,2,1) e o número não se
   mexeu — o `songs` continuou a reprovar o E4 com o mesmo `P` a 16 px. Quem
   ganha é `html.dt .songs-list .tabroot-soon-card p` (app.css:10110), que tem
   (0,3,1). É a terceira vez nesta frente: a regra do `dt` traz sempre uma
   classe a mais, porque foi escrita para desempatar com o telemóvel. */
html.tb .songs-list .tabroot-soon-card p{ font-size:var(--tt-fz-body); }
html.tb .practice-cta-sub{ font-size:var(--tt-fz-body); }
/* micro (piso 14), não corpo: são RÓTULOS, não frases */
html.tb .se-label{ font-size:var(--tt-fz-micro); }
html.tb .audio-btn{ font-size:var(--tt-fz-micro); }

/* ── 18g · O HUB DAS MÚSICAS (`score`) — e um defeito MEU do lote 1 ─────────
   🔴 A CAUSA, e é a lição desta secção: **`flex:1 1 0` muda de significado
   quando o contentor vira coluna.** O `dt` desenhou o hub em LINHA e deu ao
   cartão `flex:1 1 0` (app.css:5488 e 7050) — base 0 no eixo principal, que
   numa linha quer dizer "repartam a largura em partes iguais". No lote 1 eu
   virei o contentor para coluna no tablet em pé (app.css:10390) e **deixei o
   `flex:1 1 0` lá**. Numa coluna o eixo principal é a ALTURA, e base 0 passou a
   querer dizer *não tenhas altura nenhuma*.
   Medido em B e D1: `height` computada **58 px** com `scrollHeight` 89, e o
   `.ms-card` tem `overflow:hidden` com `align-items:center` — logo o conteúdo
   de 89 px centra-se numa caixa de 58 e é cortado dos DOIS lados. O título
   ficava em `top:-4px` no primeiro cartão e **`top:-76px` no terceiro**: o
   "Enviar vídeo de Notas Caindo" não estava pequeno nem apertado — estava
   FORA do cartão, invisível, e o corpo do texto cortado a meio da frase.
   O V2 gritava isto desde o lote 1 (`c:56 · s:87`) e eu li-o como dívida da
   tela em vez de ir ver de onde vinha o 56. Foi a FOTO da caixa B que o disse.

   🔑 E o desenho em si, que é a outra metade: em paisagem o `dt` punha os três
   cartões lado a lado em colunas de **261 px** — com o corpo do texto a 19 px
   isso dá **16 caracteres por linha** ("Aprenda a / ler / partitura / do
   zero."), que é o T4b a reprovar pela ponta que quase nunca se vê: não é letra
   pequena, é LINHA CURTA. E os cartões esticavam a 645 px de altura numa janela
   de 810, o que tirava a ação principal da dobra (V1).
   Três cartões de texto não são três colunas: são uma LISTA. Empilham-se, com
   teto de 700 px (o E3 reprova botão-tarja acima de 720) e centrados — o mesmo
   gesto em pé e deitado, uma regra só para as quatro caixas. */
html.tb .sc-hub-page .ms-cards, html.dc .sc-hub-page .ms-cards{
  flex-direction:column; max-width:700px; margin-inline:auto; align-items:stretch;
}
html.tb .sc-hub-page .ms-card,  html.dc .sc-hub-page .ms-card,
html.tb .sc-hub-page .ms-card--vid, html.dc .sc-hub-page .ms-card--vid{ flex:0 0 auto; }
/* 🔑 O `dc` LEVA A MESMA REGRA, e escrevê-la só para `tb` foi engano meu de meia
   hora: o telemóvel DEITADO herda a grelha do `dt` (é para isso que o `dc`
   existe) mas NÃO tem `tb`. Medido: A1 ficava com cartões de 206 px e
   **6 caracteres por linha**, e a ação principal ia de 140 a 1244 numa janela
   de 390. Uma cura escrita para o tablet que deixa o telemóvel deitado de fora
   não é meia cura — é a mesma dívida com outra caixa a pagá-la. */

/* ── 18h · A CHAVE DAS DUAS VOZES (`.mp-voz`) ───────────────────────────────
   A dívida vinha medida do lote 2 e por curar: 34×26 px e 13 px de letra, num
   contexto onde o piso é 48 e 14. Não se curou lá porque o `[data-mp-vozes]`
   está `hidden` com uma peça de UMA voz e a régua nunca o media — escrever
   regra para um elemento que a régua não vê é uma promessa, não uma defesa.
   Agora a bancada tem `PECA=2vozes` (caixas.cjs) e ele passa a ser cobrado:
   `E5 2 alvos abaixo` e `E4 menor 13px` nas quatro caixas de tablet. */
html.tb .mp-voz, html.dc .mp-voz{
  width:var(--tt-tap); height:var(--tt-tap); min-height:0;
  font-size:var(--tt-fz-micro);
}

/* ── 18i · O ANEL DESCONTA A FILEIRA DAS VOZES ──────────────────────────────
   🔴 DEFEITO MEU DO LOTE 2, que só a peça de 2 vozes podia mostrar. O bloco 17
   deu ao anel `width/height: min(100cqmin, 480px)` sobre o `.mp-ed-anel`, e
   `100cqmin` é o contentor INTEIRO — escrito a contar que o anel fosse filho
   único. Não é: a árvore, medida, tem DOIS filhos —

       DIV.mp-vozes        h=48   ← só existe quando a peça tem 2 vozes
       DIV.mp-roda-grande  h=439

   — num contentor de 439 de conteúdo. 48 + 6 de gap + 439 = 493, e como o
   `.mp-ed-anel` é `justify-content:center` a sobra de 54 px sai pelos DOIS
   lados: 27 acima e 27 abaixo. O anel ficava a terminar 25 px POR BAIXO da sua
   própria caixa, em cima da fila de ações — `V3 mp-fig × mp-act`, área 311.
   Com a peça de 1 voz o `.mp-vozes` está `hidden`, não é caixa nenhuma, a conta
   fecha exacta e não há falha: **a regra estava errada desde o lote 2 e o que a
   escondia era a peça de prova.** É a mesma forma do `.mp-voz` — um estado que
   a bancada nunca visitava — só que aqui o que se escondia não era uma dívida
   conhecida, era um erro de aritmética meu.
   A cura desconta o irmão quando ele existe, e só então. */
html.tb .mp-ed-anel .mp-vozes:not([hidden]) ~ .mp-roda-grande,
html.dc .mp-ed-anel .mp-vozes:not([hidden]) ~ .mp-roda-grande{
  width:min(100cqmin, 100cqh - var(--tt-tap) - 6px, 480px);
  height:min(100cqmin, 100cqh - var(--tt-tap) - 6px, 480px);
}

/* ── 18j · `?hv=0` · O ESCAPE DOS SLOTS VERTICAIS — RETIRADO · LÁPIDE ──────
   🔴 [16/09 · LOTE 4] E ISTO É A COISA MAIS IMPORTANTE DESTA LÁPIDE, porque é
   uma PERDA e não uma arrumação.

   O `?hv=0` prometia devolver a harmonia do ar **ao byte**, e cumpria: os slots
   verticais eram só CSS, e `flex-direction:row` devolvia o carrossel horizontal
   inteiro — scroll, snap, zoom e a varredura do tour — porque o eixo tinha
   deixado de estar cravado no JS (`window.__hmEixo` pergunta ao CSS).

   O lote 4 tira-lhe o objecto. A tela nova não é outra camada de CSS: é outro
   **DOM** — o `.hm-grid` com sete `.hm-card` deu lugar a uma quarta `.hm-dock`
   de blocos, e nasceu um botão PRATICAR que abre uma página nova. Nenhum
   `data-hv` traz de volta markup que não foi escrito. O que eu poderia fazer era
   um escape que devolvia METADE — os três slots à horizontal e a fileira dos
   graus como está — e isso é exactamente o que 15/09 me ensinou a não fazer:
   *"um escape que desliga metade do lote não é um escape: é um rollback que
   mente"*. Então o escape SAI, com o nome, em vez de ficar a mentir.

   🔴 CONSEQUÊNCIA, e está escrita também no `prova.sh`: depois deste lote a
   harmonia (e a tela Músicas, pelo mesmo motivo — o cartão novo vale em TODOS os
   aparelhos, por ordem dele) **deixam de ter volta dentro do mesmo build**. A
   volta delas é o `git` ou o `wrangler rollback`. O `?tb=0` continua honesto no
   que lhe resta: a camada de tablet das outras dezassete telas. */


/* ── 18k · O EDITOR COM DUAS VOZES EM PAISAGEM — SAIU · LÁPIDE ─────────────
   🔴 [16/09 · LOTE 4] Saiu pela mesma ordem do 17b: o editor é vertical-only,
   logo `@media (orientation:landscape)` nunca casa nesta tela. Eram as 43 linhas
   que punham a peça de 2 vozes em grelha abaixo de 892 px de altura.
   A dívida que ele curava de verdade — o `.mp-voz` de 34×26 px — está no **18h**
   e FICA, porque é alvo de dedo no tablet EM PÉ, onde a tela continua a existir.
   Exemplar: `TABLET-BANCADA/bloco18.css`. */

/* ═══════════════════════════════════════════════════════════════════════════
   BLOCO 19 · O VEREDITO DO LUIS — 16/09/2026, de manhã, com a galeria à frente
   ═══════════════════════════════════════════════════════════════════════════
   Sete pontos ditos por ele depois de ver as 66 fotos do lote 3. Dois deles
   REVOGAM trabalho dos lotes 2 e 3, e por isso a primeira coisa que este bloco
   faz é tirar, não pôr (ver as lápides dos blocos 17b e 18k).

   🔑 A REGRA NOVA QUE MANDA EM TUDO AQUI, e que eu não tinha:
     *"não deve haver tanta diferenciação entre dispositivos. tela horizontal
       fica tudo igual, apenas adaptação de tamanho."*
   Confirmado por ele: vale entre TABLETS e MONITORES. O telemóvel deitado
   (`dc`, 390 px de altura) continua com resposta própria — 390 px não são um
   tamanho menor de 810, são outro problema, e ele concordou.
   E em RETRATO vale a regra irmã: *"vertical no tablet deve ter apenas
   adaptações de tamanho da fonte"* — o MIOLO das telas verticais não se
   reorganiza. (O trilho à esquerda FICA: perguntei, e ele escolheu ficar.)
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 19a · A FOLHA: UM BOTÃO POR LINHA (ponto 06) ───────────────────────────
   *"os botoes exportar midi, pdf, nova pauta apagar, estao 2 botoes por linha,
     e acaba sobrando muito espaco embaixo. faca cada botao em uma linha ao lado
     direito da folha."*

   O painel da direita já é o ARQ-3 do bloco 15; o que ele reprova é a REPARTIÇÃO
   dentro dele — `flex:1 1 44%` dá duas fichas por linha e deixa a coluna a
   terminar a meio da tela. Com uma por linha o painel passa a ler-se como uma
   LISTA de ações, que é o que quatro utilidades são.

   🔑 E o `dt` entra JUNTO, que é o ponto 03 a agir: o monitor tinha a mesma
   regra de 44 % (app.css:10225) e é a mesma tela. `:not(.dc)` porque o telemóvel
   deitado também acende `dt` — lá as quatro fichas empilhadas pediriam 260 px
   de um quadro de 390, e a ordem dele foi que o telemóvel deitado fica de fora.

   🔑 A FICHA DEIXA DE SER COLUNA E VIRA LINHA. Uma ficha de ícone-sobre-rótulo
   com 100 % de largura é um alvo largo com o conteúdo a boiar no meio; em lista,
   o ícone à esquerda e o rótulo a seguir é o que a própria casa já faz no
   `.ms-card` e no `.gm-card`. E o `min-height:2.16em` do rótulo (que reservava
   DUAS linhas para os quatro ficarem iguais) deixa de fazer sentido quando a
   largura é a do painel inteiro: o mais longo cabe em uma. */
@media (orientation:landscape){
  html.tb .mp-folha-util,
  html.dt:not(.dc) .mp-folha-util{ flex-direction:column; flex-wrap:nowrap; gap:8px; }
  html.tb .mp-util-btn,
  html.dt:not(.dc) .mp-util-btn{
    flex:0 0 auto; width:100%;
    flex-direction:row; justify-content:flex-start; gap:12px;
    padding-left:16px; padding-right:16px; text-align:left;
  }
  html.tb .mp-util-btn span,
  html.dt:not(.dc) .mp-util-btn span{ min-height:0; }
  /* [19/09] no painel deitado a ficha é uma linha e quem manda é o
     `min-height:64` dela — a chave acompanha aí também. */
  html.tb .mp-modo{ min-height:64px; }
  html.tb .mp-util-btn svg,
  html.dt:not(.dc) .mp-util-btn svg{ width:18px; height:18px; }
}

/* ── 19b · A TELA MÚSICAS: O CARD NOVO (ponto 04) ───────────────────────────
   *"a tela musicas, eu quero algo como a m1 monitor. mas os cards ainda nao
     ocupam a tela inteira. coloque o icone do card no canto superior esquerdo,
     o titulo do card ao lado a esquerda. e o texto embaixo, alinhado a esquerda
     e nao centralizado no card."*

   🔴 O QUE ISTO REVOGA, e é preciso dizê-lo em voz alta: o bloco **18g** decidiu
   que "três cartões de texto não são três colunas, são uma LISTA" e empilhou-os
   nas quatro caixas de tablet. O argumento era medido (colunas de 261 px davam
   16 caracteres por linha). O Luis viu as duas e escolheu a LINHA — "algo como a
   m1 monitor". A medida que eu tinha continua verdadeira; ela media a linha
   CURTA, e a cura dele é outra: a linha deixa de ser curta porque o texto passa a
   correr por BAIXO do ícone, com a largura toda do cartão, em vez de ficar
   entalado na coluna que sobra ao lado dele. Os 16 caracteres por linha eram um
   sintoma do desenho do cartão, não do número de colunas.

   🔑 A GEOMETRIA, e é uma grelha e não um flex por uma razão: o pedido tem o
   ícone e o título na MESMA linha e o texto a atravessar os dois. Isso é
   exactamente uma grelha de 2×2 com a 2.ª linha a fazer `span 2` — em flex
   precisaria de um invólucro que o DOM não tem.

        ┌──────────────────────────┐
        │ [ícone]  Título          │   1ª linha: coluna auto + coluna 1fr
        │ texto do cartão, alinhado│   2ª linha: span 2, largura toda
        │ à esquerda, a correr até │
        │ à borda do cartão.       │
        └──────────────────────────┘

   O `.ms-card-text` dissolve-se em `display:contents` para que o título e o
   corpo sejam itens da MESMA grelha do ícone — sem tocar no DOM, que é o que a
   régua `validate_minhas_partituras` lê por descendência.
   ⚠️ `align-content:start` é o "não centralizado no card" do pedido: sem ele a
   grelha centra-se na altura e, num cartão esticado, o conteúdo boia no meio.

   Âmbito: `.sc-hub-page` — a aba Músicas, que é a tela que ele nomeou. O mesmo
   `.ms-like .ms-card` serve o ecrã de escolha de modo (index.html:19976), que
   ele não viu nem nomeou; alargar-lhe o desenho por minha conta seria o oposto
   do ponto 03. Em TODOS os aparelhos, isso sim: telemóvel, tablet e monitor. */
.sc-hub-page .ms-card{
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  grid-template-rows:auto auto;
  align-content:start;                       /* encosta em CIMA: "não centralizado no card" */
  justify-items:start;
  row-gap:clamp(10px, 1.6vh, 16px);
  column-gap:clamp(12px, 2.4vw, 20px);
  text-align:left;
}
.sc-hub-page .ms-card > .ms-ic{ grid-area:1 / 1 / 2 / 2; align-self:center; }
.sc-hub-page .ms-card > .ms-card-text{ display:contents; }
.sc-hub-page .ms-card-title{ grid-area:1 / 2 / 2 / 3; align-self:center; }
.sc-hub-page .ms-card-body { grid-area:2 / 1 / 3 / 3; width:100%; text-align:left; }

/* ── os cartões OCUPAM A TELA (ponto 04, 2.ª metade) ────────────────────────
   Medido no M1 antes: a fileira acabava aos 600 px numa janela de 900 — 300 px
   de vazio por baixo, que é o mesmo defeito que ele já tinha nomeado na capa da
   música ("espaços vazios não aproveitados"). A página passa a coluna flex e a
   fileira recebe o que sobra; o cartão estica com ela.
   🔑 `min-height:0` na fileira porque um item de flex não encolhe abaixo do
   conteúdo por omissão, e sem isso o cartão do vídeo (que tem o texto mais
   longo) impunha a altura dele aos outros dois em vez de a partilhar. */
@media (orientation:landscape){
  /* 🔴🔑 [16/09 · LOTE 4] `display` NUMA `.page` É A CHAVE DE VISIBILIDADE DA CASA,
     e eu escrevi-a como se fosse layout. A tela mostra-se e esconde-se por
       body.mode-interactive .page{display:none}          (0,2,1)
       body.mode-interactive .page.visible{display:block}
     e um `html.tb .games-page{display:flex}` vale (0,2,1) TAMBÉM — empata e ganha
     por vir depois. Medido: a tela dos JOGOS deixou de se esconder, a harmonia
     passou a nascer 810 px abaixo da janela (`top:810` numa janela de 810) e a
     foto da harmonia mostrava... a tela dos jogos, com o trilho a dizer HARMONIA.
     O sintoma não se parecia nada com a causa.
     A regra leva `.visible` e o `body.mode-interactive` do dono: assim só age na
     tela que está à frente, e nunca discute com quem esconde as outras. */
  html.tb body.mode-interactive .page.sc-hub-page.visible,
  html.dt:not(.dc) body.mode-interactive .page.sc-hub-page.visible{ display:flex; flex-direction:column; }
  /* 🔴 [16/09 · LOTE 4c] "cards exageradamente grandes. nao precisa aproveitar
     todo o espaco, pode adequar ao tamanho do texto de cada um".
     Eu tinha esticado a fileira para encher a página — foi o que ele pediu na
     1.ª volta ("os cards ainda nao ocupam a tela inteira") — e com o texto
     encostado em cima ficaram três colunas altas com um terço de conteúdo e dois
     terços de nada. O pedido de então era sobre a LARGURA e sobre os 262 px
     mortos à direita, não sobre esticar a altura até ao fundo.
     🔑 E o contrário do que ele pediu para os JOGOS, com razão: lá são seis
     fichas de um menu e o vazio era da TELA; aqui são três blocos de TEXTO, e
     cada um tem o tamanho que o seu texto tem. `flex-start` em vez de `stretch`
     é o que deixa o do vídeo (230 caracteres) ser mais alto que o do curso (31)
     sem obrigar os outros dois a acompanhá-lo. */
  /* 🔴 [16/09 · LOTE 4e] E VOLTA A SER COLUNA, por ordem dele: *"as telas deitadas
     da pagina musicas nao tem por que os 3 cards ficarem lado a lado. pode deixar
     tudo numa coluna vertical."*
     🔑 Isto fecha um círculo que vale a pena registar: o bloco **18g** já os tinha
     empilhado, com uma medida por trás (três colunas de 261 px davam 16 caracteres
     por linha). Ele reprovou ("quero algo como a m1 monitor") e eu pus em linha; a
     linha exigiu o cartão novo — ícone no canto, texto por baixo com a largura
     toda — e é o CARTÃO que ele queria, não a fileira. Com o cartão certo, a
     coluna volta e a medida do 18g volta a valer: agora o texto tem 700 px em vez
     de 261, e nem sequer precisa de encolher a letra para caber.
     O teto de 760 px não é estética: a 19 px, 75 caracteres por linha — o TECTO do
     T4b — pedem 712. Acima disso a linha cansa. */
  html.tb .sc-hub-page .ms-cards,
  html.dt:not(.dc) .sc-hub-page .ms-cards{
    flex-direction:column; align-items:stretch;
    /* 🔴 760 ERA O NÚMERO ERRADO, e falhou por dois lados ao mesmo tempo: o E3
       apanha botão com mais de 720 px (e o cartão É um `<button>`), e o T4b deu
       **77 caracteres por linha** contra um tecto de 75. Eu tinha feito a conta a
       19 px de letra; em C a letra é 17, e a 17 os mesmos 712 px de texto dão 77.
       700 passa nos dois — e quem garante a linha deixa de ser este número:
       passa a ser o `max-width:62ch` do corpo, que vale a qualquer tamanho de
       letra em vez de depender de eu acertar um px. */
    flex:0 0 auto; max-width:700px; margin-inline:auto;
    gap:clamp(12px, 1.6vh, 20px);
  }
  html.tb .sc-hub-page .ms-card,
  html.dt:not(.dc) .sc-hub-page .ms-card{ flex:0 0 auto; }
  /* o cartão do vídeo tinha `flex:1.35 1 0` (app.css:9561) por ser "a porta que
     ninguém procura sozinho". Com os três a encher a tela essa vantagem passa a
     ser altura partilhada e largura desigual sem razão — a ênfase dele já está
     na cor e na borda de 2 px. Os três repartem igual. */
  /* 🔴🔑 TERCEIRA VEZ QUE ESTA FAMÍLIA MORDE, e a bancada já tem a lição escrita:
     **`flex:1 1 0` muda de significado quando o contentor vira coluna.** O
     cartão do vídeo leva `flex:1.35 1 0` do `dt` (app.css:9561) porque em LINHA
     isso quer dizer "leva mais um terço da largura". Ao devolver a coluna por
     ordem dele, o eixo principal passou a ser a ALTURA e base 0 passou a querer
     dizer *não tenhas altura nenhuma*.
     Medido: **60 px de caixa para 232 de conteúdo** no M1, e o `.ms-card` tem
     `overflow:hidden` — o texto do cartão do vídeo ficava cortado a meio da
     frase. É o MESMO defeito que o bloco 18g documentou em 15/09 (title em
     `top:-76px`), a voltar porque eu virei o eixo e deixei o override para trás.
     Ao virar um contentor de eixo, reler TODOS os `flex` que vivem lá dentro. */
  html.dt:not(.dc) .sc-hub-page .ms-card--vid,
  html.tb .sc-hub-page .ms-card--vid{ flex:0 0 auto; }
}

/* ── 19b(2) · A ESCALA DO CARTÃO SAI DA COLUNA, NÃO DO APARELHO ─────────────
   🔴 MEDIDO na 1.ª volta, e é o mesmo defeito que o 18g tinha nomeado — só que
   agora com o desenho que o Luis escolheu: na caixa C os três cartões ficam com
   280 px, o ícone leva 60, o vão 20, e sobram **140 px** para um título de 32 px.
   "Enviar vídeo de Notas Caindo" quebrava em CINCO linhas, e o `T4b` da casa
   caiu com **20 caracteres por linha** no corpo (o teto de leitura é 30–75).

   🔑 E A CURA NÃO É O TOKEN DO APARELHO. O `--tt-fz-body` do tablet vale 19 px
   porque a tela é grande; aqui o que aperta não é a tela, é a COLUNA — três
   cartões repartem a mesma largura em qualquer aparelho. Então as três medidas
   deste cartão passam a sair de `vw`, que é a régua da coluna, com piso para o
   telemóvel e teto para o monitor. É a distinção que o lote 3 já tinha aprendido
   noutra tela: medida ESCRITA e medida que se AJUSTA não se medem com a mesma
   régua — e esta é escrita, logo é minha a responsabilidade do número.

   A conta do corpo, para o número não ser palpite: a coluna útil é a largura do
   cartão menos 2×30 de padding; 30 caracteres pedem `fz ≤ útil / 30 / 0.5`
   (0,5 em é a largura média de um glifo nesta fonte). C: 220/15 = 14,6 px.
   M1: 300/15 = 20 px. O `clamp(14px, 1.45vw, 17px)` cai dentro nos dois.

   ⚠️ Só em PAISAGEM. Em pé o cartão tem a largura toda e nada disto morde — e
   em pé vale a ordem contrária, a do ponto 01: a letra do tablet SOBE. */
@media (orientation:landscape){
  html.tb .sc-hub-page .ms-ic,
  html.dt:not(.dc) .sc-hub-page .ms-ic{
    width:clamp(34px, 3.4vw, 48px); height:clamp(34px, 3.4vw, 48px);
  }
  html.tb .sc-hub-page .ms-card-title,
  html.dt:not(.dc) .sc-hub-page .ms-card-title{
    font-size:clamp(18px, 2.05vw, 28px); letter-spacing:-.4px;
  }
  html.tb .sc-hub-page .ms-card-body,
  html.dt:not(.dc) .sc-hub-page .ms-card-body{
    font-size:clamp(14px, 1.45vw, 17px); line-height:1.45;
  }
  /* o cartão do vídeo tinha padding maior (app.css:9550) para se destacar; com
     os três da mesma altura isso só lhe rouba largura de texto. */
  html.tb .sc-hub-page .ms-card,
  html.dt:not(.dc) .sc-hub-page .ms-card,
  html.tb .sc-hub-page .ms-card--vid,
  html.dt:not(.dc) .sc-hub-page .ms-card--vid{
    padding:clamp(18px, 2vw, 28px) clamp(16px, 1.8vw, 26px);
  }
}

/* ── 19b(3) · O CORPO DO CARTÃO É PARÁGRAFO, LOGO VAI NA FONTE DE PARÁGRAFO ──
   🔴 A 2.ª volta devolveu duas réguas em conflito na caixa C (1080×810):
       E4  pede corpo ≥ 17 px no tablet     (o piso deste lote)
       T4b pede ≥ 30 caracteres por linha   (a régua de leitura da casa)
   e a 15,66 px o T4b dava **26**. Eu ia declarar uma isenção — e a isenção era
   a saída errada, porque as duas réguas estão certas: o que estava errado era
   uma terceira coisa que nenhuma das duas mede.

   🔑 `.ms-card-body` estava em `var(--font-h)` com `font-weight:700` — a fonte
   de TÍTULO. Ela gasta ~0,6 em por glifo contra ~0,5 da fonte de texto, ou seja
   **20 % menos texto na mesma linha**, e num parágrafo de sete linhas ainda pesa
   como um título. Um subtítulo de uma linha ("Aprenda a ler partitura do zero")
   aguenta isso; o cartão do vídeo, que tem 230 caracteres, não.
   Medido depois: coluna útil 257 px em C, corpo a 17,06 px → **30 cpl**. As duas
   réguas passam sem que nenhuma ceda, e o cartão fica mais leve de ler.
   Não é afinação de número: é a peça certa para o trabalho. */
.sc-hub-page .ms-card-body{
  font-family:var(--font-b); font-weight:500; letter-spacing:0;
}
@media (orientation:landscape){
  html.tb .sc-hub-page .ms-card-body,
  html.dt:not(.dc) .sc-hub-page .ms-card-body{
    font-size:var(--tt-fz-body); line-height:1.5; max-width:62ch;
  }
  /* o padding lateral desce para o que a conta dos 30 cpl exige em C
     (285 de cartão − 2×14 = 257) e cresce com a tela nas outras. */
  html.tb .sc-hub-page .ms-card,
  html.dt:not(.dc) .sc-hub-page .ms-card,
  html.tb .sc-hub-page .ms-card--vid,
  html.dt:not(.dc) .sc-hub-page .ms-card--vid{
    padding:clamp(18px, 2vw, 28px) clamp(13px, 1.3vw, 26px);
  }
}

/* ── 19b(4) · A CONTA FECHADA: OS 30 cpl E O PISO DE 17 px CABEM OS DOIS ────
   🔴 A 3.ª volta, e é aritmética de largura, não de gosto. Em C (1080×810) o
   orçamento horizontal é 1080 − 96 de trilho = **984 px**, e ele reparte-se em
   três cartões + dois vãos + o respiro da página. Com o respiro de MONITOR
   (`--dt-pad`, ~43 px de cada lado) e o vão de 21, cada cartão ficava com 285 e
   o texto com 257 — e 257 px não dão 30 caracteres a 17 px, faltam 29.

   🔑 E OS 29 px SAEM DE ONDE O PRÓPRIO PEDIDO MANDA TIRÁ-LOS: *"os cards ainda
   nao ocupam a tela inteira"*. O respiro de monitor e o vão largo são o que
   impede o cartão de chegar à borda. Cortados para 14 e 12, o cartão passa a
   **311 px** e a conta fecha com o piso INTACTO — nenhuma régua cede.
   Este é o oposto de declarar uma isenção: a isenção teria escondido que o que
   apertava o texto não era o piso, era a margem. */
@media (orientation:landscape){
  html.tb .sc-hub-page,
  html.dt:not(.dc) .sc-hub-page{
    padding-left:clamp(14px, 1.6vw, 34px); padding-right:clamp(14px, 1.6vw, 34px);
  }
  html.tb .sc-hub-page .ms-cards,
  html.dt:not(.dc) .sc-hub-page .ms-cards{ gap:clamp(12px, 1.2vw, 22px); }
  html.tb .sc-hub-page .ms-card,
  html.dt:not(.dc) .sc-hub-page .ms-card,
  html.tb .sc-hub-page .ms-card--vid,
  html.dt:not(.dc) .sc-hub-page .ms-card--vid{
    padding:clamp(18px, 2vw, 28px) clamp(12px, 1.15vw, 24px);
  }
  html.tb .sc-hub-page .ms-card-body,
  html.dt:not(.dc) .sc-hub-page .ms-card-body{ font-size:clamp(15px, 1.575vw, 19px); }
}

/* ── 19b(5) · E A REGRA DO `dt` TRAZ SEMPRE UMA CLASSE A MAIS ───────────────
   🔴 QUARTA VEZ NESTA FRENTE, e desta vez apanhei-a pelo número não se mexer:
   pedi 14 px de respiro lateral e a sonda continuou a devolver **40**. A regra
   do respiro de monitor é
     `html.dt body.mode-interactive .page.tabroot-page.visible:not(.toc-page):not(.mode-select-page)`
   — (0,7,2) por causa dos dois `:not()`. A minha, `html.tb .sc-hub-page`, é
   (0,2,0) e perde sem fazer barulho. O sintoma é sempre o mesmo: o valor não
   mexe, e quem lê um `clamp()` no ficheiro julga que ele está a valer.
   Aqui a minha leva `.sc-root.sc-hub-page` (as duas estão no `<section>`) e
   chega a (0,8,2) — ganha por especificidade, não por ordem, que é o que
   convém a uma regra que existe para vencer uma nomeada.

   🔑 E CAI TAMBÉM O TETO `--dt-col: 1180px`. Medido no M1 (1440): a página
   parava nos 1180 e sobravam **262 px** de margem morta à direita do trilho. Um
   teto de coluna serve TEXTO (é o que ele protege nas outras telas); numa parede
   de três cartões ele é exactamente o *"os cards ainda nao ocupam a tela
   inteira"* do pedido. Só nesta tela, e só deitado.

   A conta em C, agora fechada: 984 − 2×13 de respiro = 958; menos 2×10 de vão =
   938; ÷ 3 = **312 px** de cartão; menos 2×12 de padding = 288 de texto; a
   17 px, que é o piso, dá **30 caracteres por linha**. */
@media (orientation:landscape){
  html.tb body.mode-interactive .page.tabroot-page.sc-root.sc-hub-page.visible,
  html.dt:not(.dc) body.mode-interactive .page.tabroot-page.sc-root.sc-hub-page.visible{
    max-width:none;
    /* 🔴 [16/09] ERA `clamp(12px, 1.2vw, 30px)` E FALHAVA POR 0,6 px. O C1 do
       `validate_desktop` exige `L ≥ trilho + 16`, e a 1280 aquele clamp dava
       **15,4** — o conteúdo encostava ao trilho. O aperto tinha sido feito para
       os três cartões em LINHA caberem com 30 caracteres por linha; com a coluna
       que ele pediu depois, ele deixou de comprar nada e só pagava. Volta ao
       respiro normal, com piso de 18 para haver folga acima dos 16. */
    padding-left:clamp(18px, 1.8vw, 40px); padding-right:clamp(18px, 1.8vw, 40px);
  }
  html.tb .sc-hub-page .ms-cards,
  html.dt:not(.dc) .sc-hub-page .ms-cards{ gap:clamp(10px, 1vw, 20px); }
  html.tb .sc-hub-page .ms-card,
  html.dt:not(.dc) .sc-hub-page .ms-card,
  html.tb .sc-hub-page .ms-card--vid,
  html.dt:not(.dc) .sc-hub-page .ms-card--vid{
    padding:clamp(18px, 2vw, 28px) clamp(12px, 1.15vw, 24px);
  }
}

/* ── 19c · OS JOGOS APROVEITAM A TELA (ponto 07) ────────────────────────────
   *"jogos no ipad em pe esta com muito espaco vazio na tela. aproveitamento de
     espaco e importante aqui."*

   🔴 MEDIDO no D1 (1024×1366): seis cartões de 104 px de altura em duas colunas
   ocupam **~380 px** e deixam ~800 abaixo. E o mesmo defeito está no AR — a foto
   do `?tb=0` mostra a mesma faixa morta, só que com a barra de abas em baixo.
   Não é defeito do lote; é defeito que o lote destapou ao pôr a tela maior.

   🔑 E A CURA É A OUTRA METADE DO PONTO 01 DELE: *"o layout fica todo maior mas
   as fontes não acompanham"*. Aqui o mesmo, em ponto grande: o CARTÃO é que não
   acompanha a tela. Então ele cresce — `grid-auto-rows:1fr` reparte a altura
   pelas três fileiras — e o conteúdo cresce COM ele: ícone, título e descrição
   saem de `vmin`, que é a régua da caixa, e não de um número fixo de telemóvel.
   O arranjo NÃO muda (continuam duas colunas em pé e as mesmas seis fichas, na
   mesma ordem), que é o que o ponto 01 protege. O que muda é o tamanho — que é
   exactamente o que ele pediu que mudasse.

   🔴 A 1.ª volta pôs um TETO de 300 px na fileira, com medo de o cartão ficar
   mais alto que largo no iPad Pro. A foto desmentiu o medo e mostrou o custo: com
   o teto sobravam **250 px** no fundo — exactamente o vazio que ele reprovou, só
   que menor. Um teto que devolve o defeito em ponto pequeno não é prudência, é a
   mesma dívida com outro número. Sai o teto: em D1 as três fileiras ficam a
   ~353 px num cartão de 448 de largura, que continua mais largo que alto. */
html.tb body.mode-interactive .page.games-page.visible,
html.dt:not(.dc) body.mode-interactive .page.games-page.visible{ display:flex; flex-direction:column; }
html.tb .games-page .gm-hub,
html.dt:not(.dc) .games-page .gm-hub{
  flex:1 1 auto; min-height:0;
  grid-auto-rows:minmax(0, 1fr);
  align-content:center;
  gap:clamp(12px, 1.6vmin, 20px);
}
html.tb .games-page .gm-card,
html.dt:not(.dc) .games-page .gm-card{
  min-height:0;                       /* o `min-height:104px` do `dt` impedia o 1fr de repartir */
  padding:clamp(16px, 2.4vmin, 30px);
  gap:clamp(12px, 1.8vmin, 22px);
  border-left-width:4px;
}
html.tb .games-page .gm-ico,
html.dt:not(.dc) .games-page .gm-ico{
  width:clamp(42px, 6.4vmin, 74px); height:clamp(42px, 6.4vmin, 74px); border-radius:16px;
}
html.tb .games-page .gm-ico svg,
html.dt:not(.dc) .games-page .gm-ico svg{ width:52%; height:52%; }
/* 🔴 OS DOIS PISOS SAEM DA MEDIDA, e a 1.ª volta errou o de baixo: `1.75vmin`
   na caixa B (810×1080) dá 14,17 px e o piso de corpo do tablet é 17 — o E4 caiu
   com o nome da peça (`EM`) em vez de um número meu. `vmin` é a régua da CAIXA e
   está certa para a proporção; o que faltava era o PISO da família, e ele não é
   um número redondo: é 2,15vmin, que em B dá 17,4 e em D1 chega ao teto. */
html.tb .games-page .gm-card strong,
html.dt:not(.dc) .games-page .gm-card strong{ font-size:clamp(17px, 2.55vmin, 26px); }
html.tb .games-page .gm-card em,
html.dt:not(.dc) .games-page .gm-card em{ font-size:clamp(15px, 2.15vmin, 20px); }

/* ── 19f · A TELA DE ESCOLHA DOS GRAUS (pedido novo, 16/09) ─────────────────
   *"ao clicar no praticar unico do campo harmonico, abre nova janela similar ao
     unit intro. todos os graus do campo harmonnico aparecem numa checkbox e vc
     seleciona os que vai praticar. se selecionar apenas 1 pratica de acorde
     normal. selecionando mais de 1 vira pratica de progressao harmonica"*

   🔑 A MARCA NÃO É UM ✓, É UM NÚMERO — e essa é a única decisão de desenho que
   eu tomei sozinho nesta tela. Uma progressão é uma SEQUÊNCIA: I-V-vi-IV e
   I-IV-V-vi são coisas diferentes. Uma caixa de seleção comum diz "escolhido" e
   cala a ordem, e a tela ficaria a decidir por ele em silêncio — a mesma forma
   do "terceiro estado" que esta casa proíbe na fita. O número na marca mostra a
   ordem em que vai tocar, e desmarcar/marcar põe o grau no fim: reordenar sem
   inventar arrastar-e-largar.
   Com um só grau marcado o número é "1" e não atrapalha nada — e o caminho de
   um grau é o de 08/08, sem desvio. */
.hm-prat-page .hm-prat-lead{ margin:0 0 clamp(14px, 2vh, 22px); max-width:60ch; }
.hm-prat-lista{ display:grid; grid-template-columns:1fr; gap:clamp(8px, 1.2vh, 12px); }
@media (min-width:680px){ .hm-prat-lista{ grid-template-columns:1fr 1fr; } }
.hm-prat-item{
  display:flex; align-items:center; gap:clamp(10px, 1.6vw, 16px);
  width:100%; padding:clamp(10px, 1.4vh, 16px) clamp(12px, 1.6vw, 18px);
  border-radius:14px; cursor:pointer; text-align:left; color:var(--text);
  background:rgba(18,18,18,.34);
  border:1px solid var(--border2); border-left:3px solid transparent;
  -webkit-appearance:none; appearance:none; font:inherit;
  -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  transition:background .14s var(--e-out,ease), border-color .14s var(--e-out,ease);
}
.hm-prat-item.is-on{
  border-color:color-mix(in srgb, #22C55E 55%, transparent);
  border-left-color:#22C55E;
  background:color-mix(in srgb, #22C55E 10%, rgba(18,18,18,.34));
}
/* a marca: vazia = por escolher; com número = a posição na progressão */
.hm-prat-mark{
  flex:0 0 auto; width:var(--tt-tap); height:var(--tt-tap); border-radius:12px;
  display:flex; align-items:center; justify-content:center;
  border:1.5px solid var(--border2); background:rgba(0,0,0,.28);
  font-family:var(--font-h); font-weight:900; font-size:var(--tt-fz-body); line-height:1;
  color:#04130A;
}
.hm-prat-item.is-on .hm-prat-mark{ background:#22C55E; border-color:#22C55E; }
.hm-prat-grau{
  flex:0 0 auto; min-width:3.2em;
  font-family:var(--font-h); font-weight:800; font-size:var(--tt-fz-body);
  letter-spacing:.06em; color:var(--muted);
}
.hm-prat-item.is-on .hm-prat-grau{ color:#22C55E; }
.hm-prat-txt{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px; }
.hm-prat-txt strong{ font-family:var(--font-h); font-weight:900; font-size:var(--tt-fz-lead); line-height:1.1; }
.hm-prat-txt em{ font-family:var(--font-b); font-style:normal; font-size:var(--tt-fz-micro); color:var(--muted); line-height:1.3; }
.hm-prat-kbd{ flex:0 0 auto; display:flex; align-items:center; }
.hm-prat-kbd .mk-svg{ height:clamp(30px, 4.4vh, 44px); width:auto; }
/* as teclas acesas, todas iguais — a mesma regra do bloco, pela mesma razão */
.hm-prat-kbd .mk-k.is-on, .hm-prat-kbd .mk-k.is-on.is-root{ fill:var(--accent, #22C55E); }

/* ── 19e · A HARMONIA: QUATRO FILEIRAS IGUAIS (ponto 02, 2.ª volta) ──────────
   *"a tela em pe do campo harmonico deve ser igual a do ar, mas os resultados de
     graus tambem devem ser por slots giraveis para os lados. entao assim, a
     pagina nova tera 4 slots giraveis do mesmo tamanho e identicos. os 3 antigos
     e o novo slot resultante. (…) agora e so imitar mais ou menos a mesma logica
     para as telas deitadas, so que ai os slots seriam de giro vertical."*

   🔴 A 1.ª VOLTA DESTE LOTE ERRA A LEITURA e ele corrigiu-a: eu tinha feito os
   graus como blocos MENORES, num carrossel com desenho próprio. Ele quer as
   QUATRO da mesma peça — mesmo tamanho, mesma moldura, mesmo neon, mesmo zoom —
   com a lei que o ar já tem. E o desenho do ar é o que este bloco restaura, não
   reinventa: foi copiado de `TABLET-BANCADA/.antes-harmonia/app.css`.

   AS QUATRO LEIS DO SLOT, com as palavras dele:
     · "todos os slots do mesmo tamanho"     → um `--cw` só, herdado por todos.
     · "efeito degrade perfeito ao redor"    → o neon do ar, ao byte: gradiente
       radial + anel interno + halo externo, com o ALCANCE a seguir o `--near`.
     · "o slot do meio sempre fica maior"    → o zoom de 1,07 do `hmDockUpdate`.
     · "a rolagem e suave e da o efeito de zoom in no slot do meio".

   🔑 O EIXO É A ÚNICA COISA QUE MUDA COM A ORIENTAÇÃO, e nem o JS sabe disso: o
   `window.__hmEixo` pergunta o `flex-direction` ao dock e os seis consumidores
   seguem-no. Em pé as quatro rolam para os lados e empilham-se; deitadas rolam
   para cima e para baixo e ficam lado a lado.

   🔑 E O TAMANHO SAI DO CONTENTOR, NÃO DO ECRÃ — `cqw`/`cqh` sobre o
   `.hm-dock-wrap`. É o que faz "do mesmo tamanho" valer em dez caixas sem eu
   escrever dez números, e evita a armadilha que já me apanhou duas vezes nesta
   frente: **percentagem de padding resolve-se sempre contra a LARGURA**, nos
   quatro lados. Num dock vertical `calc(50% - cw/2)` daria um número que não tem
   nada a ver com a altura; `50cqh` dá exactamente a metade da faixa.
   ═════════════════════════════════════════════════════════════════════════ */

/* 🔴🔑 A REGRA LEVA `.visible` E O `body.mode-interactive` DO DONO, e é a segunda
   vez que esta lição me apanha no mesmo lote. A casa mostra e esconde tela por
   `display`: `body.mode-interactive .page{display:none}` vale (0,2,1), e um
   `html .harmony-page{display:flex}` vale (0,1,1) — PERDE, em silêncio. Medido:
   a página ficava `block`, o `margin-top:auto` do CTA não empurrava nada, a mesa
   não esticava e — pior — o contentor ficava sem altura definida, o que faz um
   `container-type:size` colapsar para ZERO. Na foto do C as quatro fileiras
   sumiram e o botão laranja apareceu colado aos rótulos.
   Com `.visible` a regra chega a (0,3,2), ganha, e só age na tela da frente. */
/* 🔴 E NEM (0,3,2) CHEGOU. Em pé passou; DEITADO não, e a foto do C mostrou as
   quatro fileiras colapsadas com o botão laranja colado aos rótulos. A culpada é
   do lote 1: o bloco 9 crava
     `html.tb body.mode-interactive .page.tabroot-page.visible[data-page="harmony"]{display:block}`
   que vale (0,6,2) — e no telemóvel em pé não há `tb`, por isso a minha ganhava
   lá e perdia aqui. Uma regra que só funciona em metade das caixas é pior que
   uma que não funciona: parece certa na foto que se olhou primeiro.
   Esta nomeia `.special` além das outras e chega a (0,7,2), que ganha em qualquer
   das caixas sem depender da ordem. */
html body.mode-interactive .page.special.tabroot-page.harmony-page.visible[data-page="harmony"]{
  display:flex; flex-direction:column;
}
html .harmony-page .hm-mesa{
  display:flex; flex-direction:column;
  /* 🔑 [16/09 · LOTE 4g] O VÃO DA MESA PASSA A TER NOME, porque deixou de ser
     só um vão. É dele que sai a metade da folga do brilho que NÃO se paga em
     tamanho de card (o `--fora` do dock), e é nele que o rótulo se centra. */
  --hm-banda:clamp(6px, 1.2vh, 14px);
  gap:var(--hm-banda); flex:1 1 auto; min-height:0; min-width:0;
}
/* 🔑 OS QUATRO GRUPOS REPARTEM A ALTURA EM PÉ, E A LARGURA DEITADOS — e é isso
   que dá ao WRAP uma altura DEFINIDA nas duas orientações. Sem ela o
   `container-type:size` não tem contra o que medir e o `100cqh` vale zero.
   É também o que faz as quatro fileiras CABEREM numa tela sem sobra: o slot sai
   da fatia, em vez de a fatia sair do slot. */
html .harmony-page .hm-mesa > .hm-group{ flex:1 1 0; min-height:0; min-width:0; }

/* ── 19f · O RÓTULO SAI DO FLUXO E CENTRA-SE ENTRE AS FILEIRAS (só EM PÉ) ────
   *"ha espaco mal aproveitado, espaco vazio entre os cards de tonalidade e a
     palavra escala (…) podemos aumentar o tamanho dos cards giraveis sem
     sobrepor conteudo, vamos deixar um pequeno espaco vazio para caber as
     palavras (…) simetricamente entre as linhas de cards"*

   🔑 O DESPERDÍCIO E O CORTE ERAM A MESMA CONTA. Medido no iPhone em pé: entre
   duas fileiras havia **49,4 px**, dos quais só 16,3 eram a palavra — 33 px de
   nada, enquanto ao brilho faltavam 12,6. O rótulo estava EM FLUXO, por isso
   comia 21,3 px da faixa (16,3 de altura + 5 de margem) antes de o `100cqh`
   sequer existir.
   Fora do fluxo, a faixa herda-os e o slot cresce; e o rótulo, em vez de ficar
   colado por cima de um card, fica com o CENTRO no meio do vão da mesa — que é
   exactamente o ponto médio entre o bordo de baixo de um card e o de cima do
   seguinte, porque as duas folgas (`--dentro`) são iguais. É a simetria que ele
   pediu, e sai de graça da geometria em vez de um número escolhido a olho.

   🔴 SÓ EM PÉ. Deitado os quatro grupos são COLUNAS lado a lado e o rótulo fica
   por cima de cada uma: tirá-lo do fluxo punha-o em cima do primeiro slot da
   coluna, que é o defeito e não a cura. O eixo transversal lá é o X, e lá o
   rótulo não custa faixa nenhuma.
   🔴 [16/09 · LOTE 4h] A EXCEPÇÃO DA PRIMEIRA FILEIRA SAIU — ver a lápide logo
   abaixo da regra. As quatro seguem agora a mesma lei.
   `pointer-events:none`: a palavra passou a ficar POR CIMA da caixa que rola, e
   um rótulo que engole o arrasto do dedo seria um defeito novo em troca de um
   velho. */
@media (orientation:portrait){
  /* ── 19g · A FILEIRA CHEGA À BORDA DO ECRÃ ────────────────────────────────
     🔴 [16/09 · Luis] *"os cards das pontas laterais nao estao sendo cortados
       pelo real fim da tela, existe uma barra preta nas 2 pontas da tela (…) o
       fundo estrelado chega ate a extremidade lateral, mas os cards sao cortados
       antes."*

     E é literal: o céu estrelado é da PÁGINA (pinta a caixa inteira, incluindo o
     padding) e a fileira é filha da caixa de CONTEÚDO, que pára no gutter do
     `.tabroot-page`. Medido no iPhone 13/14: **16 px mortos de cada lado** —
     8,2 % da tela, num carrossel que é exactamente aquilo que a tela tem de dar.

     🔑 A SANGRIA VEM MEDIDA, NÃO COPIADA. O gutter tem DOIS donos em CSS (o
     `clamp(16px,4vw,40px)` do `.tabroot-page` e o `var(--dt-pad)` da coluna de
     leitura do computador), e escrever aqui um terceiro seria pôr um número a
     envelhecer longe dos dois que mandam — a armadilha que esta frente já pagou
     no `se-label` do lote 4. Quem lê o padding da página é o `hmSangra()`
     (index.html), na mesma volta em que o `hmFitChips` mede a letra, e escreve-o
     aqui. Sem ele o `0px` do fallback devolve exactamente o layout de hoje.
     ⚠️ SÓ EM PÉ: deitado os docks rolam na VERTICAL e as pontas laterais são
     colunas, não pontas de faixa — não há o que cortar. */
  html .harmony-page .hm-mesa{
    width:calc(100% + var(--hm-sangra-e, 0px) + var(--hm-sangra-d, 0px));
    margin-left:calc(-1 * var(--hm-sangra-e, 0px));
    margin-right:calc(-1 * var(--hm-sangra-d, 0px));
  }
  html .harmony-page .hm-mesa > .hm-group > .se-label{
    position:absolute; left:0; right:0; z-index:2; margin:0;
    top:calc(-1 * var(--hm-banda) / 2); transform:translateY(-50%);
    pointer-events:none;
  }
  /* 🔴🔑 [16/09 · LOTE 4h · Luis] *"o nome tonalidade pode ficar um pouco mais
     pra cima, ele esta sendo cortado pelo card do meio"* — e aqui estava a
     excepção que o causava. A 1.ª fileira ancorava-se PELO BAIXO (`top:--dentro`
     + `translateY(-100%)`), isto é, o pé da palavra encostava ao topo do slot
     NÃO ESCALADO. Só que o slot do meio é pintado a `--zoom-max`, e o topo dele
     sobe `cw·(z−1)/2` — que é exactamente `--dentro`. Medido com o zoom LIGADO:
     a folga entre a palavra e o card é **0,0 px** no iPhone 13/14 e no SE, 5,6
     no iPad Pro — e o halo do card (`--reach`, 21,6 px) passa POR CIMA da
     palavra inteira. As outras três estavam a 13,5 px, simétricas ao décimo.
     🔑 A CURA É APAGAR A EXCEPÇÃO, não afiná-la. A regra geral põe o CENTRO do
     rótulo em `group top − --fora`, que é o bordo exterior do brilho do card —
     o mesmo sítio, ao píxel, onde as outras três ficam em relação às suas
     fileiras. As quatro passam a ter a MESMA lei e a 1.ª sobe 13,5 px (P1),
     saindo para a folga que o herói já deixava por cima da mesa.
     ⚠️ O medo antigo ("a palavra subia para fora da mesa") continua verdadeiro e
     passou a ser o objectivo: ela sai da mesa e nada se mexe, porque está FORA
     DO FLUXO e por cima de espaço vazio. */
}

/* o WRAP é o contentor que dá a medida — e é a única peça com `container-type` */
html .harmony-page .hm-dock-wrap{
  /* 🔴🔑 [16/09 · LOTE 4g] `--halo:inherit` E NÃO UM NÚMERO. O dono do `--halo`
     desta tela passou a ser o `.hm-group` (o rótulo também precisa dele), mas o
     `.hm-dock-wrap` BASE — o do dock genérico, app.css:5987 — declara
     `--halo:clamp(10px, 2.4vw, 14px)` na PRÓPRIA caixa, e uma declaração local
     vence sempre o valor herdado, por mais específico que seja quem o herdou.
     Medido: o `html.dc …{--halo:10px}` do telemóvel deitado ficou inerte e a
     faixa voltou a ler 14 — quatro fileiras a reprovarem o Z2 com a cura no
     sítio. `inherit` desliga a declaração local sem lhe tocar, e o número volta
     a ter um dono só. */
  --halo:inherit;
  container-type:size;
  flex:1 1 auto; width:100%; height:auto; min-height:0; max-width:none; margin:0;
  /* 🔴🔑 [16/09 · LOTE 4f] O ZOOM PASSA A TER UM DONO SÓ, E É ESTE.
     Até agora o número vivia dentro do `hmDockUpdate` (index.html) e a geometria
     daqui reservava espaço para OUTRO número — o de 22/07, 7 %. Quando o Luis
     disse "não estou vendo zoom in nas imagens" eu subi o do JS para 1,16 e não
     havia como o CSS saber. As duas consequências chegaram juntas, e ele apanhou
     as duas: slots sobrepostos e o neon cortado a régua.
     Agora o JS LÊ estes dois do CSS. Um número, um dono. */
  --zoom-max:1.16;
  --zoom-min:.92;
  /* 🔴 [16/09 · LOTE 4b] O TETO ESTAVA A SEGURAR O SLOT ABAIXO DO QUE A FAIXA
     DÁ. Medido em C: a faixa comportava 145 px e o teto cortava em 137,7 (17vmin
     de 810) — o slot ficava mais pequeno por causa de um limite meu, não do
     espaço. Um teto existe para o slot não virar meia tela num monitor, não para
     mandar onde já há uma conta melhor. Sobe para 20vmin e passa a morder só
     onde deve: nas telas onde a largura e a altura sobram as duas. */
  --teto:clamp(96px, 20vmin, 190px);
}

/* ── EM PÉ · as quatro rolam para os LADOS e empilham-se ────────────────────
   A conta é a do ar, letra por letra: a largura dá três slots à vista, e três é
   ÍMPAR porque o encaixe é no CENTRO — num número par não há como centrar um e
   caber o outro inteiro. O teto existe para o tablet não ficar com slots de
   meia tela, e é `vmin` para crescer com o aparelho (é a outra metade do ponto
   01 dele: "o layout fica todo maior mas as fontes não acompanham"). */
html .harmony-page .hm-dock{
  /* 🔴 [16/09 · LOTE 4f] A CAIXA DEIXA DE SER "O QUE CABE" E PASSA A SER
     "O QUE CABE DEPOIS DE CRESCER". Com `--zoom-max:1.16` o slot do meio fica
     8 % maior DE CADA LADO do que a caixa que o `--cw` dá, e esses 8 % têm de sair
     de algum sítio. Saíam dos dois sítios errados, e o Luis viu os dois:
       · no eixo TRANSVERSAL comiam a folga do neon — medido no iPad Pro em pé
         sobrava **1,5 px** para um brilho que pede ≈9, e o que se vê é o halo
         cortado por uma recta: *"surgiu uma linha imaginária que corta o efeito
         ao redor dos slots"*;
       · no eixo do CARROSSEL comiam o vão — medido **−12,4 px** no iPad Pro
         deitado, **−10,8** no monitor, **−5,8** no iPhone deitado:
         *"os retangulos ficam sobrepondo uns aos outros"*.
     A conta nova, com T = o lado transversal e L = o do carrossel:
       transversal:  cw · 1,16 + 2 · halo ≤ T   →   cw = (T − 2·halo) / 1,16
       carrossel:    3 · cw + 2 · vão ≤ L, vão = 0,16 · cw   →   cw = L / 3,32
     O 1,16 é o `--zoom-max` e o 3,32 é 3 + 2·0,16.
     🔴 E O VÃO NÃO É O CRESCIMENTO DE UM SLOT, que foi o meu primeiro número
     (0,10) e ainda deixava **−1,4 px em B** e −0,1 em P1. Entre dois vizinhos
     crescem OS DOIS: o pior caso não é um slot no centro, é a rolagem a meio,
     com os dois a meio caminho do centro. Na curva do `hmDockUpdate` isso dá
     1,13 em cada um — 0,13·cw de avanço somado, não 0,08. Daí 0,16, que é esse
     pior caso com margem em todas as dez caixas.
     🔑 E a razão de o defeito aparecer SÓ EM PÉ: o `raio` da curva é um terço
     da faixa, e em pé a faixa é larga (5,5 slots em B) — o vizinho fica a 0,44
     de raio do centro e ainda cresce 6 %. Deitada a faixa dá 3 slots, o vizinho
     fica a 0,77 de raio e ENCOLHE. A mesma fórmula, dois regimes.
     🔑 Medido depois: o slot não encolheu em caixa nenhuma — CRESCEU em seis
     (C 142→161, B 105→114, D1 178→181). A folga estava a ser paga duas vezes. */
  /* 🔴🔑 [16/09 · LOTE 4g] A RESERVA ERA O ALCANCE DA PONTA, E O QUE SE VÊ É O
     DO MEIO. O brilho é `--reach: calc(var(--halo) * .4 + 18px * var(--near))`
     e `--near` vale **1 exactamente no slot do centro** — o único que está
     sempre à vista. A reserva transversal era `--halo`, que é o alcance com
     `--near:0`, ou seja o da PONTA da faixa. Medido contra o ar: faltavam
     **12,6 px** no iPhone em pé, **9,6** no tablet em pé, **12** no iPhone
     deitado e **6,9** no tablet deitado — e o que se vê a essa distância do
     bordo já não é o degradê a acabar, é uma RECTA. As palavras dele, duas
     voltas seguidas: *"uma linha imaginária que corta o efeito"* e, depois de
     eu ter "consertado", *"corta também os próprios cards"*.
     🔴 E A RÉGUA DE ONTEM ASSINOU VERDE. O `zoom.cjs · Z2` cobrava `--halo`
     porque herdou a premissa do comentário que dizia "`--reach` ≤ `--halo` na
     borda". Na borda, sim. No meio, não — e é o meio que se olha.
     A folga passa a ser o alcance A SÉRIO, e paga-se em dois sítios:
       `--fora`   metade do vão da mesa, que já estava VAZIO: a caixa de recorte
                  cresce para lá (altura maior + margem negativa igual), sem
                  mexer no layout e sem custar um pixel de card;
       `--dentro` o resto, dentro do recorte — este sim sai da conta do `--cw`.
     E como o rótulo saiu do fluxo (bloco 19f, adiante), o `100cqh` herdou a
     altura dele e o card ainda CRESCE: 84,3→90,4 no iPhone em pé,
     132,4→152,9 no tablet em pé, 210,1→231,6 no iPad Pro. */
  --cw:min( calc(100cqw / 3.32),
            calc((100cqh - 2 * var(--dentro)) / 1.16),
            var(--teto) );
  display:flex; flex-direction:row; flex-wrap:nowrap; align-items:center;
  gap:calc(var(--cw) * .16);
  overflow-x:auto; overflow-y:hidden;
  scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch; scrollbar-width:none;
  /* 🔑 A CAIXA DE RECORTE É A CAIXA DE PADDING, e é ela que cresce `--fora`
     para cada lado; a margem negativa igual devolve o espaço ao layout, para
     que nada à volta se mexa. O `box-sizing:border-box` da casa é o que torna
     isto exacto — o `height` já conta o padding, logo a caixa de conteúdo fica
     em `100% − 2·--dentro`, que é o que o `--cw` acima reserva. */
  height:calc(100% + 2 * var(--fora)); width:100%; min-width:0;
  margin-block:calc(-1 * var(--fora));
  /* 🔑 A FOLGA TRANSVERSAL DEIXA DE SER PADDING. Com `align-items:center` o que
     sobra do lado curto reparte-se sozinho pelos dois lados, e a conta do `--cw`
     acima garante que o que sobra é ≥ halo de cada lado. O padding era uma
     SEGUNDA reserva do mesmo espaço — e era ela que apertava o slot. */
  padding:calc(var(--fora) + var(--dentro)) calc(50cqw - var(--cw) / 2);
  margin-inline:0;
}

/* ── DEITADO · as quatro ficam LADO A LADO e rolam para cima e para baixo ───
   Aqui o escasso troca de eixo: a largura sobra (quatro colunas numa tela larga)
   e a altura é que manda. A conta espelha-se — e é por isso que ela é a MESMA
   fórmula com `cqw` e `cqh` trocados, em vez de dois desenhos diferentes. */
@media (orientation:landscape){
  html .harmony-page .hm-mesa{
    flex-direction:row; align-items:stretch;
    /* mesmo nome, outro número: deitada a mesa reparte LARGURA */
    --hm-banda:clamp(8px, 1.4vw, 22px);
    flex:1 1 auto; gap:var(--hm-banda);
  }
  html .harmony-page .hm-mesa > .hm-group{ flex:1 1 0; min-width:0; min-height:0; }
  html .harmony-page .hm-dock{
    /* a mesma cura do bloco em pé, com os eixos trocados — e é de propósito
       que é a MESMA fórmula e não um segundo desenho. */
    --cw:min( calc(100cqh / 3.32),
              calc((100cqw - 2 * var(--dentro)) / 1.16),
              var(--teto) );
    flex-direction:column;
    overflow-y:auto; overflow-x:hidden;
    scroll-snap-type:y mandatory;
    align-items:center;
    height:100%; width:calc(100% + 2 * var(--fora));
    margin-block:0; margin-inline:calc(-1 * var(--fora));
    padding:calc(50cqh - var(--cw) / 2) calc(var(--fora) + var(--dentro));
  }
}

/* ── O SLOT · o desenho do ar, para as quatro fileiras ──────────────────────
   Copiado de `.antes-harmonia/app.css`, não reescrito de memória. O que mudou
   foi só de onde vem o `--cw`. */
html .hm-dock .hm-chip{
  flex:0 0 auto; scroll-snap-align:center;
  width:var(--cw); height:var(--cw); min-height:0; padding:10px 8px;
  border-radius:22px;                       /* quadrada de cantos suaves */
  /* 🔴 [16/09 · LOTE 4f] A ORIGEM DO ZOOM VOLTA AO CENTRO, e é metade da cura
     da sobreposição. O desenho do ar tinha `transform-origin:center bottom`
     (app.css:6112) porque a fileira era UMA e HORIZONTAL: crescer para cima,
     com a base fixa, lê-se como uma doca. Com quatro fileiras e duas
     orientações esse mesmo valor passou a querer dizer *todo o crescimento de um
     só lado* — num dock vertical o slot do meio sobe 16 % da própria altura para
     dentro do vizinho de cima, em vez de 8 % para cada lado.
     Ao centro, o que cresce é 8 % por lado, que é exactamente o número que a
     conta do `--cw` reserva. Uma peça herdada continua certa até o contexto
     dela mudar; esta mudou de eixo e de número ao mesmo tempo. */
  transform-origin:center;
}
/* 🔴 O PADDING LATERAL DESCE A 5 px NO TABLET, e é uma conta e não um gosto: o
   `hmFitChips` procura o maior corpo de letra que cabe na caixa de texto, e em B
   o slot de 105 px com 8 px de cada lado deixava 89 px — "MENOR HARMÔNICA" só
   coube a **13,38 px**, abaixo do piso de 14 do tablet (E4). Com 5 px sobram 95,
   que é o que falta para o piso. O halo do neon não depende disto — quem o
   reserva é a conta do `--cw` (o `/1.16` do lado transversal). É o mesmo lever
   que o lote 3 já tinha medido para o telemóvel deitado, aplicado onde ele agora
   morde — e com o slot de B a passar de 105 para 114 px sobra ainda mais. */
html.tb .hm-dock .hm-chip{ padding:10px 5px; }
/* a 4.ª cor. As três do ar são azul → verde → laranja, uma por eixo (ordem do
   Luis de 22/07: "assim o aluno distingue os 3 eixos de relance"). A dos graus
   é CREME e não uma quarta cor de eixo, porque ela não é um eixo: é o
   RESULTADO dos outros três. A cor diz isso sem uma palavra. */
.hm-dock.dock-grau{ --dock:#F6F1E7; }
/* 🔴🔑 [16/09 · LOTE 4h] E A COR CREME PAGA A FOLGA MAIS CARA QUE AS OUTRAS TRÊS.
   Depois de o `--esc` arrumar as quatro fileiras, SOBRAVA uma — esta, e só esta,
   em 6 das 8 caixas. A causa não é geometria nenhuma: é LUZ. Uma sombra com a
   mesma extensão em píxeis deixa, ao fim da reserva, tanto mais brilho quanto
   mais clara for a cor, e o creme é **1,46×** o mais claro dos outros três.
   Medido numa réplica isolada, exactamente ao fim da reserva (21,6 px do bordo):

       azul  L 165,0 → −0,35/255      laranja L 139,0 → −0,65/255
       verde L 154,9 → +0,15/255      CREME   L 241,3 → **+1,00/255**

   Um ponto em 255 é pouco e é exactamente o que se vê: uma recta muito ténue, que
   é o "a linha já está quase no final do efeito" da ordem dele.
   🔑 ENCURTA-SE O ALCANCE, NÃO SE BAIXA O ALFA. Baixar o alfa apagava o brilho
   desta fileira em toda a extensão — e ela é o RESULTADO das outras três, a que
   mais tem de brilhar. Encurtar deixa a luz igual junto ao card e fá-la acabar
   dentro da reserva. Varrido na réplica: `.90` já dá 0,00 e `.85` dá −0,79 — e é
   `.85` que fica, porque é o valor provado nas OITO caixas (mediana do resíduo
   0,00 nas dez linhas desta fileira), não o que mal toca o zero numa réplica. */
.hm-dock.dock-grau .hm-chip{ --reach:calc((var(--halo) * .4 + 18px * var(--near)) / var(--esc) * .85); }

/* o que vai DENTRO do slot do grau: o número em cima, o tecladinho em baixo */
html .hm-chip.hm-bloco{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:calc(var(--cw) * .05); overflow:hidden; text-align:center;
}
html .hm-chip.hm-bloco .hm-roman{
  flex:0 0 auto; font-size:max(var(--tt-fz-micro), calc(var(--cw) * .17));
  line-height:1; letter-spacing:.06em; margin:0; color:inherit;
}
html .hm-chip.hm-bloco .hm-card-kbd{
  flex:0 0 auto; width:100%; display:flex; align-items:center; justify-content:center;
}
html .hm-chip.hm-bloco .hm-card-kbd .mk-svg{
  height:calc(var(--cw) * .40); width:auto; max-width:100%;
}
/* 🔑 "as teclas verdes acesas todas iguais" — o defeito que ele nomeou, e tinha
   nome no ficheiro: `.mk-k.is-on` pintava a 62 % e `.is-on.is-root` a 100 %, para
   a fundamental se destacar. Num tecladinho deste tamanho isso não lê como "a
   fundamental é esta"; lê como uma tecla mais acesa sem razão. O `.is-root`
   continua no DOM (o `validate_f4` mede-o) — deixa só de ser pintado diferente. */
html .hm-chip.hm-bloco .mk-k.is-on,
html .hm-chip.hm-bloco .mk-k.is-on.is-root{ fill:#22C55E; }

/* a cifra e a qualidade: fora da vista, dentro da árvore de acessibilidade */
.hm-so-leitor{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* ── MONTAR HARMONIA · laranja, colado ao fundo ─────────────────────────────
   `sticky` e não `fixed`: colado ao fundo da JANELA enquanto a página rola, mas
   ainda dentro do fluxo — um `fixed` tapava a última fileira em vez de a deixar
   passar por baixo, que é o defeito que ele já apanhou na capa da música
   ("a barra passa a ser um tapa-olhos"). O gradiente do fundo devolve a leitura
   ao que passa por trás. */
html .harmony-page .practice-cta.hm-cta{
  position:sticky; bottom:0; z-index:5;
  /* 🔴 `flex:0 0 auto` E NÃO O `flex-shrink:1` DE OMISSÃO. Medido: o V2 apanhou o
     próprio botão com `clientHeight 76 · scrollHeight 90` em cinco caixas — ele é
     item de uma coluna flex, e quando o conteúdo da mesa aperta, um item
     encolhível cede altura. O `.practice-cta` tem `overflow:hidden`, logo o que
     cede não é a caixa: é o TEXTO, cortado por dentro. Um CTA que encolhe é a
     última peça que pode ceder — ela é a saída da tela. */
  flex:0 0 auto;
  /* 🔴 [16/09 · LOTE 4b] "botao montar harmonia com barras pretas ridiculas do
     lado". Era o `max-width:620px` do bloco 4, que existe para um CTA não virar
     tarja de ponta a ponta numa tela de 1080 — lei boa, contexto errado: aqui
     por baixo dele há QUATRO colunas que ocupam a largura toda, e um botão mais
     estreito que elas lê-se como uma faixa recortada, não como um botão contido.
     Ele acompanha as colunas. O E3 (tarja > 720 px) passa a acusá-lo nas caixas
     largas: é dívida DECLARADA por ordem dele, não descuido. */
  margin-top:auto; margin-bottom:0; max-width:none; width:100%; align-self:stretch;
}
/* 🔴 [16/09] AQUI ESTAVA UM `::after` MEU, e ele partia a peça que devia ajudar.
   Era uma faixinha opaca 14 px abaixo do botão, para o que rola por trás não
   encostar nele. Duas coisas erradas ao mesmo tempo: `bottom:-14px` ESTENDE a
   área rolável do próprio botão (medido: `clientHeight 76 · scrollHeight 90`,
   e o V2 acusou "texto cortado" em cinco caixas — o texto do CTA, cortado por
   mim), e o `.practice-cta` tem `overflow:hidden`, logo a faixa nem chegava a
   desenhar-se. Um enfeite invisível a partir o critério de texto cortado.
   Não há substituto: a página já reserva respiro em baixo. */

/* ── 19e(2) · O TELEMÓVEL DEITADO: TRÊS À VISTA, COMO EM TODA A PARTE ───────
   🔴 A MINHA 1.ª RESPOSTA FOI UM SLOT POR COLUNA, E ELE REPROVOU-A:
   *"no iphone deitado so da pra ver um slot em cada coluna. nao pode. deve
     sempre haver mais de um slot visivel, mas o slot selecionado e sempre o slot
     do meio."*
   Ele tem razão e o argumento é o dele próprio: com UM slot à vista não há
   "slot do meio" — há o único. A lei que ele acabou de pedir perde o sentido
   exactamente na caixa onde eu a tinha declarado mais explícita. E sem vizinhos
   à vista não há como saber que a fileira roda.

   🔑 ENTÃO O ORÇAMENTO SAI DE OUTRO SÍTIO, e a conta fecha: em A1 (844×390) o
   quadro útil é 390 − 30 de respiro = 360, e a pilha pede herói 34 + rótulo 16 +
   PRATICAR 52. Sobram **258 px** para a faixa; três slots + dois vãos de 8 + duas
   folgas de 10 dão **74 px de slot**. No slot de 74 a caixa de texto tem 64×54, e
   "HARMÔNICA" a 11 px (o piso desta família) mede 59 — cabe, com as duas linhas
   a ocuparem 25 dos 54. Não é apertado: é medido.
   O que eu tinha feito era pagar o aperto com o NÚMERO de slots, que é a única
   coisa da lei do ar que não se pode negociar. */
@media (orientation:landscape){
  /* 🔑 A CONTA DE A1 (844×390), refeita com a faixa MEDIDA e não estimada.
     O que aperta é o rótulo mais longo: "HARMÔNICA" são 9 letras em caixa alta,
     e ao piso desta família (11 px) medem ~61 px. Para elas caberem, a caixa de
     texto do slot tem de ter ≥61 — ou seja `cw ≥ 61 + 2×padding`.
     Na 1.ª medição a faixa era 226 px e o slot saía a 63,5: caixa de 55, e o
     `hmFitChips` encolhia até **8,5 px**. Cada px devolvido à faixa vale 1/3 de px
     no slot, por isso a cura é somar pequenas devoluções em vez de procurar uma
     grande: título 20→18, folga 10→8, vão 8→6, padding do chip 4→3 e o
     espaçamento entre letras a zero (é o que mais engorda caixa alta). */
  html.dc .harmony-page .toc-mega-title{ font-size:18px; letter-spacing:-.3px; }
  html.dc .harmony-page .lead{ display:none; }        /* o título já diz o que é */
  /* 🔴 QUINTA VEZ QUE A REGRA DO `dt` GANHA POR TRAZER UMA CLASSE A MAIS, e o
     sintoma foi o de sempre: o número não se mexeu. Eu pedi 4 px de margem ao
     herói e a sonda continuou a devolver **34** — é o
     `html.dt .tabroot-page .toc-hero:not(.sc-sub-hero){margin:0 0 34px}` (0,4,1)
     a vencer a minha (0,3,1). E aqui custava caro: 30 px de faixa, que em três
     slots são **10 px de slot**, que era exactamente o que faltava ao rótulo.
     Esta nomeia a página inteira e chega a (0,6,1). */
  html.dc .page.tabroot-page.harmony-page .toc-hero:not(.sc-sub-hero){ margin:0 0 4px; }
  html.dc .harmony-page .se-label{ margin-bottom:2px; font-size:11px; letter-spacing:.10em; }
  html.dc .harmony-page .hm-group{ --halo:10px; }
  html.dc .harmony-page .hm-dock-wrap{ --teto:110px; }
  /* 🔴🔑 [16/09 · LOTE 4f] AQUI A FOLGA NÃO SE RESOLVE PELO `--cw`, e é a única
     caixa onde isso acontece: o slot deste tamanho é uma LINHA (`width:100%`),
     logo o lado transversal dele não é o `--cw` — é o que sobra da coluna depois
     do padding. A reserva do zoom fica circular (o padding decide a largura que
     decide o crescimento que decide o padding), e resolve-se de uma vez:
       f ≥ 0,08·(W − 2f) + halo   →   f ≥ (0,08·W + halo) / 1,16
     `8cqw` é exactamente 0,08·W — a largura do próprio contentor, não do ecrã.
     Medido em A1: 19,5 px de cada lado, slot de 119 px, e o rótulo mais longo
     ("HARMÔNICA", ≈61 px ao piso de 11) continua com 103 px de caixa. */
  /* [16/09 · LOTE 4g] o `--halo` desta conta era a mesma reserva curta de todas
     as outras: passa a ser o `--dentro` (o alcance real menos o que vem de fora),
     e a caixa de recorte ganha o `--fora` como nas restantes. A largura do slot
     não se mexe: o que entra no padding sai da margem negativa. */
  html.dc .harmony-page .hm-dock{
    padding-inline:calc((8cqw + var(--dentro)) / 1.16 + var(--fora));
    width:calc(100% + 2 * var(--fora)); margin-inline:calc(-1 * var(--fora));
  }
  /* 🔴🔑 NO TELEMÓVEL DEITADO O SLOT DEIXA DE SER QUADRADO, e é o desperdício que
     o obriga. Medido em A1: cada coluna tem **158 px de largura** e o quadrado
     só usa 74 — **84 px por coluna** a não fazer nada, enquanto o rótulo era
     espremido até 9,7 px ao lado desse vazio. Três slots quadrados numa faixa de
     250 px é a altura a mandar; a largura, essa, sobra.
     O slot passa a ocupar a coluna inteira e a manter a ALTURA que a faixa dá:
     158×74 em vez de 74×74. As quatro fileiras continuam idênticas ENTRE SI, que
     é o que ele pediu ("do mesmo tamanho e identicos") — o que muda é a forma
     nesta caixa, e ele já tinha dito que o telemóvel deitado tem resposta
     própria. A caixa de texto passa de 68 para ~150 px e o rótulo volta ao piso.
     Nas outras nove caixas o slot continua quadrado, como no ar. */
  html.dc .hm-dock{ align-items:stretch; }
  html.dc .hm-dock .hm-chip{
    width:100%; height:var(--cw); min-height:0;
    padding:5px 8px; border-radius:14px; letter-spacing:0;
  }
  /* 🔴 E O BOTÃO VOLTA AO ALVO DE DEDO. Eu tinha-o posto em `min-height:0` para
     poupar 6 px de faixa, e o E5 apanhou-o a **38 px** — 6 px abaixo do piso de
     44. Poupar altura contra um piso de dedo é a troca que esta bancada não faz:
     o que se poupa vem do slot, o que se perde é a mão de quem toca. */
  html.dc .harmony-page .practice-cta.hm-cta{ min-height:44px; padding:6px 16px; }
  html.dc .harmony-page .practice-cta-title{ font-size:14px; }
  html.dc .harmony-page .practice-cta-arrow{ font-size:18px; }
}

/* ── 19e(4) · O MONITOR DEITADO TAMBÉM NÃO TEM BARRA EM BAIXO ───────────────
   🔴 MEDIDO a 1280×720 pelo `validate_desktop`: o rótulo do slot descia a
   **9,72 px** contra um piso de 12. A causa não é o slot — é o orçamento: o
   bloco `dt` gasta **56 px em cima e 96 em baixo** de respiro de MONITOR, e numa
   janela de 720 isso deixa a faixa com ~252 px. Três slots nessa faixa dão 56 px
   de lado, e "HARMÔNICA" a 12 px precisa de 67 de caixa.
   🔑 É a MESMA conta que o lote 3 fez para o tablet e que saiu com a lápide do
   bloco 18: numa tela deitada a barra de abas é um TRILHO à esquerda, e os 96 px
   reservados em baixo são espaço para uma peça que não está lá. Devolvidos, a
   faixa passa a ~380 e o slot a 98 — o rótulo volta acima do piso.
   E o padding lateral do chip desce a 5 px pelo mesmo lever que o `tb` já usa. */
@media (orientation:landscape){
  html.dt:not(.tb) body.mode-interactive .page.special.tabroot-page.harmony-page.visible[data-page="harmony"]{
    padding-top:calc(env(safe-area-inset-top, 0px) + 14px);
    padding-bottom:calc(env(safe-area-inset-bottom, 0px) + 14px);
  }
  html.dt:not(.tb) .hm-dock .hm-chip{ padding:10px 5px; }
}

/* ── 19e(3) · O RESPIRO QUE A BARRA DE ABAS NÃO USA ─────────────────────────
   A `.page` reserva `82px + safe-area` em baixo para a barra de abas. No tablet
   essa barra é um TRILHO à esquerda (app.css:5743) e os 82 px ficam a ser vazio
   por baixo do botão — medido em B: 80 px. É a mesma conta que o bloco 18 fez em
   paisagem no lote 3; aqui vale também em pé, e só no `tb`, que é onde a barra
   de baixo não existe. Não é reorganizar a tela (o ponto 01): é não reservar
   espaço para uma peça que não está lá. */
html.tb body.mode-interactive .page.special.tabroot-page.harmony-page.visible[data-page="harmony"]{
  padding-bottom:calc(env(safe-area-inset-bottom, 0px) + 12px);
}

/* ── 19f(2) · O CTA DA TELA DE ESCOLHA ──────────────────────────────────────
   A mesma `.practice-cta` da harmonia, e por isso quase não tem regra própria:
   o que muda é o RÓTULO ("Montar harmonia" lá, "Praticar" aqui), porque é o
   rótulo que diz o que cada tela faz. Foi assim que o Luis resolveu o conflito
   entre a fileira e a checkbox, e o desenho segue a resolução em vez de a
   contradizer com duas peças diferentes.
   🔴 A 1.ª volta deste bloco deixou este botão SEM ESTILO NENHUM — eu apaguei as
   regras do `.hm-cta` ao refazer a harmonia e não reparei que a tela de escolha
   as partilhava. Saiu um botão cinzento de sistema no canto, com o texto certo
   lá dentro: o tipo de estrago que a régua não apanha (o texto está lá, o alvo é
   clicável) e que a FOTO mostra em dois segundos. */
html .hm-prat-page{ display:flex; flex-direction:column; }
html .hm-prat-page .practice-cta.hm-cta{
  position:sticky; bottom:0; z-index:5; flex:0 0 auto;
  margin-top:auto; margin-bottom:0; max-width:none; width:100%; align-self:stretch;
}
html .hm-prat-page .practice-cta[disabled]{
  opacity:.38; pointer-events:none; box-shadow:none; filter:saturate(.5);
}

/* ── 19f(3) · O BOTÃO COLADO AO FUNDO DESCONTA A BARRA DE ABAS ──────────────
   🔴 MEDIDO no P1: o `position:sticky; bottom:0` cola ao fundo da JANELA, e no
   telemóvel a janela acaba por baixo da `.tabbar` — 58 px de barra fixa mais a
   área segura. A foto mostrou o "Praticar" cortado ao meio pela barra assim que
   a lista dos sete graus passou a rolar. Na harmonia não se via porque lá a
   página cabe e o `sticky` nunca chega a agarrar — o mesmo defeito, escondido
   por uma tela caber e a outra não.
   No `dt` a barra é um TRILHO à esquerda e não há nada em baixo para descontar:
   fica só a área segura. */
html .harmony-page .practice-cta.hm-cta,
html .hm-prat-page .practice-cta.hm-cta{
  bottom:calc(58px + env(safe-area-inset-bottom, 0px) + 8px);
}
html.dt .harmony-page .practice-cta.hm-cta,
html.dt .hm-prat-page .practice-cta.hm-cta{
  bottom:calc(env(safe-area-inset-bottom, 0px) + 8px);
}

/* ── 19c(2) · O TEXTO PASSA A APROVEITAR O CARTÃO (ponto 07, 2.ª volta) ──────
   *"eles aproveitam a tela, mas o texto de cada card nao aproveita o card"*

   🔴 MEDIDO, e ele tem razão nas duas pontas:
     caixa   cartão      bloco de texto   usa da ALTURA   largura do texto
      D1    416×353        278×91            **26 %**       278 de 416
      M1    357×300       235×113              38 %         235 de 357
      C     446×170        335×77              45 %         335 de 446
   São duas perdas com a mesma raiz: o cartão é uma LINHA (`align-items:center`),
   logo o texto fica entalado ao LADO do ícone — perde a largura que o ícone
   ocupa — e centrado na vertical, com o resto da altura a sobrar acima e abaixo.
   Fazer a letra crescer sem mexer nisto seria pior: no D1 o texto tem 278 px, e
   a 24 px daria **23 caracteres por linha**, abaixo do piso de leitura de 30.

   🔑 A CURA É A QUE ELE JÁ APROVOU NO CARTÃO DAS MÚSICAS: ícone no canto
   superior esquerdo, título ao lado, e a descrição por BAIXO dos dois, com a
   largura toda. Aí a letra pode crescer sem encurtar a linha — em D1 o texto
   passa de 278 para 366 px e a 21 px dá 35 cpl, dentro do piso.
   Duas telas, o mesmo gesto: é vocabulário do app, não uma ideia nova.

   🔑 E O TAMANHO SAI DO CARTÃO, NÃO DO ECRÃ. `vmin` foi o que eu usei na 1.ª
   volta e é a régua errada: o que aperta aqui é a ficha, e ela muda de tamanho
   com o número de colunas. Com `cqh` sobre o próprio cartão, um cartão de 353 px
   dá um ícone de 92 e um título de 28; um de 124 (o tablet em pé, onde as fichas
   são baixas e largas) dá 32 e 17 — a mesma regra, sem eu escrever dois números.
   ⚠️ `container-type:size` só entra aqui porque a fileira do grelha tem altura
   DEFINIDA (`grid-auto-rows:minmax(0,1fr)`, bloco 19c). No telemóvel a altura vem
   do conteúdo, e ali a contenção colapsaria o cartão — por isso nada disto chega
   ao `dc` nem ao telemóvel em pé. */
html.tb .games-page .gm-card,
html.dt:not(.dc) .games-page .gm-card{
  container-type:size;
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto;
  /* 🔴 [16/09 · LOTE 4e] AS FILEIRAS DEIXAM DE TER UMA QUE COME TUDO.
     *"nos cards dos jogos em telas grandes, ainda ha muito espaco vazio dentro
       dos cards. e so pegar o texto e centralizar nos espacos vazios."*
     A fileira do meio era `minmax(0,1fr)` e engolia a sobra, empurrando-a toda
     para BAIXO do texto — o bloco ficava encostado em cima com meio cartão
     vazio por baixo. Com as três a `auto` e `align-content:center`, elas passam
     a ser um bloco só e a sobra reparte-se em cima e em baixo.
     🔑 E é o contrário do que eu tinha recusado duas voltas atrás: eu não quis
     empurrar a descrição para o FUNDO porque isso abriria um buraco no MEIO —
     e tinha razão. Centrar o bloco INTEIRO não abre buraco nenhum: transforma a
     cauda em respiro simétrico, que é a distinção que o V4 desta bancada faz. */
  grid-template-rows:auto auto auto;
  align-items:center; align-content:center;
  column-gap:clamp(10px, 4cqh, 20px);
  row-gap:clamp(8px, 4cqh, 18px);
  text-align:left;
}
html.tb .games-page .gm-ico,
html.dt:not(.dc) .games-page .gm-ico{
  grid-area:1 / 1 / 2 / 2;
  /* 🔴 O ÍCONE CRESCE MAIS DO QUE EU TINHA POSTO, e é ele que fecha a conta.
     Com 26cqh o texto passou a usar 43 % da altura em D1 — melhor que os 26 %,
     mas ainda meio cartão vazio. Crescer a DESCRIÇÃO não serve: a 28 px ela
     cairia para 26 caracteres por linha, abaixo do piso de leitura. E empurrá-la
     para o fundo era pior ainda — criava um buraco no MEIO, que é precisamente a
     distinção que o V4 desta bancada faz entre buraco fechado e cauda.
     Quem pode crescer sem pagar nada é o ícone: ele é o âncora visual da ficha e
     não tem linha para encurtar. A 34cqh dá 120 px num cartão de 353. */
  width:clamp(42px, 34cqh, 128px); height:clamp(42px, 34cqh, 128px); border-radius:18px;
}
html.tb .games-page .gm-ico svg,
html.dt:not(.dc) .games-page .gm-ico svg{ width:50%; height:50%; }
/* o agrupador dissolve-se para o título e a descrição serem itens da MESMA
   grelha do ícone — sem tocar no DOM, que é o que as réguas da casa leem. */
html.tb .games-page .gm-txt,
html.dt:not(.dc) .games-page .gm-txt{ display:contents; }
html.tb .games-page .gm-card strong,
html.dt:not(.dc) .games-page .gm-card strong{
  grid-area:1 / 2 / 2 / 3; align-self:center;
  /* 🔴 O PISO DA FAMÍLIA CONTINUA A MANDAR. Tirar o tamanho só da altura do
     cartão colapsa-o onde o cartão é BAIXO: medido em B (fichas de 634×124) e em
     C (446×170), o título caía a 17 e a descrição a **14 px** — abaixo do piso de
     corpo do tablet, que é 17. A altura do cartão diz quanto ele PODE crescer;
     quem diz quanto ele TEM de ter é a família. `max()` entre os dois. */
  font-size:max(var(--tt-fz-lead), clamp(17px, 9.5cqh, 34px));
  line-height:1.08; letter-spacing:-.01em;
}
html.tb .games-page .gm-lock,
html.dt:not(.dc) .games-page .gm-lock{ grid-area:1 / 3 / 2 / 4; align-self:center; }
html.tb .games-page .gm-card em,
html.dt:not(.dc) .games-page .gm-card em{
  grid-area:2 / 1 / 3 / 4; align-self:start; width:100%;
  /* 🔑 O TETO DA DESCRIÇÃO É O PISO DE LEITURA, e é uma conta: a coluna de texto
     em D1 tem 362 px, e 30 caracteres por linha (o chão do T4b) pedem
     `fz ≤ 362 / 30 / 0.5 = 24 px`. Acima disso a letra cresce mas a LINHA
     encurta, e o cartão fica cheio de texto que se lê pior — que é trocar um
     defeito por outro. 24 é o ponto onde os dois se tocam.
     🔴 E O TAMANHO TEM DE SAIR DAS DUAS MEDIDAS DO CARTÃO, não só da altura: o
     D2 tem cartões de 356×363 — mais ALTOS que largos — e com o tamanho a sair só
     de `cqh` a linha caiu para **28 caracteres**, abaixo do piso. A largura é que
     manda na linha; a altura manda em quanto cabe. `min()` das duas, e o termo de
     largura (5,6cqw) é exactamente a conta dos 30 cpl feita ao contrário. */
  font-size:max(var(--tt-fz-body), clamp(14px, min(6.8cqh, 5.6cqw), 24px)); line-height:1.42;
  /* 🔴 E UM TETO DE LINHA. Ao dar a largura toda ao texto, B passou a **84
     caracteres por linha** — o T4b tem chão E tecto (30–75), e uma linha comprida
     demais cansa tanto como uma curta. 62ch fica no meio da janela. */
  max-width:62ch;
}
html.tb .games-page .gm-best,
html.dt:not(.dc) .games-page .gm-best{ grid-area:3 / 1 / 4 / 4; justify-self:start; }

/* ── 19g · O ANEL DE FIGURAS, NAS DUAS TELAS QUE ELE APONTOU ─────────────────
   *"no ipad pro em pe o anel de figuras esta pequeno, muito espaco a aproveitar.
     no monitor o anel deve ser substituido por algo que tenha layout que caiba
     bem em um retangulo horizontal, como o da tela. o anel foi otimizado para
     dispositivos moveis e ocupa um espaco redondo. nessa tela o espaco
     disponivel e diferente. adapte"*

   🔑 ELE NOMEOU A CAUSA MELHOR DO QUE EU A TERIA ESCRITO: um anel é uma peça
   REDONDA, e uma peça redonda só usa bem um espaço quadrado. Medido:

     caixa   caixa do anel      anel        o que sobra
      D1        848×795        480×480      315 de altura e 368 de largura
      M1       1100×325        321×321      **779 px de largura**
      M2       1100×161        157×157      ainda pior

   No tablet em pé o espaço É quadrado e o anel serve — só estava travado por um
   teto meu. No monitor o espaço é uma FAIXA, e ali o anel encolhe até à altura e
   abandona três quartos da largura. Não é o mesmo defeito com dois tamanhos: são
   dois problemas, e por isso duas curas.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── 19g(1) · O TABLET EM PÉ: o teto sobe, porque o espaço é quadrado ───────
   O teto de 480 px veio do lote 2, onde a conta que mandava era a do ALVO: o aro
   interno só paga um alvo de 48 px a partir de S ≥ 320, e 480 era folga de sobra
   para as caixas de então. Nunca foi um limite de desenho — era um mínimo que
   virou máximo por descuido. Em D1 a caixa tem 795 px de lado útil e o anel
   parava nos 480.
   A aritmética do aro continua a fechar, e com muito mais folga: a figura é 17 %
   do anel, logo a 720 px ela mede 122 e a folga do aro interno é
   `0,17·720 − 61 − 24 = 37 px` (contra os 3,2 medidos no lote 2 a 340 px). */
@media (orientation:portrait){
  html.tb .mp-ed-anel .mp-roda-grande{
    width:min(100cqmin, 720px); height:min(100cqmin, 720px);
  }
  /* 🔑 E OS MODIFICADORES CRESCEM COM ELE. O alvo deles vinha de `--tt-tap/1.45`
     (48 px visuais), que é o PISO do dedo — certo num anel de 340, desproporcional
     num de 720, onde as figuras medem 122. A aritmética do aro interno diz quanto
     eles podem crescer: `folga = 0,17·S − F/2 − T/2`, e com S=720 e F=122 sobra
     espaço até T≈106. A 70 px visuais a folga fica em 26 px — o dobro do que o
     lote 2 tinha a 340. Cresce o que pode crescer, não o máximo. */
  html.tb .mp-ed-anel .mp-roda-grande{
    --mp-tog-b:clamp(calc(var(--tt-tap) / 1.45), 6cqmin, 52px);
    --mp-tup-b:clamp(calc(var(--tt-tap) / 1.12), 7cqmin, 62px);
  }
}

/* ── 19g(2) · O MONITOR: O ANEL VIRA BARRA ──────────────────────────────────
   *"o anel foi otimizado para dispositivos moveis e ocupa um espaco redondo.
     nessa tela o espaco disponivel e diferente. adapte"*

   🔑 E O QUE ELE ESTÁ A DIZER É QUE A PEÇA TEM DE MUDAR DE FORMA, não de
   tamanho. Numa faixa de 1100×325 um círculo de 321 usa 9 % da área; as mesmas
   dez peças numa LINHA usam-na inteira, e ainda ficam maiores do que estavam.

   🔑 E NÃO SE TOCA NO DOM — que é o que torna isto barato e reversível. As
   posições do anel são `left`/`top` em percentagem, escritas pelo JS em
   `__mpRodaHTML`. `position:static` faz o navegador IGNORAR left/top sem que
   ninguém as tenha de apagar: os mesmos dez botões, a mesma ordem, os mesmos
   `data-mp-fig`/`data-mp-tog` que o delegate lê. O JS não sabe que isto existe,
   exactamente como o `__hmEixo` não sabe se o dock está de pé ou deitado.

   A ORDEM DO DOM É A ORDEM DA BARRA, e calha certa: seis figuras, depois as nove
   razões (escondidas fora do modo quiáltera), depois os quatro modificadores.
   Em repouso lê-se `figuras · modificadores`; em quiáltera o `.is-tup` esconde as
   figuras e mostra as razões, e a barra passa a `razões · modificadores` sem uma
   regra a mais. O respiro antes do primeiro modificador é o que separa os dois
   grupos — não é um enfeite, é a única fronteira que a linha tem.

   ⚠️ Os AROS saem: são três círculos SVG de `inset:0` desenhados para marcar as
   posições de um anel que deixou de existir. Deixá-los seria um contorno a
   atravessar a barra. */
@media (min-width:600px){
  /* 🔴 O ANEL CONTINUA A ABSORVER A SOBRA. Pus `flex:0 0 auto` na 1.ª volta e o
     espaço que a barra libertou (200 px) caiu TODO num sítio só — um vão morto
     entre as ações e o piano, medido: ações aos 380, piano aos 631. Trocar um
     círculo pequeno num descampado por uma barra com um buraco por baixo não é
     adaptar, é mudar o vazio de sítio. Com `flex:1 1 0` a sobra volta a ser
     repartida à volta da barra, que é respiro simétrico e não buraco. */
  html.dt:not(.tb) .mp-ed-anel{
    flex:1 1 0; container-type:normal; width:100%;
  }
  html.dt:not(.tb) .mp-roda-grande{
    aspect-ratio:auto; width:100%; height:auto;
    max-width:none; max-height:none;
    display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
    gap:clamp(8px, 1.1vw, 20px);
    padding:clamp(10px, 1.4vh, 22px) 0;
  }
  html.dt:not(.tb) .mp-roda-grande .mp-aro{ display:none; }
  /* `position:static` é o que desliga o `left`/`top` que o JS escreveu, sem
     ninguém ter de os apagar; as margens negativas eram a centragem do ponto. */
  html.dt:not(.tb) .mp-roda-grande .mp-fig,
  html.dt:not(.tb) .mp-roda-grande .mp-raz,
  html.dt:not(.tb) .mp-roda-grande .mp-tog{
    position:static; margin:0; flex:0 0 auto; transform:none;
  }
  html.dt:not(.tb) .mp-roda-grande .mp-fig{
    width:clamp(64px, 7.6vw, 124px); height:clamp(64px, 7.6vw, 124px);
  }
  html.dt:not(.tb) .mp-roda-grande .mp-fig svg,
  html.dt:not(.tb) .mp-roda-grande .mp-fig-pausa svg{ width:52%; height:70%; }
  /* 🔴 OS MODIFICADORES PRECISAM DE MOLDURA, e é o aro que lha dava. No anel
     eles são glifos NUS pousados em cima do contorno interno — o círculo é que
     diz "isto é uma fileira de botões". Com o aro fora, ficaram quatro sinais
     soltos a flutuar ao lado das figuras: medido, fundo transparente e borda de
     0 px. A moldura que entra aqui não é enfeite: é a peça que se perdeu ao
     trocar de forma, devolvida noutro material.
     E o GLIFO cresce com a caixa — sem isto o `·` e o `⌒` ficavam do tamanho de
     sempre dentro de um botão de 49 px, que foi o que a foto mostrou. */
  html.dt:not(.tb) .mp-roda-grande .mp-tog{
    width:clamp(44px, 3.4vw, 60px); height:clamp(44px, 3.4vw, 60px);
    background:rgba(246,241,231,.07);
    border:1px solid rgba(246,241,231,.18);
    font-size:clamp(20px, 1.9vw, 30px); line-height:1;
  }
  html.dt:not(.tb) .mp-roda-grande .mp-tog svg{ width:58%; height:58%; }
  html.dt:not(.tb) .mp-roda-grande .mp-tog.is-on{
    background:rgba(232,105,10,.20); border-color:rgba(232,105,10,.75);
  }
  html.dt:not(.tb) .mp-roda-grande .mp-tog-tup{ font-size:clamp(13px, 1.1vw, 17px); }
  html.dt:not(.tb) .mp-roda-grande .mp-tog-tup{
    width:clamp(62px, 5vw, 84px); border-radius:12px;
  }
  html.dt:not(.tb) .mp-roda-grande .mp-raz{
    width:clamp(44px, 3.6vw, 62px); height:clamp(44px, 3.6vw, 62px);
  }
  /* a fronteira entre os dois grupos da barra */
  html.dt:not(.tb) .mp-roda-grande .mp-tog[data-mp-tog="pausa"]{
    margin-left:clamp(14px, 2.4vw, 48px);
  }
}

/* ── 19h · O `[hidden]` VOLTA A VALER NA TELA LARGA ─────────────────────────
   🔴 [16/09 · Luis] *"a pagina minhas musicas agora quando deito a tela, mostra
   a pagina da musica junto, botoes de exportar exclusivo das paginas. sendo que
   essa pagina deveria ter apenas a lista de musicas, e os botoes de envio."*

   MEDIDO, e ele tem razão em cinco das seis caixas largas:

     caixa   `.mp-folha[hidden]`          o que aparecia por baixo da lista
      B       display:grid · 560 px       Exportar PDF · MIDI · Nova pauta · Apagar · Praticar
      C       display:grid · 453 px       as mesmas cinco
      D2      display:grid · 453 px       as mesmas cinco
      M1      display:grid · 434 px       as mesmas cinco
      A1      display:grid · 283 px       as mesmas cinco
      P1      display:NONE  ·   0 px      (o telemóvel em pé nunca teve o defeito)

   🔑 O MECANISMO, e é uma lição que vale para todo o ficheiro: **`[hidden]` é
   uma regra de folha do AGENTE, e vale (0,1,0)** — `[hidden]{display:none}`. A
   tela desta secção mostra a LISTA e a FOLHA no mesmo `[data-page="mp-folha"]`,
   trocando uma pela outra com `el.hidden = true/false` (index.html:15694 e
   15817). E o bloco 15 escreveu `html.dt .mp-folha{display:grid}` — (0,2,0),
   que GANHA ao `[hidden]`. Enquanto o `dt` era só monitor ninguém rodava para
   lá; o lote 3 acendeu o `dt` no TABLET (o lado curto ≥600 no toque) e o defeito
   chegou ao aparelho dele com a classe.

   🔴 A CASA JÁ SABIA DISTO e está escrito em onze sítios — `.mp-sheet[hidden]`,
   `.sc-fim[hidden]`, `.mp-pdf[hidden]`, com o comentário do 6888 a dizer em voz
   alta *"o display:flex TEM de ceder ao [hidden]"*. O que faltou foi aplicá-lo
   à regra que nasceu depois. Por isso esta não nomeia dois elementos: nomeia a
   secção inteira com peso que ganha a qualquer `html.dt`/`html.tb` que venha,
   hoje ou daqui a um lote.

   ⚠️ NÃO leva `!important`: `html .x[hidden]` é (0,3,0) e já ganha ao (0,2,0)
   do `dt` por especificidade e à ordem por posição — e um `!important` aqui
   tiraria a um futuro estado legítimo (uma animação de saída, por exemplo) a
   hipótese de discordar. */
html .mp-hub[hidden],
html .mp-folha[hidden],
html .mp-folha-util[hidden],
html .mp-folha-foot[hidden]{ display:none; }

/* 🔴🔑 E A VARREDURA ACHOU UM SEGUNDO, QUE O LUIS NÃO NOMEOU E ESTÁ NO AR.
   Depois de perceber o mecanismo, perguntei à tela inteira em vez de à tela
   dele: em cinco caixas e vinte telas, que elementos com `[hidden]` têm
   `display` diferente de `none`? Vieram dois. O outro é este:

     BUTTON.practice-cta.qz-next → display:flex   [P1 B C M1 A1 · tela quiz]
     medido no P1: **359×52 px, opacidade 1, dentro da janela**

   É o "Continuar ›" do jogo. O quiz esconde-o ao começar a pergunta
   (index.html:34942), mostra-o depois de responder (35081) e volta a esconder
   (35152) — a intenção não tem dúvida nenhuma. Só que `.practice-cta` tem
   `display:flex` (0,1,0) e empata com o `[hidden]` da folha do agente, ganhando
   por ORDEM. Resultado: o botão esteve sempre lá, e clicá-lo salta a pergunta
   sem responder.
   ⚠️ NÃO É deste lote nem do lote 3 — aparece também no telemóvel em pé, onde
   não há `dt` nenhum. Está no ar hoje. Entra nesta subida porque é a MESMA
   cura de uma linha, e porque subir ao lado de um defeito conhecido sem o
   arrumar é o que faz dívida virar mobília. */
html .practice-cta[hidden]{ display:none; }

/* ── 19i · AS PORTAS DE ENTRADA VIRAM O PAINEL DA FOLHA ─────────────────────
   🔴 [16/09 · Luis] *"transforme os botoes de entrada de musica (notas caindo,
   escanear, nova partitura etc) deixe eles exatamente iguais e seguindo a mesma
   logica dos botoes da pagina da musica (exportar pdf, midi, etc). as musicas
   em si continuam como cards normais."*

   🔑 "A MESMA LÓGICA" JÁ VEIO DE GRAÇA, e é o sinal de que a troca foi feita no
   sítio certo: as quatro portas passaram a ser `.mp-util-btn` dentro de um
   `.mp-folha-util` (index.html, `__mpListaRender`) e herdaram TODA a resposta
   que o painel da folha já tinha, sem uma linha nova. Medido:

     caixa                     antes            agora
     P1 telemóvel em pé        4 cartões        4 fichas de 84×68 na linha
     B  tablet em pé           4 cartões        2×2, 312×67 a 14 px
     A1 telemóvel deitado      4 cartões        2×2, 330×64
     C  tablet deitado         4 cartões        uma por linha, em coluna
     M1 monitor                4 cartões        uma por linha, em coluna

   ESTA REGRA É A ÚNICA COISA QUE FALTAVA, e é sobre a COLUNA, não sobre a
   ficha: com duas filhas (a lista e o painel), o `repeat(auto-fit,minmax(360px,
   1fr))` do bloco 15 reparte a tela ao MEIO — medido, 445|445 no tablet deitado
   e 543|543 no monitor. Na folha da música a repartição não é meio a meio: a
   pauta leva o que sobra e o painel leva 320-380 px (app.css:10232). É a mesma
   hierarquia aqui — quem manda na tela é a LISTA DE MÚSICAS; as portas são a
   margem. Sem isto, "a mesma lógica" parava na ficha e falhava na página.
   ⚠️ Só em paisagem e fora do `dc`: em pé a coluna é uma só e a lista fica em
   cima com as portas por baixo, que é exactamente o que a folha faz em pé. */
@media (orientation:landscape){
  html.tb .mp-hub,
  html.dt:not(.dc) .mp-hub{
    grid-template-columns:minmax(0,1fr) minmax(280px,360px);
    align-items:start;
  }
}
