/* tokens.css: <link> próprio via mvp3d_links_css_app() — @import relativo
   não herda ?v= (2026-08-11). */

/* Letras desportivas (texto do editor 3D) — self-hosted em /fonts, sem
   chamada a CDN externo (multi-tenant, offline-friendly). Só o subconjunto
   "latin" de cada família (pt/en/es/fr/nl não precisam de mais) para manter
   o peso baixo. font-display:swap: mostra a alternativa do sistema até a
   letra carregar, sem bloquear o resto da página. */
@font-face { font-family: 'Bebas Neue'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/bebas-neue.woff2') format('woff2'); }
@font-face { font-family: 'Anton'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/anton.woff2') format('woff2'); }
@font-face { font-family: 'Oswald'; font-style: normal; font-weight: 600; font-display: swap; src: url('fonts/oswald.woff2') format('woff2'); }
@font-face { font-family: 'Teko'; font-style: normal; font-weight: 600; font-display: swap; src: url('fonts/teko.woff2') format('woff2'); }
@font-face { font-family: 'Racing Sans One'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/racing-sans-one.woff2') format('woff2'); }
@font-face { font-family: 'Russo One'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/russo-one.woff2') format('woff2'); }
@font-face { font-family: 'Squada One'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/squada-one.woff2') format('woff2'); }
@font-face { font-family: 'Staatliches'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/staatliches.woff2') format('woff2'); }
@font-face { font-family: 'Rajdhani'; font-style: normal; font-weight: 600; font-display: swap; src: url('fonts/rajdhani.woff2') format('woff2'); }
@font-face { font-family: 'Titillium Web'; font-style: normal; font-weight: 700; font-display: swap; src: url('fonts/titillium-web.woff2') format('woff2'); }


/* MW: SPECIFIC */
html, body {
    margin: 0;
    /* Arrastar no palco ou nos painéis fazia o iOS entrar em seleção de texto
       e pintar tudo de azul, com pegas de seleção por cima do modelo (o
       utilizador mandou capturas). Isto é uma aplicação, não um documento —
       só os campos de texto é que se selecionam (regra a seguir). */
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    /* O bloqueio de pinch em painel.js (gesturestart/gesturechange/gestureend)
       só trava o gesto de dois dedos — o duplo-toque do Safari para ampliar é
       outro mecanismo, sem gesture events, e continuava a passar em qualquer
       zona fora do canvas (que já tinha touch-action: none). Isto fecha essa
       lacuna sem tirar o toque simples nem o arrastar. */
    touch-action: manipulation;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background: var(--bg);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
    color: var(--texto);
}

/* MW: REMOVE (Tailwind cobre) */
.escondido { display: none !important; }

/* a exceção: escrever num campo tem de continuar a permitir selecionar */
/* MW: SPECIFIC */
input, textarea, [contenteditable] {
    user-select: text;
    -webkit-user-select: text;
}

/* ------------------------------------------------------------ header */
/* #topo, .marca, .brand*, .topo-voltar, .topo-centro, .topo-modelo,
   .topo-acoes, conta e .topo-creditos → utilities em partials/topo.php.
   Fica: override absolute do .menu-contexto na conta, .topo-creditos.negativo
   (JS) e .ico (árvore de camadas). */

/* .menu-contexto é position:fixed (nasceu para menus abertos no sítio do
   rato); aqui é um dropdown preso ao avatar, daí o absolute — fica em CSS
   porque sobrepor um :fixed partilhado por utility exigiria !important. */
/* MW: SPECIFIC */
.conta-caixa .menu-contexto {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 190px;
}

/* Estado ligado por JS (saldo negativo, ver js/ia.js) — não dá para exprimir
   em utilities estáticas. */
/* MW: REMOVE (Tailwind cobre) */
.topo-creditos.negativo { color: var(--perigo-claro, #e5484d); }
/* MW: REMOVE (Tailwind cobre) */
.topo-creditos.negativo svg { color: currentColor; }

/* ícones SVG inline (substituem os emojis) */
/* MW: SPECIFIC */
.ico {
    width: 15px;
    height: 15px;
    vertical-align: -2px;
    flex-shrink: 0;
}

/* MW: SPECIFIC */
.camada .camada-acoes button .ico {
    width: 12px;
    height: 12px;
    vertical-align: -1px;
}

/* --------------------------------------------- editor de moldes (principal) */

/* Largura do painel esquerdo (rail + conteúdo: Partes, Cores, Tecido…) e do
   painel de camadas à direita — usadas para alinhar tudo o resto da faixa
   central (alternador, ferramentas, palco) sem repetir números mágicos em
   cada regra. */
/* MW: SPECIFIC */
:root {
    --painel-largura: 336px;
    --camadas-largura: 260px;
    /* 14px davam cinco goteiras (fora, rail↔painel, painel↔palco, palco↔
       camadas, fora) — ~70px que o palco não tinha e as colunas não usavam
       para nada. A 6px continua a ler-se a separação, porque os painéis já
       têm borda e canto próprios; quem ganha o espaço é a peça, que é o que
       se veio ver (pedido do Wermerson, 2026-08-07). */
    --margem: 6px;
}

/* top mínimo (só a folga do header) — alternador-vista e a barra de
   ferramentas são overlays A SÉRIO agora (position:fixed + z-index por
   cima), não reservam faixa própria, por isso o palco/editor podem começar
   quase logo abaixo do header. */
/* MW: SPECIFIC */
#editor-principal {
    /* claro de propósito, mesmo no tema escuro — ver a nota do --palco-bg */
    background: var(--palco-bg);
    position: fixed;
    top: 68px;
    left: calc(var(--painel-largura) + var(--margem) * 2);
    right: calc(var(--camadas-largura) + var(--margem) * 2);
    bottom: var(--margem);
}

/* Vista principal 3D: ocupa a mesma faixa que o editor 2D — ver painel.js
   (aplicarVista) e cena3d.js (moverPalcoPara/redimensionar, reaproveitados
   tal e qual da miniatura/modal, sem duplicar a cena three.js).
   #palco-principal-caixa preenche o retângulo TODO (não fica preso a um
   quadrado central) — a câmara tem FOV vertical fixo, por isso um retângulo
   mais largo só revela mais FOV horizontal, sem encolher a peça. */
/* MW: SPECIFIC */
#palco-principal {
    /* Palco claro de sempre por padrão; o cenário é opt-in («Exibir palco»
       desmarcado por omissão, decisão de 2026-08-01) — o painel.js põe a
       classe com-palco quando o toggle liga, e aí o fundo passa a preto por
       baixo da foto do pódio (#palco-podio), do shader do fumo
       (#palco-fumaca) e da cena transparente. O controlo «Fundo» dos 3D
       pinta a cena por cima em qualquer caso. */
    background: var(--palco-bg);
    position: fixed;
    top: 68px;
    left: calc(var(--painel-largura) + var(--margem) * 2);
    right: calc(var(--camadas-largura) + var(--margem) * 2);
    bottom: var(--margem);
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Preto e não #020208: é a cor dos cantos da foto do pódio, para não se ver
   onde ela acaba. */
/* MW: SPECIFIC */
#palco-principal.com-palco { background: #000; }

/* Com o cenário ligado, as margens à volta do palco (fundo do body) ficavam
   brancas contra o preto — uma moldura clara a cortar o efeito. O body
   acompanha o preto enquanto o palco escuro está NA VISTA (a classe escondido
   é como aplicarVista alterna 2D/3D); os cartões flutuantes têm fundo e
   z-index próprios e continuam por cima. */
/* MW: SPECIFIC */
body:has(#palco-principal.com-palco:not(.escondido)) { background: #000; }

/* Cursor de arrastar quando o rato está sobre um decalque selecionado no 3D.
   A classe decal-hover é adicionada por cena3d.js; o !important é necessário
   para sobrepor o cursor inline que o OrbitControls escreve no canvas. */
/* MW: SPECIFIC */
body.decal-hover #palco-principal-caixa canvas,
body.decal-hover #palco-grande canvas { cursor: move !important; }

/* Interruptor bloqueado (JS: painel.js + classe .desativada). */
/* MW: SPECIFIC */
.controlos-3d-linha-modo.desativada { opacity: .45; cursor: not-allowed; }

/* Foto do pódio néon: as medidas (left/top/width) são escritas pelo cena3d.js
   a cada frame, a partir da projeção do chão da cena — é isso que faz a peça
   pousar no tampo e a sombra de contacto cair no sítio certo. Sem 3D montado
   fica escondida (o JS põe display:none), nunca desalinhada. */
/* MW: SPECIFIC */
#palco-podio {
    position: absolute;
    z-index: 0;
    left: 0;
    top: 0;
    width: 100%;
    isolation: isolate;
    background: url('img/palco-podio.webp') center / 100% auto no-repeat;
    aspect-ratio: 1600 / 589;
    pointer-events: none;
    /* A foto raramente cobre o palco todo (o tamanho dela vem do 3D, não do
       ecrã): sem isto via-se a aresta dela contra o preto do #palco-principal.
       Desvanecer as margens faz o estúdio continuar para lá da imagem. */
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 16%),
        linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 16%),
        linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
}

/* Tingir o chão do palco (pedido do Wermerson, 2026-08-03): --palco-foto-tint
   fica transparente por omissão (sem efeito nenhum) — só ganha cor quando o
   #cor-fundo-palco em painel.js escreve nesta variável. mix-blend-mode:color
   usa o matiz/saturação da cor por cima mas mantém a luminosidade da foto por
   baixo, dando um filtro colorido sem apagar a textura do chão. isolation no
   pai garante que o blend só reage à foto dele, não ao resto do palco atrás. */
/* MW: SPECIFIC */
#palco-podio::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--palco-foto-tint, transparent);
    mix-blend-mode: color;
    pointer-events: none;
}

/* MW: SPECIFIC */
#palco-fumaca {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    border: 0;
    pointer-events: none;
}

/* MW: SPECIFIC */
#palco-principal-caixa { position: relative; z-index: 2; width: 100%; height: 100%; }

/* Controlos 3D: base desktop em utilities (index.php). Aqui fica o que
   utilities não cobrem bem — reparent para Configurações (seletor composto),
   interruptor (::after/:checked), seta do <details>, xadrez da bolinha. */

/* Dentro da aba "Configurações": deixa de ser cartão flutuante — vive na
   coluna do painel (painel.js reparenta só no desktop). */
/* MW: SPECIFIC */
[data-secao="configuracoes"] #controlos-3d {
    position: static;
    left: auto;
    bottom: auto;
    transform: none;
    width: 100%;
    /* A moldura do cartão flutuante tem de sair TODA. Faltavam a borda, o
       fundo e o canto (2026-08-11): dentro da secção davam um cartão dentro
       do cartão do painel, com uma borda a cortar a coluna a meio. O
       box-shadow já cá estava — os outros três é que ficaram para trás. */
    border: 0;
    background: none;
    border-radius: 0;
    box-shadow: none;
    padding: 4px 2px;
    gap: 14px;
}

/* MW: SPECIFIC — linha1 passou a levar o botão Girar/Play-Pause E os
   ícones enquadrar/zoom lado a lado (2026-08-14, compactar a área); a coluna
   e o stretch eram de quando só tinha o botão. */
[data-secao="configuracoes"] .controlos-3d-linha1 {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

/* MW: SPECIFIC */
[data-secao="configuracoes"] #bt-girar-3d {
    padding: 6px 10px;
    font-size: 13px;
    background: var(--bg-sutil);
    border-radius: 10px;
}

/* MW: SPECIFIC */
[data-secao="configuracoes"] .controlos-3d-zoom { justify-content: flex-end; }

/* MW: SPECIFIC */
[data-secao="configuracoes"] .controlos-3d-zoom button { width: 30px; height: 30px; }

/* MW: SPECIFIC */
[data-secao="configuracoes"] .controlos-3d-linha-modo {
    gap: 12px;
    padding: 4px 2px;
}

/* MW: SPECIFIC */
[data-secao="configuracoes"] .folha-3d-grupo {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* MW: SPECIFIC */
[data-secao="configuracoes"] .controlos-3d-rotulo {
    min-width: 0;
    font-size: 12.5px;
}

/* toast descartável (avisoBreve em editor2d.js) — por cima da nav do telemóvel */
/* MW: SPECIFIC */
.aviso-breve {
    position: fixed;
    left: 50%;
    bottom: calc(var(--m-nav-h, 0px) + var(--m-painel-h, 0px) + 14px);
    transform: translateX(-50%);
    z-index: 60;
    max-width: min(88vw, 420px);
    padding: 9px 14px;
    border-radius: 12px;
    background: var(--bg-elevado);
    color: var(--texto);
    border: 1px solid var(--borda);
    box-shadow: 0 6px 22px rgba(0, 0, 0, .35);
    font-size: 12.5px;
    line-height: 1.35;
    pointer-events: none;
}

/* Seta do «Mais opções» (pseudo + estado [open]). */
/* Xadrez por baixo da cor (inline style pinta o background-color). */
/* MW: SPECIFIC */
.cor-mini-bola {
    background-image: conic-gradient(var(--xadrez) 90deg, transparent 90deg 180deg, var(--xadrez) 180deg 270deg, transparent 270deg);
    background-size: 8px 8px;
}

/* #barra-vistas + #vistas-camara + .vista-btn → utilities em index.php.
   Fica o alternador (abaixo) e a posição em paisagem (media). */

/* MW: SPECIFIC */
#alternador-vista {
    display: flex;
    justify-content: center;
    pointer-events: none;
}

/* MW: SPECIFIC */
#alternador-vista .alt-btns { pointer-events: auto; }

/* MW: SPECIFIC */
#alternador-vista > .alt-btns {
    display: flex;
    gap: 2px;
    background: var(--bg-elevado);
    border: 1px solid var(--borda);
    border-radius: 999px;
    padding: 3px;
    box-shadow: var(--sombra);
}

/* MW: SPECIFIC */
.alt-btn {
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .02em;
    padding: 6px 16px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--texto-fraco);
    cursor: pointer;
}

/* MW: SPECIFIC */
.alt-btn:hover { color: var(--texto); }

/* MW: SPECIFIC */
.alt-btn.ativa { background: var(--accent); color: var(--sobre-accent); }

/* Os moldes vivem soltos sobre o fundo da página: o canvas ocupa o palco
   inteiro, transparente e sem moldura — o desenho recorta as peças pelas
   máscaras, por isso não há "cartão" à volta. */
/* MW: SPECIFIC */
#canvas-moldes {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    background: transparent;
    touch-action: none;
}

/* Rail de ferramentas 2D — skin em utilities (index.php). Fica o estado
   activo (JS) e o layout mobile/paisagem mais abaixo. */
/* MW: REMOVE (Tailwind cobre) */
.ferramenta.ativa { background: var(--accent-fraco); }
/* MW: REMOVE (Tailwind cobre) */
.ferramenta-auto.ativa .fer-rotulo { color: var(--accent); }
/* MW: REMOVE (Tailwind cobre) */
.ferramenta.ativa svg { fill: var(--accent); }

/* ------------------------------------------------ painel de edição (esquerda) */

/* #nav-secoes é irmão de #painel (não filho) — assim consegue continuar
   visível/fixo mesmo quando #painel está colapsado ou (no mobile) só uma
   secção de cada vez está aberta; um pai com display:none esconderia um
   filho position:fixed junto, não há como escapar disso em CSS. */
/* MW: SPECIFIC */
#nav-secoes {
    position: fixed;
    top: 66px;
    left: var(--margem);
    bottom: var(--margem);
    width: 84px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 14px 6px;
    background: var(--bg-elevado);
    border: 1px solid var(--borda);
    /* Encostado ao #painel (pedido do Wermerson): só os cantos exteriores
       arredondam — o rail e o painel leem-se como um bloco só, não duas
       cartas com vão entre elas. A borda direita fica sem raio para
       encaixar na esquerda quadrada do #painel logo a seguir. */
    border-right: 0;
    border-radius: 14px 0 0 14px;
    box-shadow: var(--sombra);
    z-index: 6;
    overflow-y: auto;
}

/* MW: SPECIFIC */
#painel {
    position: fixed;
    top: 66px;
    left: calc(var(--margem) + 84px);
    bottom: var(--margem);
    width: calc(var(--painel-largura) - 84px);
    box-sizing: border-box;
    background: var(--bg-elevado);
    border: 1px solid var(--borda);
    border-radius: 0 14px 14px 0;
    box-shadow: var(--sombra);
    color: var(--texto);
    z-index: 6;
    /* visible e não hidden: a pega de colapsar vive a cavalo na borda
       (right/left: -12px) e com overflow:hidden ficava cortada ao meio —
       via-se meia lua e não um botão (chamado #117). Quem tem de cortar é o
       conteúdo, e esse já rola dentro de #painel-conteudo. */
    overflow: visible;
}

/* Base dos botões do rail → utilities ($twNavBtn no index.php). Fica o
   estado activo (JS). */
/* MW: REMOVE (Tailwind cobre) */
#nav-secoes button.ativa { color: var(--accent-texto); background: var(--accent-fraco-2); }

/* Botão de encolher/expandir cada painel lateral — poking pra fora do
   próprio cartão (não fica preso a #painel-conteudo, senão desaparecia
   junto com o resto quando colapsado e não haveria como reabrir). Encolher
   só muda --painel-largura/--camadas-largura (ver :root mais acima) — como
   toda a faixa central é calc() em cima dessas variáveis, o palco/editor
   crescem automaticamente para ocupar o espaço libertado. */
/* MW: SPECIFIC */
.bt-colapsar {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 24px;
    height: 24px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Contraste próprio, não emprestado do painel: colapsado, o cartão fica
       transparente e a pega passa a estar sozinha em cima do fundo da página
       — com o tom do painel (quase o do fundo) parecia ter desaparecido
       (chamado #117). */
    background: var(--bg-sutil);
    border: 1px solid var(--borda-forte);
    border-radius: 50%;
    box-shadow: var(--sombra);
    color: var(--texto);
    cursor: pointer;
    z-index: 8;
}

/* MW: REMOVE (Tailwind cobre) */
.bt-colapsar svg { width: 14px; height: 14px; }

/* MW: REMOVE (Tailwind cobre) */
.bt-colapsar:hover { background: var(--accent); border-color: var(--accent); color: var(--sobre-accent); }

/* MW: SPECIFIC */
.bt-colapsar-dir { right: -12px; }

/* MW: SPECIFIC */
.bt-colapsar-esq { left: -12px; }

/* peça girada 180° quando o painel está colapsado (aponta para o lado
   oposto, "puxa para abrir" em vez de "empurra para fechar") */
/* MW: SPECIFIC */
.bt-colapsar.colapsado svg { transform: rotate(180deg); }

/* MW: SPECIFIC */
body.painel-colapsado { --painel-largura: 84px; }
/* MW: SPECIFIC */
body.painel-colapsado #painel-conteudo { display: none; }

/* MW: SPECIFIC */
body.camadas-colapsado { --camadas-largura: 0px; }
/* MW: SPECIFIC */
body.camadas-colapsado #painel-camadas > *:not(.bt-colapsar) { display: none; }
/* MW: SPECIFIC */
body.camadas-colapsado #painel-camadas { width: 26px; padding: 0; border: 0; box-shadow: none; background: transparent; }
/* MW: SPECIFIC */
body.camadas-colapsado .bt-colapsar-esq { left: 0; }

/* Reserva espaço fixo para a scrollbar — sem isto a barra sobrepõe-se ao
   conteúdo quando aparece (chamado 2026-08-07). */
/* MW: REMOVE (Tailwind cobre) */
#painel-conteudo,
#painel-camadas-conteudo {
    scrollbar-gutter: stable;
}

/* Num flex column com overflow, os filhos ENCOLHEM antes de haver scroll —
   o menu do "Adicionar imagem" chegava a ficar com 2px com zoom/ecrã baixo.
   Sem encolher, o painel rola como deve ser. (flex/padding em utilities) */
/* MW: REMOVE (Tailwind cobre) */
#painel-conteudo > * {
    flex-shrink: 0;
}

/* O <select> do nome da peça saiu com os chips de peça (2026-08-13) — as
   regras dele (skin do select, seta, foco, altura no toque) saíram junto. */

/* Gaveta "Avançado": marker nativo escondido + seta a rodar ao abrir.
   Tipografia do summary → utilities ($twAdvancedSummary). */
/* MW: SPECIFIC */
.grupo-avancado[open] > summary svg { transform: rotate(90deg); }

/* Espelhos da estampa (chamado #169): estado activo fica no CSS (JS).
   Base do botão → utilities ($twStampMirror). */
/* MW: REMOVE (Tailwind cobre) */
.bt-estampa-espelho.ativa { color: var(--sobre-accent); background: var(--accent); border-color: var(--accent); }

/* Presença: skin em utilities (app.js). Estado "eu" ligado por JS. */
/* MW: REMOVE (Tailwind cobre) */
.presenca-avatar.eu { background: var(--fundo-fraco-2); color: var(--texto-fraco); }

/* Cores / picker / recentes — skin em utilities (index.php + painel.js).
   Fica: xadrez da cor base (JS limpa background quando não há cor uniforme),
   pastilha aberta do picker, xadrez do molde 2D e amostra com --cor-amostra. */
/* MW: SPECIFIC */
#cor-base-bola {
    /* Sem cor base escolhida: xadrez discreto, a dizer "ainda não pintaste". */
    background:
        linear-gradient(45deg, var(--borda) 25%, transparent 25%, transparent 75%, var(--borda) 75%) 0 0 / 10px 10px,
        linear-gradient(45deg, var(--borda) 25%, transparent 25%, transparent 75%, var(--borda) 75%) 5px 5px / 10px 10px,
        var(--bg-elevado);
}

/* MW: REMOVE (Tailwind cobre) */
.zona-cor-linha-aberta { border-color: var(--accent); }

/* MW: SPECIFIC */
#peca-desenho-bloco {
    background:
        linear-gradient(45deg, var(--borda) 25%, transparent 25%, transparent 75%, var(--borda) 75%) 0 0 / 16px 16px,
        linear-gradient(45deg, var(--borda) 25%, transparent 25%, transparent 75%, var(--borda) 75%) 8px 8px / 16px 16px,
        var(--bg-elevado);
}

/* linhas de cor da estampa (multi-cor, 2026-08-01): estilos em utilities
   Tailwind montados pelo painel.js — .cor-estampa-linha/.cor-estampa-hex
   ficam só como identificadores para o JS, sem estilos aqui */

/* estampa selecionada na árvore: o dock mostra SÓ as coisas da estampa */
/* MW: SPECIFIC */
#painel-peca.so-estampa #fundo-bloco { display: none; }

/* ferramentas da árvore (expandir/fechar/alfinete): estilos em utilities
   Tailwind no index.php — 1.º bloco migrado (tw.css, 2026-08-01) */

/* Catálogo tecido/estampa — grelha + cartões em utilities (index.php /
   painel.js). Fica: seta do <details> (pseudo) e estado .ativa (JS). */
/* MW: SPECIFIC */
.estampa-grupo > summary::before {
    content: '';
    width: 0;
    height: 0;
    border: 4px solid transparent;
    border-left-color: currentColor;
    transition: transform .15s;
}

/* MW: SPECIFIC */
.estampa-grupo[open] > summary::before { transform: rotate(90deg) translateX(1px); }

/* MW: REMOVE (Tailwind cobre) */
.estampa.ativa {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent) inset;
}

/* Modais adicionar/texto/ajuda/partilha — overlay + cartões + campos
   migrados para utilities (index.php / galeria.php, 2026-08-07). Fica o
   estado JS da zona de largar e a biblioteca gerada em ia.js. */
/* MW: SPECIFIC */
#zona-colar:focus,
#zona-colar.arrastando {
    border-color: var(--accent);
    background: rgba(108, 92, 231, .06);
}

/* Biblioteca do modal "Adicionar imagem": chrome estático → utilities no
   index.php. Aqui ficam itens gerados em ia.js (.bib-item…) e o estado
   #biblioteca.vista-lista. */
/* MW: REMOVE (Tailwind cobre) */
.bib-item { position: relative; }

/* MW: REMOVE (Tailwind cobre) */
.bib-imagem {
    display: block;
    width: 100%;
    padding: 0;
    border: 2px solid var(--borda);
    border-radius: 10px;
    background: var(--bg-sutil-2);
    cursor: pointer;
    overflow: hidden;
    aspect-ratio: 1;
}

/* MW: REMOVE (Tailwind cobre) */
.bib-imagem:hover { border-color: var(--accent); }

/* MW: REMOVE (Tailwind cobre) */
#biblioteca-grelha img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* texto (pedido + data): só existe na vista de lista */
/* MW: SPECIFIC */
.bib-texto { display: none; }

/* MW: REMOVE (Tailwind cobre) */
.bib-pedido {
    font-size: 12px;
    color: var(--texto);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* MW: REMOVE (Tailwind cobre) */
.bib-data { font-size: 12px; color: var(--texto-fraco-2); }

/* × de remover: ao pairar no rato; sempre visível no toque, senão no
   telemóvel não havia forma nenhuma de tirar uma imagem da biblioteca. */
/* MW: REMOVE (Tailwind cobre) */
.bib-remover {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--fundo-sobreposto);
    color: #fff;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    display: none;
    align-items: center;
    justify-content: center;
}

/* MW: SPECIFIC */
.bib-item:hover .bib-remover { display: flex; }

/* MW: REMOVE (Tailwind cobre) */
.bib-remover:hover { background: var(--perigo-forte); }

/* MW: SPECIFIC */
@media (pointer: coarse) {
    .bib-remover { display: flex; }
}

/* vista de lista: miniatura pequena + pedido + data, uma por linha */
/* MW: SPECIFIC */
#biblioteca.vista-lista #biblioteca-grelha {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* MW: SPECIFIC */
#biblioteca.vista-lista .bib-item {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 4px;
    border-radius: 10px;
}

/* MW: SPECIFIC */
#biblioteca.vista-lista .bib-item:hover { background: var(--bg-sutil); }

/* MW: SPECIFIC */
#biblioteca.vista-lista .bib-imagem { flex: 0 0 auto; width: 46px; }

/* MW: SPECIFIC */
#biblioteca.vista-lista .bib-texto {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

/* MW: SPECIFIC */
#biblioteca.vista-lista .bib-remover { position: static; flex: 0 0 auto; display: flex; }

/* MW: REMOVE (Tailwind cobre) */
.bib-carregar {
    padding: 10px 0;
    font-size: 12px;
    text-align: center;
    color: var(--texto-fraco-2);
}

/* lista de camadas — chrome (#lista-camadas, .camadas-vazio, .pasta-dica-vazia)
   → utilities no index.php / editor2d.js. Aqui fica a máquina de estados
   (pastas, linhas, hover, arrasto, sem-camadas). */

/* Pastas da lista: "Geral" (imagens livres) + uma por molde. Também são alvo
   de largada — largar aqui muda a camada de pasta (fixa/solta do molde). */
/* MW: SPECIFIC */
.camada-pasta {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    padding: 3px 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--texto-fraco);
    border-radius: 8px;
}

/* MW: SPECIFIC */
#lista-camadas .camada-pasta:first-child { margin-top: 0; }

/* MW: REMOVE (Tailwind cobre) */
.camada-pasta svg { width: 13px; height: 13px; flex: 0 0 auto; }

/* MW: REMOVE (Tailwind cobre) */
.camada-pasta .pasta-nome { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* hover nas pastas: são clicáveis (selecionam a peça) — sem isto pareciam
   cabeçalhos mortos */
/* MW: REMOVE (Tailwind cobre) */
.camada-pasta { cursor: pointer; }
/* MW: REMOVE (Tailwind cobre) */
.camada-pasta:hover { background: var(--fundo-fraco); }

/* Filhos INDENTADOS: lê-se "isto está DENTRO da pasta". (A antiga linha-guia
   em border-left morria contra o `border` shorthand do bloco base da .camada
   e deixava as linhas tortas — o indent limpo chega e lê melhor.) */
/* MW: SPECIFIC */
.camada { margin-left: 20px; }

/* Polish profissional da árvore (2026-08-01): transições suaves, contador
   escondido a zero, lixo da estampa só no hover (é ação, não estado). */
/* MW: SPECIFIC */
.camada, .camada-pasta, .pasta-seta, .camada-estado button, .camada-olho, .pasta-olho { transition: background .12s, color .12s, border-color .12s; }
/* MW: REMOVE (Tailwind cobre) */
.pasta-contagem:empty { display: none; }
/* MW: SPECIFIC */
.camada .camada-lixo { opacity: 0; pointer-events: none; }
/* MW: SPECIFIC */
.camada:hover .camada-lixo, .camada.ativa .camada-lixo, .camada:focus-within .camada-lixo { opacity: 1; pointer-events: auto; }
/* MW: REMOVE (Tailwind cobre) */
.camada-pasta.pasta-ativa { background: var(--accent-fraco-2); }
/* MW: REMOVE (Tailwind cobre) */
/* --accent-texto e não --accent: o roxo da marca sobre o fundo escuro fica
   abaixo de 4.5:1 (AA) — o token de texto existe para isto. */
.camada-pasta.pasta-ativa .pasta-nome { color: var(--accent-texto); }

/* seta fechada roda para ▸ e o conteúdo não se monta (ver editor2d.js) */
/* MW: REMOVE (Tailwind cobre) */
.pasta-seta { cursor: pointer; padding: 4px; margin: -4px; border-radius: 4px; }
/* Hover tem de se VER no tema escuro (feedback do Wermerson, 2026-08-01):
   fundo com a marca translúcida, não um cinza que se confunde com a linha. */
/* MW: REMOVE (Tailwind cobre) */
.pasta-seta:hover { color: var(--texto); background: rgba(124, 108, 255, .18); }
/* MW: SPECIFIC */
.pasta-seta svg { transition: transform .15s; }
/* MW: SPECIFIC */
.pasta-seta.fechada svg { transform: rotate(-90deg); }

/* Sem camada nenhuma no design as pastas continuam a aparecer (são a porta de
   seleção das peças) — e crescem para preencher o vazio (pedido 2026-08-01). */
/* MW: SPECIFIC */
#lista-camadas.sem-camadas .camada-pasta { padding: 7px 8px; font-size: 12px; gap: 9px; }
/* MW: SPECIFIC */
#lista-camadas.sem-camadas .camadas-vazio { padding-bottom: 4px; }
/* MW: SPECIFIC */
#lista-camadas.sem-camadas .camada-pasta > svg { width: 20px; height: 20px; }

/* seta "aberta" — a pista de que a pasta contém as camadas listadas abaixo.
   16px com alvo de 24px: a 11px nem se via nem se acertava (2026-08-01). */
/* MW: REMOVE (Tailwind cobre) */
.pasta-seta { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; color: var(--texto-fraco); flex: none; }
/* MW: REMOVE (Tailwind cobre) */
.pasta-seta svg { width: 16px; height: 16px; }

/* dica de largada (base → utilities em editor2d.js). Só o delta de largada. */
/* MW: SPECIFIC */
.camada-pasta.solta-dentro + .pasta-dica-vazia { border-color: var(--accent); color: var(--accent-texto); }

/* rail-acao / Partes / Cores / #nav-adicionar — visibilidade por largura em
   utilities ($twRailAcao / $twNavSoMobile no index.php). O estilo FAB do «+»
   continua no bloco mobile. */

/* alças de largura dos painéis — só desktop (o telemóvel usa a folha) */
/* MW: REMOVE (Tailwind cobre) */
#camadas-redimensionar { display: none; }
/* MW: REMOVE (Tailwind cobre) */
#painel-redimensionar { display: none; }

/* MW: REMOVE (Tailwind cobre) */
.camada-pasta .pasta-contagem { font-weight: 500; }

/* MW: REMOVE (Tailwind cobre) */
.camada-pasta.pasta-vazia { opacity: .55; }

/* pasta da peça ativa (chamado #170): as camadas seguem a peça selecionada */
/* MW: REMOVE (Tailwind cobre) */
.camada-pasta.pasta-ativa { background: var(--accent-fraco-2); color: var(--accent); opacity: 1; }

/* MW: SPECIFIC */
.camada-pasta.solta-dentro { background: var(--accent-fraco-2); color: var(--accent); opacity: 1; }

/* arrastar para reordenar: a linha em trânsito esmorece; o risco marca onde cai */
/* MW: SPECIFIC */
.camada.arrastando { opacity: .35; }

/* MW: SPECIFIC */
.camada.solta-antes { box-shadow: 0 -2px 0 0 var(--accent); }

/* MW: SPECIFIC */
.camada.solta-depois { box-shadow: 0 2px 0 0 var(--accent); }

/* MW: SPECIFIC */
.camada {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 6px;
    border: 1px solid transparent;
    border-radius: 8px;
    cursor: pointer;
    user-select: none;
}

/* MW: REMOVE (Tailwind cobre) */
.camada:hover { background: var(--fundo-fraco); }

/* MW: REMOVE (Tailwind cobre) */
.camada.ativa {
    border-color: var(--accent);
    background: var(--accent-fraco-2);
}

/* MW: SPECIFIC */
.camada canvas {
    width: 38px;
    height: 30px;
    border: 1px solid var(--borda);
    border-radius: 5px;
    background:
        conic-gradient(var(--xadrez) 90deg, transparent 90deg 180deg, var(--xadrez) 180deg 270deg, transparent 270deg) 0 0 / 12px 12px;
    flex: 0 0 auto;
}

/* MW: REMOVE (Tailwind cobre) */
.camada .camada-nome {
    flex: 1;
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Estado (olho/cadeado/⋮) e ações partilham o canto direito, mas só o estado
   ocupa largura na linha: as ações abrem POR CIMA do nome (right: 100% em
   relação ao canto). Em fila, num painel de 260px, seis ícones de ação mais os
   de estado não cabem — o nome era espremido a 0 e os últimos botões saíam
   para fora do painel. */
/* MW: SPECIFIC */
.camada-ferramentas {
    position: relative;
    display: flex;
    flex: 0 0 auto;
}

/* MW: SPECIFIC */
.camada .camada-acoes {
    display: none;
    position: absolute;
    top: 50%;
    right: 100%;
    transform: translateY(-50%);
    gap: 2px;
    padding: 0 4px 0 8px;
    /* Fundo opaco (cor do painel) para tapar o nome por baixo em vez de o
       espremer. Os tons da linha (--fundo-fraco, --accent-fraco-2) são
       translúcidos — sozinhos deixavam o texto ver-se por entre os ícones —,
       por isso vão por cima como camada, tal como acontece na própria linha. */
    background-color: var(--bg-elevado);
}

/* MW: SPECIFIC */
.camada:hover .camada-acoes {
    background-image: linear-gradient(var(--fundo-fraco), var(--fundo-fraco));
}

/* MW: SPECIFIC */
.camada.ativa .camada-acoes {
    background-image: linear-gradient(var(--accent-fraco-2), var(--accent-fraco-2));
}

/* Sem rato não há hover que revele esta fila — aí quem manda é o ⋮ (ver o
   bloco do menu de camada mais abaixo). */
/* MW: SPECIFIC */
@media (hover: hover) and (pointer: fine) {
    .camada:hover .camada-acoes, .camada:focus-within .camada-acoes { display: flex; }
}

/* 18px e não 22px como os de estado: a linha mais cheia (texto fixo a um
   molde) leva seis destes e tem de caber entre a miniatura e o canto. */
/* MW: REMOVE (Tailwind cobre) */
.camada .camada-acoes button {
    width: 18px;
    height: 18px;
    border: 0;
    border-radius: 5px;
    background: transparent;
    color: var(--texto-fraco);
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    padding: 0;
}

/* mesmo realce dos botões de estado: o preto translúcido não se via no tema
   escuro, e agora que a fila é mais miudinha o alvo tem de se ler bem */
/* MW: REMOVE (Tailwind cobre) */
.camada .camada-acoes button:hover { background: var(--fundo-fraco-2); color: var(--texto); }

/* ------------------------------------------- painel de camadas (direita) */

/* Shell fixo + --camadas-largura (como #painel). Padding/overflow do
   #painel-camadas-conteudo → utilities no index.php. */
/* MW: SPECIFIC */
#painel-camadas {
    position: fixed;
    top: 66px;
    right: var(--margem);
    bottom: var(--margem);
    width: var(--camadas-largura);
    z-index: 6;
    box-sizing: border-box;
    background: var(--bg-elevado);
    border: 1px solid var(--borda);
    border-radius: 14px;
    box-shadow: var(--sombra);
    color: var(--texto);
    /* ver a nota do #painel: a pega de colapsar não pode ser cortada */
    overflow: visible;
}

/* clique em "Camadas" no rail (desktop): as camadas vivem neste painel — o
   pulso mostra ONDE, em vez de o clique parecer não fazer nada */
/* MW: SPECIFIC */
@keyframes pulso-camadas {
    0% { box-shadow: 0 0 0 0 var(--accent); }
    100% { box-shadow: 0 0 0 14px transparent; }
}

/* MW: SPECIFIC */
#painel-camadas.pulso-atencao { animation: pulso-camadas .55s ease-out 2; }

/* Peças (#zonas/.zona) — skin em utilities (painel.js). Fica: estado activo
   + check (::after) e halo da amostra (filter com token). Layout mobile/
   paisagem mais abaixo. */
/* MW: REMOVE (Tailwind cobre) */
.zona.ativa {
    border-color: var(--accent);
    background: var(--accent-fraco-2);
    box-shadow: 0 0 0 1px var(--accent) inset;
}

/* MW: SPECIFIC */
.zona.ativa::after {
    content: '✓';
    position: absolute;
    top: 8px;
    right: 8px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--sobre-accent);
    font-size: 12px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
}

/* MW: SPECIFIC */
/* a sombra segue a forma do molde — sem ela, um molde branco sumia no card */
.zona .amostra { filter: drop-shadow(0 0 1px var(--halo-amostra)); }

/* ------------------------------------------------------------ palco 3D
   A janela flutuante da miniatura (#mini3d) saiu a 2026-08-11 — com ela foram
   a alça de redimensionar, os estados de arrastar/minimizar e o esconder em
   paisagem. Fica só o touch-action do canvas, que vale para os palcos todos. */

/* MW: SPECIFIC */
/* Sem isto, um arrasto no ecrã tátil é disputado entre o OrbitControls e o
   scroll/pan nativo do browser: o gesto pode ser cancelado a meio e a rotação
   sai errática/invertida (o dedo continua a mexer mas o evento já não chega
   ao three.js). O canvas é sempre o mesmo re-parentado (moverPalcoPara). */
#palco-grande canvas, #palco-principal-caixa canvas {
    display: block;
    touch-action: none;
}

/* ------------------------------------------- modal de pacotes de créditos */
/* (Estes estilos perderam-se numa cirurgia de hunks a 2026-08-01 — sem eles
   o modal abria INLINE espalhado pelo topo. Recuperados aqui.) */

/* .creditos-nota/.creditos-pacote/.creditos-pacotes saíram a 2026-08-09: o
   modal de créditos passou a utilities e nenhum markup do repo as usa
   (scripts/css-usage-map.py). São as únicas regras deste ficheiro que se
   apagaram por estarem MORTAS — as 73 marcadas MW: REMOVE continuam em uso. */

/* modal pós-guardar (#modal-guardado / .guardado-*) → utilities em
   partials/topo.php (2026-08-07). .topo-botao-rotulo continua hook do
   executarComFeedback e do hide no telemóvel. */

/* ---------------------------------------------------------- modal da IA
   Chrome (overlay, caixa, cabeçalho, bolhas, composer, sugestões, consumo,
   lightbox, def-select/chips) → utilities em index.php + ia.js (2026-08-07).
   Ficam estados JS, irmãos do chat, animações de progresso e coluna ancorada. */

/* MW: SPECIFIC */
/* Visão realista — chrome/ecrãs → utilities em index.php + js/realista.js.
   Fica a roda (keyframes) e o reduced-motion. */
.realista-roda {
    width: 28px;
    height: 28px;
    border: 3px solid var(--borda);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: realista-roda 1s linear infinite;
}

/* MW: SPECIFIC */
@keyframes realista-roda { to { transform: rotate(360deg); } }

/* MW: SPECIFIC */
@media (prefers-reduced-motion: reduce) {
    .realista-roda { animation-duration: 3s; }
}

/* MW: SPECIFIC */
/* ─────────────────────── moldura do chat da IA (2026-08-07) ────────────────
   Gradiente NA BORDA, não à volta dela: dois fundos na mesma caixa — o cheio
   em padding-box, o gradiente em border-box, com a borda transparente a
   deixá-lo ver-se. É a única forma sem um pseudo-elemento por cima, que
   apanharia os cliques do chat. O movimento é do background-position da
   camada de fora; a de dentro não se mexe porque tem background-size próprio.
   200% + 0→200% percorre um mosaico exato, por isso a volta não dá salto.
   Não vale para o chat dentro do painel esquerdo — lá a caixa não tem
   moldura nenhuma (regra no @media do desktop, mais abaixo). */
.caixa-ia {
    border: 1px solid transparent;
    background:
        linear-gradient(var(--bg-elevado), var(--bg-elevado)) padding-box,
        var(--ia-borda-viva) border-box;
    background-size: auto, 200% 200%;
    box-shadow:
        var(--sombra-modal),
        0 20px 60px -26px var(--accent-fraco);
    animation: iaBordaViva 16s linear infinite;
}

/* MW: SPECIFIC */
@keyframes iaBordaViva {
    from { background-position: 0 0, 0% 50%; }
    to   { background-position: 0 0, 200% 50%; }
}

/* MW: SPECIFIC */
/* Halo por dentro do cartão. z-index explícito nos dois lados: um ::before
   posicionado pinta por cima do conteúdo em fluxo, e sem o z-index:1 nos
   filhos a conversa ficava por baixo do véu. */
.caixa-ia::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(120% 55% at 50% -12%, var(--accent-fraco), transparent 64%),
        radial-gradient(80% 40% at 108% 0, var(--accent-fraco-2), transparent 60%);
}

/* MW: SPECIFIC */
.caixa-ia > * { position: relative; z-index: 1; }

/* MW: SPECIFIC */
/* O botão fechar (top-2.5 right-2.5, Tailwind) caía para position:relative por
   causa da regra acima (mesma especificidade, mas esta vem depois no CSS
   compilado) — saía encostado ao canto superior esquerdo em vez do direito. */
.caixa-ia > .bt-fechar-modal { position: absolute; }
/* z-index maior que o "1" que a regra .caixa-ia > * dá a TODOS os filhos
   por igual (2026-08-08): sem isto, um wrapper de conteúdo com scroll que
   viesse DEPOIS do botão fechar no HTML (modal-texto, modal-encomenda —
   ambos com um <div class="flex-1 ... overflow-y-auto"> logo a seguir ao
   ×) ficava com o MESMO z-index e ganhava a ordem de pintura por vir depois
   no DOM — tapava o canto onde o × está, sem se notar visualmente (o ×
   continuava desenhado por baixo) mas a interceptar o clique. O × parecia
   morto. */
.caixa-ia > .bt-fechar-modal { z-index: 2; }

/* MW: SPECIFIC */
/* $twFecharModal (tw-classes.php) usa top-2.5 fixo — bate certo quando o
   ancestor tem o padding-top do cartão .caixa-ia (pt-[18px], modais). O
   Adicionar imagem/Escrever texto NÃO são modais (secções do painel
   esquerdo, sem esse padding — ver add-image.php/write-text.php): o título
   (h3, m-0) arranca colado ao topo e o botão, medido do mesmo ponto físico,
   ficava visivelmente abaixo da linha dele. Não dá para resolver só na
   string partilhada sem desalinhar os modais (que já batem certo) — o
   ajuste fica aqui, por id, só para estas duas secções. */
#add-image-panel > .bt-fechar-modal,
#write-text-panel > .bt-fechar-modal { top: 4px; }

/* MW: SPECIFIC */
/* Fio do cabeçalho a esbater-se nas pontas — um 1px de ponta a ponta cortava
   o cartão em dois. É fundo, não border, para poder ter gradiente. */
.ia-cabeca {
    border-bottom-color: transparent;
    background-image: linear-gradient(90deg,
        transparent,
        var(--accent-fraco) 18%,
        var(--borda-forte) 50%,
        var(--accent-fraco) 82%,
        transparent);
    background-repeat: no-repeat;
    background-position: bottom left;
    background-size: 100% 1px;
}

/* MW: SPECIFIC */
.ia-cabeca-avatar {
    box-shadow:
        0 0 0 1px var(--accent-fraco),
        0 8px 20px -10px var(--accent);
}

/* MW: SPECIFIC */
/* Barra de compor com a mesma moldura do cartão (sem animação — a esta
   largura o movimento lia-se como erro de render). O foco acende o anel; o
   estado vive aqui e não em utilities focus-within: um `border-color` do
   Tailwind pintava a borda por cima e matava o gradiente. */
.ia-composer {
    border: 1px solid transparent;
    background:
        linear-gradient(var(--bg-sutil-2), var(--bg-sutil-2)) padding-box,
        var(--ia-borda-viva) border-box;
    transition: box-shadow .18s ease;
}

/* MW: SPECIFIC */
.ia-composer:focus-within {
    box-shadow:
        0 0 0 3px var(--accent-fraco-2),
        0 12px 30px -16px var(--accent);
}

/* MW: SPECIFIC */
/* Vazio: o ícone ganha um anel que respira, para a caixa não parecer morta
   enquanto ninguém escreveu nada. */
.ia-vazio-icone {
    position: relative;
    box-shadow:
        0 0 0 1px var(--accent-fraco),
        0 12px 32px -14px var(--accent);
}

/* MW: SPECIFIC */
.ia-vazio-icone::after {
    content: '';
    position: absolute;
    inset: -9px;
    border-radius: 50%;
    border: 1px solid var(--accent-fraco);
    animation: iaHalo 3.6s ease-in-out infinite;
}

/* MW: SPECIFIC */
@keyframes iaHalo {
    0%, 100% { transform: scale(1);    opacity: .9; }
    50%      { transform: scale(1.09); opacity: .3; }
}

/* MW: SPECIFIC */
@media (prefers-reduced-motion: reduce) {
    .caixa-ia { animation: none; }
    .ia-vazio-icone::after { animation: none; }
}

/* MW: SPECIFIC */
/* Ancorar histórico em baixo pelo primeiro filho — justify-content:flex-end
   com overflow torna o topo do histórico inalcançável. */
#ia-chat > :first-child { margin-top: auto; }

/* MW: SPECIFIC */
/* Sem conversa ainda o vazio fica à vista; com histórico some. */
#ia-chat:not(.escondido) ~ #ia-vazio { display: none; }

/* MW: SPECIFIC */
/* Contador só perto do teto de 500 — JS toggles .perto */
.ia-conta { display: none; }
.ia-conta.perto { display: block; }

/* MW: SPECIFIC */
/* A gerar: conversa à vista, zona de compor bloqueada. */
#ia-passo-pedido.a-gerar .ia-sugestoes,
#ia-passo-pedido.a-gerar #ia-definicoes,
#ia-passo-pedido.a-gerar .ia-composer { opacity: .45; pointer-events: none; }
#ia-passo-pedido.a-gerar #ia-vazio { display: none; }
#ia-passo-pedido.a-gerar #ia-chat.escondido ~ #ia-progresso,
#ia-passo-pedido.a-gerar #ia-chat.escondido ~ #ia-typing { margin-top: auto; }

/* MW: SPECIFIC */
/* no painel esquerdo (estreito) os chips quebram de linha em vez de esmagar */
#painel-conteudo .ia-chip { flex: 1 1 auto; min-width: 0; }

/* MW: SPECIFIC */
/* Fita de progresso: width vem do JS; ::after garante movimento em espera. */
#ia-prog-fita {
    position: relative;
    width: 0;
    height: 100%;
    border-radius: 99px;
    background: linear-gradient(90deg, var(--accent), var(--accent-hover));
    transition: width .45s ease-out;
}

/* MW: SPECIFIC */
#ia-prog-fita::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 99px;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .4), transparent);
    animation: iaBrilho 1.5s linear infinite;
}

/* MW: SPECIFIC */
@keyframes iaBrilho {
    from { transform: translateX(-100%); }
    to   { transform: translateX(100%); }
}

/* MW: SPECIFIC */
.ia-prog-dica.a-sair { opacity: 0; }

/* MW: SPECIFIC */
/* Escape/clique fora a meio da geração: o aviso pisca em vez de ignorar. */
.ia-prog-aviso.pisca { animation: iaAvisoPisca 1s ease; }

/* MW: SPECIFIC */
@keyframes iaAvisoPisca {
    0%, 100% { color: var(--texto-fraco); transform: none; }
    15%      { color: var(--accent-texto); transform: translateX(-3px); }
    45%      { color: var(--accent-texto); transform: translateX(3px); }
    70%      { color: var(--accent-texto); transform: none; }
}

/* MW: SPECIFIC */
#bt-fechar-ia:disabled,
#bt-fechar-ia:disabled:hover {
    opacity: .35;
    cursor: not-allowed;
    background: var(--fundo-fraco);
}

/* MW: SPECIFIC */
@media (prefers-reduced-motion: reduce) {
    #ia-prog-fita::after { animation: none; }
    .ia-prog-aviso.pisca { animation: none; color: var(--accent-texto); }
}

/* O chat da IA teve, até 2026-08-10, uma TERCEIRA apresentação aqui: uma
   coluna ancorada à direita do palco a partir dos 1400px (`body.ia-ancorada`,
   `--ia-faixa`/`--ia-largura`). Estava morta havia meses e ninguém dera por
   isso: o re-parenting do app.js entrava a partir dos 901px, e as regras
   `#painel-conteudo #modal-ia` (dois ids) ganhavam-lhe sempre por
   especificidade. Foi-se com ele, e com os dois tokens que só ela usava. */

/* MW: SPECIFIC */
/* menu de contexto / conta — skin em utilities (topo.php, editor2d, cena3d).
   Fica: override absolute do dropdown da conta (composto), hover do tema e
   estado .ativa do idioma (PHP). */
.menu-linha #tema-botao:hover { background: var(--bg-sutil); }

/* ----------------------------------------------------------------- overlay */

/* MW: SPECIFIC */
/* Loading: layout em utilities (index.php). O fundo NÃO pode ser `bg-[…]`
   do Tailwind — essa utility gera `background-color:`, e um gradiente aí é
   inválido (overlay transparente, marca a flutuar em cima do editor).
   Cores da marca também ficam aqui: `.brand-pw/.brand-dev` saíram do app.css
   na migração do topo (viram utilities), mas o typing do overlay cria spans
   só com essas classes. */
.overlay {
    /* o fundo do overlay é escuro nos dois temas — no tema claro as variáveis
       de texto davam cinzento-escuro sobre preto (marca e frase invisíveis) */
    --texto: #f4f5fa;
    --texto-fraco: rgba(244, 245, 250, .55);
    --texto-suave: rgba(244, 245, 250, .55);
    --accent-texto: #8b5cf6;
    color: var(--texto);
    background:
        radial-gradient(60% 42% at 50% 78%, rgba(108, 92, 231, .16), transparent 70%),
        radial-gradient(80% 60% at 50% 30%, rgba(20, 16, 44, .55), transparent 75%),
        #07070d;
}

/* MW: SPECIFIC */
.overlay .brand-pw { color: var(--texto); }
.overlay .brand-dev { color: var(--accent-texto); }
.overlay .overlay-marca-sufixo { color: var(--texto-fraco); font-weight: 600; }

/* MW: SPECIFIC */
.overlay-cursor { animation: overlay-cursor 1s steps(1) infinite; }

/* MW: SPECIFIC */
@keyframes overlay-cursor {
    0%, 50% { opacity: 1; }
    50.01%, 100% { opacity: 0; }
}

/* MW: SPECIFIC */
.overlay-barra > span {
    position: absolute;
    inset: 0 auto 0 0;
    width: 45%;
    border-radius: inherit;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .92), transparent);
    animation: overlay-barra 1.6s cubic-bezier(.45, .05, .55, .95) infinite;
}

/* MW: SPECIFIC */
@keyframes overlay-barra {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(322%); }
}

/* MW: SPECIFIC */
@media (prefers-reduced-motion: reduce) {
    .overlay-cursor { animation: none; }
    .overlay-barra > span { animation: none; width: 100%; background: rgba(255, 255, 255, .5); }
}

/* MW: SPECIFIC */
.overlay-erro { display: none; }

/* MW: SPECIFIC */
/* Telemóvel: cada bloco tem a sua faixa — nada flutua por cima do canvas.
   Topo: cabeçalho + miniatura 3D · ferramentas na horizontal · canvas ·
   folha "Editar" (30vh, painel + rail de secções) · tira de peças. */

/* MW: SPECIFIC */
/* ------------------------------------- lados do modelo (presets de câmara)
   #vistas-camara + .vista-btn → utilities em index.php (incl. max-[900px]).
   Em paisagem o justify fica no media (item 7). */

/* MW: SPECIFIC */
/* --------------------------------------------- cores recentes de uma peça */

/* MW: SPECIFIC */
/* Skin em utilities (painel.js). Fundo com --cor-amostra (variável do JS). */
.cor-recente {
    background-image: linear-gradient(var(--cor-amostra), var(--cor-amostra)),
        conic-gradient(var(--xadrez) 90deg, transparent 90deg 180deg, var(--xadrez) 180deg 270deg, transparent 270deg);
    background-size: 100% 100%, 10px 10px;
}

/* Cor escolhida na paleta fixa do picker (classe posta pelo painel.js) —
   anel com a cor de acento, visível sobre qualquer amostra. */
.cor-recente.ativa {
    box-shadow: 0 0 0 2px var(--bg-elevado, #fff), 0 0 0 4px var(--accent);
}

/* MW: SPECIFIC */
/* ------------------------------------ abas do painel de camadas (direita) */

/* MW: SPECIFIC */
/* Base → utilities ($twAba* no index.php). Ficam estados JS e nav underline
   desktop; T/+ já vão `hidden` no markup. */
.painel-aba.ativa {
    color: var(--sobre-accent);
    background: var(--accent);
}

/* MW: SPECIFIC */
/* Glow temporário ao abrir sozinha (mostrarAba(nome, true) em painel.js) —
   chama a atenção para o painel inteiro (não só o separador) onde a seleção
   nova ficou editável, sem virar estado permanente. Vive no <aside> em vez
   do botão para envolver a caixa toda, incl. o conteúdo já trocado de aba. */
#painel-camadas.painel-camadas--destaque {
    animation: painel-aba-glow 2.2s ease-out;
}

/* MW: SPECIFIC */
@keyframes painel-aba-glow {
    0%, 100% { box-shadow: 0 0 0 0 transparent; }
    15%, 60% { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent), 0 0 18px 3px color-mix(in srgb, var(--accent) 55%, transparent); }
}

/* MW: SPECIFIC */
.aba-conteudo { display: none; }

/* MW: SPECIFIC */
.aba-conteudo.ativa { display: block; }

/* MW: SPECIFIC */
/* ------------------------------- estado de cada camada (ver/esconder, bloquear) */

/* MW: SPECIFIC */
/* Ao contrário de .camada-acoes (que só aparece no hover), estes dois estão
   sempre visíveis: são estado da camada, não ações escondidas. */
.camada-estado {
    display: flex;
    gap: 2px;
    flex-shrink: 0;
}

/* MW: SPECIFIC */
.camada-estado button,
.camada-olho,
.pasta-olho {
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--texto-fraco);
    cursor: pointer;
    flex-shrink: 0;
}

/* MW: REMOVE (Tailwind cobre) */
.camada-estado button:hover,
.camada-olho:hover,
.pasta-olho:hover { background: var(--fundo-fraco-2); color: var(--texto); }

/* MW: REMOVE (Tailwind cobre) */
.camada-estado svg,
.camada-olho svg,
.pasta-olho svg { width: 14px; height: 14px; fill: currentColor; }

/* MW: REMOVE (Tailwind cobre) */
/* escondida: fica esbatida mas legível — ainda é preciso saber o que é para
   a voltar a ligar */
.camada.camada-oculta .camada-nome,
.camada.camada-oculta canvas { opacity: .4; }

/* MW: REMOVE (Tailwind cobre) */
.camada.camada-oculta .camada-olho { color: var(--accent); }

/* MW: REMOVE (Tailwind cobre) */
.camada.camada-bloqueada .camada-cadeado { color: var(--accent); }

/* MW: REMOVE (Tailwind cobre) */
/* bloqueada não se arrasta no palco: o cursor tem de dizer isso já na lista */
.camada.camada-bloqueada { cursor: default; }

/* -------------------------------------------- recolher a folha (telemóvel) */

/* MW: SPECIFIC */
/* Pastilha na borda de cima da folha: recolhe-a para o palco ficar inteiro,
   sem obrigar a trocar de secção. Fica sempre no limite superior da folha,
   por isso acompanha a altura dela — encostada à direita, porque a barra de
   ferramentas ocupa agora o canto esquerdo dessa mesma borda.
   Declarado ANTES das media queries de propósito: com a mesma especificidade,
   ganha quem vem depois, e as regras de telemóvel/paisagem precisam de poder
   mudar estes valores. */
#bt-recolher-folha {
    position: fixed;
    right: 8px;
    bottom: calc(var(--m-nav-h) + var(--m-painel-h) + 4px);
    z-index: 8;
    width: 52px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--borda);
    border-radius: 999px;
    background: var(--bg-elevado);
    color: var(--texto-fraco);
    box-shadow: var(--sombra);
    cursor: pointer;
}

/* MW: SPECIFIC */
#bt-recolher-folha svg { width: 16px; height: 16px; transition: transform .15s; }

/* MW: SPECIFIC */
body.folha-recolhida #bt-recolher-folha svg { transform: rotate(180deg); }

/* MW: SPECIFIC */
@media (max-width: 900px) {

    /* ---------------------------------------- controlos 3D no telemóvel
       Base desktop em utilities; aqui o layout da barra + folha (bottom sheet),
       estados JS (.aberta / .a-arrastar) e pseudos do slider. */
    /* MW: SPECIFIC */
    #controlos-3d {
        left: 0;
        right: 0;
        width: auto;
        z-index: 5;
        bottom: calc(env(safe-area-inset-bottom, 0px) + 10px);
        transform: none;
        display: block;
        padding: 0;
        background: none;
        border: 0;
        box-shadow: none;
    }

    /* MW: SPECIFIC */
    .controlos-3d-barra {
        display: flex;
        align-items: center;
        gap: 14px;
        padding: 0 16px;
    }

    /* MW: SPECIFIC */
    #controlos-3d #slider-girar-3d {
        flex: 1 1 auto;
        width: auto;
        height: 30px;
        margin: 0;
        -webkit-appearance: none;
        appearance: none;
        background: transparent;
    }

    /* MW: SPECIFIC */
    /* Regras separadas por motor: um pseudo desconhecido invalida a lista. */
    #controlos-3d #slider-girar-3d::-webkit-slider-runnable-track {
        height: 5px;
        border-radius: 999px;
        background: rgba(128, 128, 140, .42);
        box-shadow: 0 0 0 1px rgba(255, 255, 255, .22);
    }

    /* MW: SPECIFIC */
    #controlos-3d #slider-girar-3d::-moz-range-track {
        height: 5px;
        border-radius: 999px;
        background: rgba(128, 128, 140, .42);
        box-shadow: 0 0 0 1px rgba(255, 255, 255, .22);
    }

    /* MW: SPECIFIC */
    #controlos-3d #slider-girar-3d::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 22px;
        height: 22px;
        margin-top: -8.5px;
        border: 2px solid #fff;
        border-radius: 50%;
        background: var(--accent);
        box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
    }

    /* MW: SPECIFIC */
    #controlos-3d #slider-girar-3d::-moz-range-thumb {
        width: 22px;
        height: 22px;
        border: 2px solid #fff;
        border-radius: 50%;
        background: var(--accent);
        box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
    }

    /* MW: SPECIFIC */
    #bt-ajustes-3d {
        display: flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        width: 46px;
        height: 46px;
        padding: 0;
        color: var(--texto);
        background: color-mix(in srgb, var(--bg-elevado) 82%, transparent);
        border: 0;
        border-radius: 999px;
        box-shadow: 0 6px 20px rgba(0, 0, 0, .28);
        backdrop-filter: blur(14px);
        -webkit-backdrop-filter: blur(14px);
        cursor: pointer;
    }

    /* MW: SPECIFIC */
    #bt-ajustes-3d svg { width: 21px; height: 21px; }
    /* MW: SPECIFIC */
    #bt-ajustes-3d:active { transform: scale(.94); }

    /* MW: SPECIFIC */
    /* Ajuda flutuante junto ao palco: dá lugar ao #rail-ajuda na barra
       inferior entre 401px e 900px (regra mais abaixo, junto às ordens do
       #nav-secoes) — só volta a flutuar aqui abaixo de 400px, onde a barra
       não tem folga para mais um botão. */
    #bt-ajuda-3d {
        display: none;
    }

    /* MW: SPECIFIC */
    @media (max-width: 400px) {
        /* MW: SPECIFIC */
        #bt-ajuda-3d {
            display: flex;
            align-items: center;
            justify-content: center;
            flex: 0 0 auto;
            width: 46px;
            height: 46px;
            padding: 0;
            color: var(--texto);
            background: color-mix(in srgb, var(--bg-elevado) 82%, transparent);
            border: 0;
            border-radius: 999px;
            box-shadow: 0 6px 20px rgba(0, 0, 0, .28);
            backdrop-filter: blur(14px);
            -webkit-backdrop-filter: blur(14px);
            cursor: pointer;
        }

        /* MW: SPECIFIC */
        #bt-ajuda-3d svg { width: 20px; height: 20px; }
        /* MW: SPECIFIC */
        #bt-ajuda-3d:active { transform: scale(.94); }
    }

    /* MW: SPECIFIC */
    /* O botão de recolher a folha do painel (não a #folha-3d): ao meio,
       COLADO à borda de cima da folha como uma lingueta (2026-08-11 — a
       +4px ficava uma pastilha solta a flutuar sobre a faixa do zoom).
       O -1px funde a borda dele com a da folha; sem canto nem borda em
       baixo, parece crescer dela. */
    #bt-recolher-folha {
        left: 50%;
        right: auto;
        transform: translateX(-50%);
        width: 46px;
        height: 22px;
        bottom: calc(var(--m-nav-h) + var(--m-painel-h) - 1px);
        border-bottom: 0;
        border-radius: 10px 10px 0 0;
        box-shadow: none;
    }

    /* MW: SPECIFIC */
    /* Folha bottom-sheet (JS: .aberta / .a-arrastar; reparent para <body>). */
    #folha-3d {
        display: flex;
        flex-direction: column;
        gap: 10px;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 31;
        max-height: 42vh;
        overflow-y: auto;
        /* `none` e não `contain`: o contain trava o encadeamento mas deixa o
           elástico do iOS — arrastar além do limite esticava o conteúdo e
           expunha um vazio branco dentro da folha (print do Wermerson,
           2026-08-10). O -webkit-overflow-scrolling: touch, que reforçava o
           elástico, saiu pelo mesmo motivo. */
        overscroll-behavior: none;
        padding: 6px 16px calc(env(safe-area-inset-bottom, 0px) + 14px);
        background: var(--bg-elevado);
        border-radius: 1.5rem 1.5rem 0 0;
        box-shadow: 0 -12px 40px rgba(0, 0, 0, .45);
        transform: translateY(101%);
        visibility: hidden;
        transition: transform .34s cubic-bezier(.32, .72, 0, 1), visibility .34s;
    }

    /* MW: SPECIFIC */
    #folha-3d.aberta { transform: none; visibility: visible; }

    /* MW: SPECIFIC */
    #folha-3d.a-arrastar { transition: none; }

    /* MW: SPECIFIC */
    #folha-3d-fundo,
    #folha-3d-fundo.escondido {
        display: block !important;
        position: fixed;
        inset: 0;
        z-index: 30;
        background: rgba(0, 0, 0, .16);
        transition: opacity .34s ease;
        pointer-events: none;
    }

    /* MW: SPECIFIC */
    #folha-3d-fundo.escondido { opacity: 0; }

    /* MW: SPECIFIC */
    .folha-3d-cabecalho {
        display: flex;
        align-items: center;
        gap: 12px;
        padding-top: 6px;
        touch-action: none;
        cursor: grab;
    }

    /* MW: SPECIFIC */
    .folha-3d-grupo {
        display: flex;
        flex-direction: column;
        padding: 2px 12px;
        background: var(--bg-sutil);
        border-radius: 14px;
    }

    /* MW: SPECIFIC */
    .folha-3d-seccao {
        display: block;
        margin-bottom: -6px;
        padding-left: 4px;
        font-size: 12px;
        font-weight: 600;
        letter-spacing: .01em;
        color: var(--texto-suave);
    }

    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-linha-modo,
    #folha-3d .controlos-3d-linha-op {
        min-height: 36px;
        gap: 12px;
        padding: 0;
        align-items: center;
    }

    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-rotulo {
        flex: 0 0 auto;
        width: auto;
        min-width: 104px;
        max-width: 45%;
        font-size: 13px;
        white-space: nowrap;
        color: var(--texto);
    }

    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-linha-modo .controlos-3d-rotulo { flex: 1 1 auto; max-width: none; }

    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-rotulo svg { width: 17px; height: 17px; opacity: .7; }

    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-linha-op input[type="range"] { flex: 1 1 auto; min-width: 0; }

    /* MW: SPECIFIC */
    .folha-3d-valor {
        flex: 0 0 auto;
        width: 42px;
        font-size: 13px;
        font-variant-numeric: tabular-nums;
        text-align: right;
        color: var(--texto-suave);
    }


    /* MW: SPECIFIC */
    #folha-3d #controlos-3d-mais {
        display: flex;
        flex-direction: column;
        gap: 10px;
    }

    /* MW: SPECIFIC */
    #folha-3d #controlos-3d-mais > summary { display: none; }

    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-linha1 {
        order: 0;
        justify-content: space-between;
        padding: 0 2px;
        min-height: 36px;
    }

    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-zoom button { width: 32px; height: 32px; font-size: 17px; }
    /* MW: SPECIFIC */
    #folha-3d .controlos-3d-zoom { gap: 6px; }

    /* MW: SPECIFIC */
    .controlos-3d-linha-repor { justify-content: stretch; margin-top: 0; }

    /* MW: SPECIFIC */
    #folha-3d .linha-cor-mini + .linha-cor-mini { margin-top: 8px; }

    /* MW: SPECIFIC */
    #folha-3d #bt-repor-aparencia {
        width: 100%;
        min-height: 44px;
        padding: 0;
        font-size: 14.5px;
        font-weight: 600;
        color: var(--accent-texto);
        background: var(--bg-sutil);
        border-radius: 16px;
    }

    /* MW: SPECIFIC */
    @media (prefers-reduced-motion: reduce) {
        /* MW: SPECIFIC */
        #folha-3d { transition-duration: .01ms; }
    }

    /* MW: SPECIFIC */
    /* .painel-abas-linha / .painel-aba / .aba-acao / .bt-colapsar /
       .topo-botao-rotulo (Guardar) → max-[900px]: em index.php / topo.php.
       T/+ do cabeçalho ficam `hidden` (proxy do rail).
       Esse max-[900px]:hidden do Tailwind não chegava a vencer aqui
       (2026-08-08): tw.css carrega ANTES de app.css, e a regra de baixo
       (.bt-colapsar, display:flex, sem media query) tem a mesma
       especificidade (0,1,0) — em empate ganha quem vem depois no CSS
       final, ou seja esta folha. Resultado: a seta de colapsar o painel
       lateral aparecia no telemóvel, onde nada colapsa (o painel já nem é
       lateral lá, é a folha de baixo) — override explícito aqui, dentro do
       media query, para não haver dúvidas sobre quem ganha. */
    #bt-colapsar-painel, #bt-colapsar-camadas { display: none; }

    /* MW: SPECIFIC */
    /* Espaço reservado no fundo: uma folha só de cada vez (ou o painel
       esquerdo, ou o de camadas — ver aplicarSecaoMobile em painel.js), com
       altura dependente da secção ativa. "Partes" fica numa tira baixa
       (pílulas horizontais com scroll lateral, sem título) porque escolher a
       peça é um gesto rápido e o palco é que interessa; as outras secções
       precisam da folha inteira. */
    :root {
        --m-nav-h: 54px;
        --m-painel-h: 30vh;
    }
    /* MW: SPECIFIC */
    /* --m-painel-h deixa de ser um número fixo: é medido em painel.js a partir
       da folha visível (ver atualizarAlturaFolha). Com 30vh fixos, "Texturas"
       — uma linha de padrões — deixava metade da folha vazia e o palco pagava
       por isso. Este valor é só o ponto de partida antes da 1.ª medição. */
    body { --m-painel-h: 30vh; }
    /* MW: SPECIFIC */
    body[data-secao-mobile="partes"] { --m-painel-h: 104px; }
    /* MW: SPECIFIC */
    body.folha-recolhida { --m-painel-h: 0px; }
    /* MW: SPECIFIC */
    body.folha-recolhida #painel,
    body.folha-recolhida #painel-camadas { display: none; }

    /* MW: SPECIFIC */
    /* O chat da IA é a única secção que precisa da folha ALTA: em 44vh cabe
       uma bolha e a barra de compor. Altura FIXA (e não a do conteúdo, que é
       como as outras secções se medem — ver atualizarAlturaFolha no painel.js)
       porque uma conversa cresce para o espaço que lhe derem: sem teto a folha
       saltava de tamanho a cada resposta. Era este o único motivo por que o
       chat tinha de ser modal no telemóvel; resolvido aqui, deixou de ser
       preciso ele mudar de identidade por breakpoint. */
    body[data-secao-mobile="ia"] #painel,
    body[data-secao-mobile="ia"] #painel-conteudo {
        /* -20px de folga: a 78vh cheios o topo da folha entrava por baixo da
           faixa flutuante #barra-vistas (3D/2D + Frente/Costas…), que é
           `fixed` e não encolhe com a folha — ficavam sobrepostos em ecrãs
           mais baixos (chamado #249). */
        height: calc(78vh - 20px);
        max-height: calc(78vh - 20px);
    }

    /* MW: SPECIFIC */
    /* A altura tem de atravessar a secção até à caixa do chat (o #ai-panel e a
       caixa já levam `flex-1 min-h-0` em utilities): parar aqui deixava a barra
       de compor a meio da folha, com um vazio por baixo. No desktop a mesma
       regra existe com .secao-ativa-desktop, mais abaixo. */
    body[data-secao-mobile="ia"] #painel-conteudo .secao-painel[data-secao="ia"] {
        display: flex;
        flex-direction: column;
        flex: 1;
        min-height: 0;
    }

    /* MW: SPECIFIC */
    /* Adicionar imagem e Escrever texto: a altura continua a ser a do conteúdo
       (como as outras secções), mas o teto sobe dos 44vh — o formulário de
       texto com contorno + sliders da loja não cabia e ficava um scroll
       apertado onde antes havia um modal de 88vh. */
    body[data-secao-mobile="imagem"] #painel,
    body[data-secao-mobile="imagem"] #painel-conteudo,
    body[data-secao-mobile="texto"] #painel,
    body[data-secao-mobile="texto"] #painel-conteudo {
        max-height: 78vh;
    }

    /* MW: SPECIFIC */
    /* Alternador 3D/2D: o .alt-btn base (fora do media) ganha às utilities
       max-[900px] — por isso o tamanho mobile fica aqui com ID. */
    #alternador-vista .alt-btn {
        font-size: 12px;
        padding: 5px 13px;
    }

    /* MW: SPECIFIC */
    /* Selecionar/Mover/Mão saem do mobile — o ecrã tátil já usa sempre o modo
       "auto" (seleciona e arrasta a vista sozinho), trocar de ferramenta não
       traz nada; só sobra zoom/enquadrar/desfazer/refazer. */
    #fer-selecionar,
    #fer-auto,
    #fer-mao,
    .editor-ferramentas .fer-divisor:first-of-type {
        display: none;
    }

    /* MW: SPECIFIC */
    /* No telemóvel volta a ser horizontal, mas encostada ao canto INFERIOR
       esquerdo do palco: a meio do ecrã (era top:178px) ficava mesmo por
       cima dos moldes, e em baixo fica na zona do polegar. A pastilha de
       recolher a folha vai para o canto oposto — uma em cada ponta da borda
       de baixo, sem nada empilhado sobre o desenho. */
    .editor-ferramentas {
        flex-direction: row;
        top: auto;
        bottom: calc(var(--m-nav-h) + var(--m-painel-h) + 16px);
        left: 8px;
        right: auto;
        transform: none;
        width: max-content;
        max-width: calc(100% - 16px);
        justify-content: center;
        border-radius: 999px;
        padding: 4px;
        gap: 2px;
    }

    /* MW: SPECIFIC */
    .ferramenta { width: 32px; height: 32px; flex: 0 0 auto; }

    /* MW: SPECIFIC */
    .ferramenta svg { width: 17px; height: 17px; }

    /* MW: SPECIFIC */
    .fer-divisor { width: 1px; height: 20px; margin: 0 2px; }

    /* MW: SPECIFIC */
    /* top mínimo — só a folga do cabeçalho. Alternador/miniatura/ferramentas
       são overlays a sério (mesma lógica do desktop), não reservam faixa. */
    #editor-principal,
    #palco-principal {
        top: 58px;
        left: 8px;
        right: 8px;
        bottom: calc(var(--m-nav-h) + var(--m-painel-h) + 8px);
    }

    /* MW: SPECIFIC */
    /* A barra é agora de bordo a bordo (a folha trata do resto) — largura
       fixa aqui só a voltava a encolher para o meio do ecrã. */
    #controlos-3d { bottom: 10px; }

    /* MW: SPECIFIC */
    /* folha "Editar": painel (só o conteúdo agora, o rail saiu daqui — ver
       #nav-secoes abaixo) ocupa a faixa logo acima da barra de abas. O painel
       de camadas usa exatamente a mesma faixa — no telemóvel não há "lado",
       só a folha da secção ativa. */
    #painel,
    #painel-camadas {
        top: auto;
        left: 8px;
        right: 8px;
        bottom: var(--m-nav-h);
        width: auto;
        /* altura do conteúdo, com teto — nunca a folha comer o palco todo */
        height: auto;
        max-height: 44vh;
    }

    /* MW: SPECIFIC */
    /* Os conteúdos rolam por overflow-y:auto com height:100% — mas aqui o
       pai passou a height:auto e a percentagem deixa de resolver: o filho
       crescia ao tamanho do conteúdo e as Propriedades ficavam cortadas sem
       scroll (chamado #108, "opções avançadas não ficam visíveis"). O teto
       tem de viver também no filho que rola. */
    #painel-conteudo,
    #painel-camadas-conteudo {
        max-height: 44vh;
    }

    /* MW: SPECIFIC */
    /* só a secção clicada aparece de cada vez (liberta espaço em vez de
       empilhar tudo) — ver painel.js: aplicarSecaoMobile() alterna estas
       classes conforme o separador tocado. */
    #painel.secao-fechada-mobile,
    #painel-camadas.secao-fechada-mobile {
        display: none;
    }

    /* MW: SPECIFIC */
    #painel-conteudo [data-secao] { display: none; }
    /* MW: SPECIFIC */
    #painel-conteudo [data-secao].secao-ativa-mobile { display: block; }
    /* MW: SPECIFIC */
    #painel-peca.secao-fechada-mobile { display: none; }

    /* MW: SPECIFIC */
    /* rail de secções vira barra de abas fixa no fundo do ecrã (era uma
       coluna vertical espremida dentro do painel — o utilizador reparou que
       ficava errado; no mobile faz mais sentido como bottom-nav a sério). */
    #nav-secoes {
        position: fixed;
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        height: var(--m-nav-h);
        flex-direction: row;
        justify-content: space-around;
        align-items: center;
        padding: 4px;
        border-right: 0;
        border-radius: 0;
        border-top: 1px solid var(--borda);
        box-shadow: none;
        z-index: 20;
        /* Mais secções do que cabem (pedido 2026-08-01): a barra desliza na
           horizontal em vez de espremer os botões — cada um mantém largura
           mínima digna de dedo e o snap alinha ao soltar. */
        overflow-x: auto;
        /* `overflow-x:auto` sozinho computa overflow-y para auto também (spec
           CSS): com texto ampliado (zoom/acessibilidade do iOS) os botões
           passam da altura da barra e um arrasto para CIMA rolava a barra na
           vertical — ícones desapareciam e o «+» ficava meio escondido atrás
           da folha (capturas do Wermerson, 2026-08-12). A barra só se arrasta
           para o lado. */
        overflow-y: hidden;
        touch-action: pan-x;
        overscroll-behavior-x: contain;
        scroll-snap-type: x proximity;
        scrollbar-width: none;
    }

    /* MW: SPECIFIC */
    #nav-secoes::-webkit-scrollbar { display: none; }

    /* MW: SPECIFIC */
    #nav-secoes button {
        flex: 1 0 64px;
        min-width: 64px;
        scroll-snap-align: center;
    }

    /* MW: SPECIFIC */
    #nav-secoes button { padding: 4px 2px; }

    /* MW: SPECIFIC */
    /* max-width + ellipsis: com texto ampliado no telemóvel, «Estampas» saía
       do botão e pintava por cima de «Camadas» (liam-se coladas). O rótulo
       corta com reticências em vez de invadir o vizinho. */
    #nav-secoes button span {
        font-size: 12px;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* MW: SPECIFIC */
    /* Ícones sempre centrados ao longo de toda a barra (pedido do Wermerson,
       2026-08-09): a grelha de colunas fixas (3fr + 64px + 1.5fr×2) só ficava
       bem com exatamente 6 botões visíveis — com menos (ex.: Ajuda escondida
       <400px, ou sem MVP_LOJA) sobravam colunas vazias e os ícones ficavam
       encostados à esquerda; com mais espalhavam-se de forma desigual. Voltar
       a flex (a base já tem justify-content:space-around) com grow:1 em cada
       botão distribui sempre o espaço todo, seja qual for a contagem — a «+»
       deixa de cair no centro matemático exato, mas fica sempre bem distribuída. */
    #nav-secoes button[data-secao="partes"] { order: 1; }
    /* MW: SPECIFIC */
    #nav-secoes button[data-secao="cores"] { order: 2; }
    /* MW: SPECIFIC */
    #nav-secoes button[data-secao="tecido"] { order: 3; }
    /* MW: SPECIFIC */
    #nav-adicionar { order: 4; }
    /* MW: SPECIFIC */
    #nav-secoes button[data-secao="texturas"] { order: 5; }
    /* MW: SPECIFIC */
    #nav-secoes button[data-secao="camadas"] { order: 6; }
    /* MW: SPECIFIC */
    /* Ajuda entra na barra fora do modo-simples também (que já tinha a sua
       própria ordem, linha ~2756) — última posição, span da grelha
       fixa/auto trata do resto via overflow-x (ver comentário acima). */
    #nav-secoes .rail-ajuda { order: 7; }

    /* MW: SPECIFIC */
    /* Abaixo de 400px a barra não tem folga para o botão extra da Ajuda —
       volta a ceder o lugar ao flutuante junto do palco (#bt-ajuda-3d). */
    @media (max-width: 400px) {
        /* MW: SPECIFIC */
        #nav-secoes .rail-ajuda { display: none; }
    }

    /* MW: SPECIFIC */
    #nav-secoes #nav-adicionar {
        flex: 1 0 64px;
        width: auto;
        height: 100%;
        min-width: 64px;
        padding: 4px 2px;
        /* o botão base é coluna sem justify — com o rótulo escondido, o SVG
           colava ao topo da bolinha (chamado #198) */
        justify-content: center;
        border-radius: 12px;
        background: transparent;
        color: #fff;
        box-shadow: none;
    }
    /* MW: SPECIFIC */
    #nav-adicionar:hover { background: transparent; }
    /* MW: SPECIFIC */
    #nav-secoes #nav-adicionar svg {
        width: 42px;
        height: 42px;
        padding: 10px;
        box-sizing: border-box;
        border-radius: 50%;
        background: var(--accent);
        box-shadow: 0 4px 14px rgba(0, 0, 0, .35);
    }
    /* MW: SPECIFIC */
    #nav-adicionar span { display: none; }   /* a bolinha fala por si */
    /* MW: SPECIFIC */
    #nav-adicionar:active { transform: scale(.94); }

    /* MW: SPECIFIC */
    #nav-secoes #nav-encomenda {
        text-shadow: 0 0 10px color-mix(in srgb, var(--accent) 55%, transparent);
    }
    /* MW: SPECIFIC */
    #nav-secoes #nav-encomenda svg {
        filter: drop-shadow(0 0 8px color-mix(in srgb, var(--accent) 62%, transparent));
    }

    /* MW: SPECIFIC */
        /* padrões de estampa: uma linha só, scroll horizontal — o grid de 4
       colunas obrigava a rolar a folha inteira de 30vh na vertical só para
       ver os últimos padrões. */
    #tecidos, .estampa-grelha {
        display: flex;
        overflow-x: auto;
        gap: 6px;
        padding-bottom: 4px;
    }

    /* MW: SPECIFIC */
    /* os grupos continuam empilhados: cada família é a sua tira horizontal */
    #estampas { gap: 0; }

    /* MW: SPECIFIC */
    .estampa-grupo > summary { margin-top: 4px; }

    /* MW: SPECIFIC */
    .estampa { flex: 0 0 76px; }

    /* MW: SPECIFIC */
    /* tira de peças: a secção "Partes" dentro da folha vira uma faixa
       horizontal com scroll — mesmos elementos (#zonas/.zona), só
       reorientados por CSS. Sem título: numa tira de 104px cada linha conta. */
    #painel-conteudo [data-secao="partes"] .panel-title { display: none; }

    /* MW: SPECIFIC */
    #zonas {
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
        width: 100%;
        justify-content: flex-start;
    }

    /* MW: SPECIFIC */
    /* Em «Cores» a peça escolhe-se nos mesmos cartões (o #zonas muda-se para
       o #zonas-slot-cores — ver painel.js): o select do cabeçalho passava a
       ser escolher às cegas, sai do caminho (pedido do Wermerson, 2026-08-02). */
    body[data-secao-mobile="cores"] #painel-peca > .panel-header { display: none; }
    /* MW: SPECIFIC */
    #zonas-slot-cores #zonas { margin-bottom: 8px; }

    /* MW: SPECIFIC */
    .zona {
        flex-direction: column;
        align-items: center;
        justify-content: center;
        text-align: center;
        width: auto;
        min-width: 62px;
        padding: 7px 8px 6px;
        gap: 5px;
    }

    /* MW: SPECIFIC */
    .zona .amostra { width: 26px; height: 26px; }

    /* MW: SPECIFIC */
    .zona-textos { align-items: center; }

    /* MW: SPECIFIC */
    .zona .etiqueta {
        font-size: 12px;
        max-width: 72px;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* MW: SPECIFIC */
    /* .zona-desc → max-[900px]:hidden em TW_ZONA_DESC (painel.js) */
}

/* Desktop: o rail filtra o painel esquerdo tal como no telemóvel — só a
   secção escolhida aparece (antes empilhava-se tudo e o rail era um mero
   scroll: clicar em "Partes" mostrava também cor, estampa e tecido).
   Ver painel.js: aplicarSecaoDesktop() alterna estas classes. */
/* MW: SPECIFIC */
@media (min-width: 901px) {
    #painel-conteudo .secao-painel:not(.secao-ativa-desktop) { display: none; }
    #painel-peca.secao-fechada-desktop { display: none; }
    /* a dica de "nada selecionado" pertence às secções de contexto */
    body:not([data-secao-desktop="cores"]):not([data-secao-desktop="texturas"]) #painel-conteudo > #painel-dica { display: none; }

    /* T/+ do cabeçalho → `hidden` no markup ($twAbaAcao*); proxy do rail. */

    /* (O bloco que desmontava os modais reparentados — position:static,
       caixa-ia sem cartão, width !important — morreu a 2026-08-10: as três
       ferramentas nascem nas secções, já sem markup de modal.) */

    /* Na secção da IA o painel ALARGA. As outras secções são listas de
       controlos e vivem bem nos 336px; uma conversa não — nos ~230px úteis os
       chips quebravam em linhas desalinhadas, o «aplicar a» ia sozinho para
       uma linha e a barra de compor tinha de se partir em duas. A 440px o
       chat fica com os mesmos ~340px da coluna ancorada e usa exatamente o
       mesmo desenho do telemóvel — um só, e não um por largura. O palco
       acompanha (os calc() saem daqui); quem avisa o canvas é o painel.js ao
       trocar de secção. */
    body[data-secao-desktop="ia"] { --painel-largura: 440px; }

    /* Colapsado ganha à secção IA: mesma especificidade que a regra acima
       (uma classe, um atributo), e por vir mais abaixo no ficheiro era ela
       que vencia — o botão trocava de ícone/tooltip mas a largura ficava
       presa a 440px (chamado do Wermerson, 2026-08-10). O seletor combinado
       tem especificidade maior, por isso ganha em qualquer ordem. */
    body.painel-colapsado[data-secao-desktop="ia"] { --painel-largura: 84px; }

    /* …e ocupa a altura toda do painel, com a barra de compor encostada ao
       fundo. Em altura automática sobrava meio painel vazio por baixo. */
    #painel-conteudo .secao-painel[data-secao="ia"].secao-ativa-desktop {
        display: flex;
        flex-direction: column;
        flex: 1;
        min-height: 0;
    }

    /* Desktop: os × não se veem — sai-se pelo rail, como de qualquer outra
       secção. No telemóvel ficam, e devolvem à secção anterior. */
    #painel-conteudo #bt-fechar-ia,
    #painel-conteudo #bt-fechar-adicionar,
    #painel-conteudo #bt-fechar-texto { display: none; }

    /* alça de largura do inspetor: uma tira invisível na borda esquerda que
       engorda no hover — o cursor col-resize é o convite */
    #camadas-redimensionar {
        display: block;
        position: absolute;
        left: -5px;
        top: 0;
        bottom: 0;
        width: 10px;
        cursor: col-resize;
        touch-action: none;
        z-index: 3;
    }
    #camadas-redimensionar::after {
        content: '';
        position: absolute;
        left: 4px;
        top: 0;
        bottom: 0;
        width: 2px;
        border-radius: 2px;
        background: transparent;
        transition: background .15s;
    }
    #camadas-redimensionar:hover::after,
    #camadas-redimensionar.a-arrastar::after { background: var(--accent); }

    /* alça gémea no painel ESQUERDO (pedido 2026-08-01): borda direita */
    #painel-redimensionar {
        display: block;
        position: absolute;
        right: -5px;
        top: 0;
        bottom: 0;
        width: 10px;
        cursor: col-resize;
        touch-action: none;
        z-index: 3;
    }
    #painel-redimensionar::after {
        content: '';
        position: absolute;
        right: 4px;
        top: 0;
        bottom: 0;
        width: 2px;
        border-radius: 2px;
        background: transparent;
        transition: background .15s;
    }
    #painel-redimensionar:hover::after,
    #painel-redimensionar.a-arrastar::after { background: var(--accent); }

    /* «Cores»/«Partes»/#nav-adicionar / .rail-acao → min-[901px]:hidden /
       max-[900px]:hidden no markup (index.php). */

    /* Inspetor em ABAS também no desktop (decisão 2026-08-01, depois de uma
       ronda empilhada): a árvore ganha a altura toda e as Propriedades abrem
       no mesmo sítio quando algo é selecionado — o painel.js troca de aba
       automaticamente ao selecionar/desselecionar. A aba ativa rola no seu
       próprio espaço. */
    #painel-camadas-conteudo {
        display: flex;
        flex-direction: column;
        overflow: hidden;
        padding-bottom: 0;
    }
    .painel-abas-linha { flex: none; }
    /* As pills segmentadas (.painel-aba flex-1, $twAba* no index.php,
       2026-08-07) substituíram o nav underline que vivia aqui — este bloco
       repunha flex:none/fundo transparente/raio 0 por cima delas em
       >=901px, e sem flex-1 as abas paravam de encolher: "Propriedades"
       ficava cortada a direito pelo overflow-x:hidden do painel em vez de
       reduzir com reticências. Removido; as utilities já tratam do estado
       ativo (app.css:1509) e do layout em qualquer largura. */
    .aba-conteudo.ativa {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        margin: 0 -14px;
        padding: 0 14px 14px;
    }
}

/* O viewport trava o zoom manual (user-scalable=no no index.php — sem isto o
   pinch nativo do browser brigava com o pan/zoom próprios do editor e do 3D).
   Só que o Safari do iOS IGNORA user-scalable/maximum-scale desde o iOS 10: o
   que ele respeita é o tamanho da letra do campo focado — abaixo de 16px dá
   zoom na marra e desalinha a página inteira, e não desfaz o zoom ao sair do
   campo.

   Regra por TIPO de campo, não por id: a versão anterior listava ids um a um
   e ficou desatualizada assim que apareceram campos novos (os de Propriedades
   entraram a 12,5px). Por ecrã tátil e não por largura, porque um iPad em
   paisagem passa dos 900px e tem o mesmo comportamento.

   O !important é preciso: as regras base usam ids (#sel-idioma),
   que ganham sempre a um seletor de tipo. A alternativa era voltar a
   enumerá-los — que é exatamente o que apodreceu. */
/* MW: SPECIFIC */
@media (pointer: coarse) {
    select,
    textarea,
    input:not([type]),
    input[type="text"],
    input[type="number"],
    input[type="password"],
    input[type="search"],
    input[type="email"],
    input[type="tel"],
    input[type="url"] {
        font-size: 16px !important;
    }
}


/* ---------------------------------- sliders em ecrã tátil (alvo maior) */

/* O polegar do sistema tem ~12px: num telemóvel é preciso acertar-lhe com um
   dedo de ~9mm. Aumentar obriga a desenhar a barra também (assim que se mexe
   no ::-webkit-slider-thumb, o accent-color deixa de pintar o resto) —
   inclui o próprio preenchimento até ao valor: o WebKit não tem um
   pseudo-elemento nativo para essa parte (ao contrário do Firefox, com
   ::-moz-range-progress, estilizado abaixo), por isso o preenchimento é uma
   var(--pct) escrita no elemento por painel.js (ver pintarPreenchimentoSlider)
   e lida aqui num gradiente. Sem isto a barra ficava sempre plana/cinzenta,
   sem indicar a percentagem (só o número ao lado dizia). */
/* MW: SPECIFIC */
@media (pointer: coarse) {
    #slider-girar-3d,
    #controlos-3d-mais input[type="range"],
    #prop-opacidade,
    #prop-brilho {
        -webkit-appearance: none;
        appearance: none;
        height: 26px;
        background: transparent;
    }

    #slider-girar-3d::-webkit-slider-runnable-track,
    #controlos-3d-mais input[type="range"]::-webkit-slider-runnable-track,
    #prop-opacidade::-webkit-slider-runnable-track,
    #prop-brilho::-webkit-slider-runnable-track {
        height: 6px;
        border-radius: 999px;
        background: linear-gradient(to right,
            var(--accent) var(--pct, 0%),
            var(--fundo-fraco-2) var(--pct, 0%));
    }

    #slider-girar-3d::-moz-range-progress,
    #controlos-3d-mais input[type="range"]::-moz-range-progress,
    #prop-opacidade::-moz-range-progress,
    #prop-brilho::-moz-range-progress {
        height: 6px;
        border-radius: 999px;
        background: var(--accent);
    }

    #slider-girar-3d::-webkit-slider-thumb,
    #controlos-3d-mais input[type="range"]::-webkit-slider-thumb,
    #prop-opacidade::-webkit-slider-thumb,
    #prop-brilho::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 24px;
        height: 24px;
        margin-top: -9px;
        border: 2px solid var(--sobre-accent);
        border-radius: 50%;
        background: var(--accent);
    }

    #slider-girar-3d::-moz-range-track,
    #controlos-3d-mais input[type="range"]::-moz-range-track,
    #prop-opacidade::-moz-range-track,
    #prop-brilho::-moz-range-track {
        height: 6px;
        border-radius: 999px;
        background: var(--fundo-fraco-2);
    }

    #slider-girar-3d::-moz-range-thumb,
    #controlos-3d-mais input[type="range"]::-moz-range-thumb,
    #prop-opacidade::-moz-range-thumb,
    #prop-brilho::-moz-range-thumb {
        width: 24px;
        height: 24px;
        border: 2px solid var(--sobre-accent);
        border-radius: 50%;
        background: var(--accent);
    }
}

/* ------------------------------------------- telemóvel deitado (paisagem) */

/* Em paisagem o ecrã é largo e BAIXO (~390px de altura no iPhone). Empilhar
   folha + tira de peças + barra de abas deixava o palco com ~100px, e os
   controlos flutuantes tapavam-no por inteiro: o utilizador mandou uma
   captura onde o modelo simplesmente não aparecia.
   Passa a organizar-se como um desktop pequeno — controlos à esquerda,
   modelo ao centro, secção ativa à direita, abas em baixo — que é o que a
   forma do ecrã pede. */
/* MW: SPECIFIC */
@media (max-width: 900px) and (orientation: landscape) {
    :root { --m-nav-h: 46px; --m-lateral: 232px; }

    /* a folha deixa de ser folha: vira coluna à direita, com altura toda */
    #painel,
    #painel-camadas {
        top: 56px;
        left: auto;
        right: 8px;
        bottom: calc(var(--m-nav-h) + 8px);
        width: var(--m-lateral);
        height: auto;
        border-radius: 14px;
        border: 1px solid var(--borda);
    }

    #editor-principal,
    #palco-principal {
        top: 56px;
        left: 8px;
        right: calc(var(--m-lateral) + 16px);
        bottom: calc(var(--m-nav-h) + 8px);
    }

    /* Alternador e lados do modelo encostam à esquerda e quebram em duas
       linhas: ao centro ficavam por cima do próprio modelo, que é o que se
       quer ver. */
    #barra-vistas {
        top: 62px;
        left: 16px;
        right: auto;
        justify-content: flex-start;
        padding-top: 0;
        max-width: 230px;
    }

    #alternador-vista,
    #vistas-camara { justify-content: flex-start; }

    /* Telemóvel deitado: a barra continua a atravessar o ecrã, só sobe acima
       da navegação. A folha é fixa ao ecrã e não precisa de nada aqui — mas o
       ecrã é baixo, por isso ela pode usar mais altura. */
    #controlos-3d { bottom: calc(var(--m-nav-h) + 12px); }

    #folha-3d { max-height: 82vh; }

    /* uma peça por linha (pedido do utilizador): em duas colunas os nomes
       longos ficavam cortados ("Manga izqui…") e a amostra minúscula */
    #zonas {
        flex-direction: column;
        flex-wrap: nowrap;
        overflow-x: hidden;
        overflow-y: auto;
        gap: 6px;
    }

    .zona {
        width: 100%;
        min-width: 0;
        flex: 0 0 auto;
        flex-direction: row;
        align-items: center;
        text-align: left;
        padding: 7px 9px;
    }

    .zona .etiqueta { font-size: 12px; max-width: none; }

    /* Cantos de baixo do palco, como no retrato — a regra do retrato ancora-se
       em --m-painel-h (altura da folha), que aqui é a altura da COLUNA da
       direita e punha a barra a meio do ecrã. */
    .editor-ferramentas { bottom: calc(var(--m-nav-h) + 16px); left: 16px; }

    #bt-recolher-folha {
        bottom: calc(var(--m-nav-h) + 8px);
        left: auto;
        right: calc(var(--m-lateral) + 16px);
        transform: none;
        /* Aqui não está encostado à folha — volta a pílula completa. */
        border-bottom: 1px solid var(--borda);
        border-radius: 999px;
    }

    /* Recolher a folha em paisagem = fechar a coluna da direita. Sem isto o
       palco continuava com a faixa de 232px reservada e o botão só escondia
       a coluna, sem devolver o espaço a ninguém. */
    body.folha-recolhida { --m-lateral: 0px; }

    /* a tira baixa de "partes" não existe em paisagem: a coluna trata disso */
    body[data-secao-mobile="partes"] #painel { height: auto; }

    #painel-conteudo [data-secao="partes"] .panel-title { display: block; }

}

/* ------------------------------------------------------- menu de camada
   Menu folha → TW_MENU_* em editor2d.js (+ painel.js «+»).
   Fica disabled da fila hover da camada (máquina de estados da linha). */

/* MW: REMOVE (Tailwind cobre) */
.camada .camada-acoes button:disabled { opacity: .38; cursor: default; }
/* MW: REMOVE (Tailwind cobre) */
.camada .camada-acoes button:disabled:hover { background: transparent; color: var(--texto-fraco); }

/* MW: SPECIFIC */
@media (pointer: coarse) {
    /* alvos a sério: 14px de ícone com 22px de caixa não se acerta com o dedo */
    .camada-estado button { width: 34px; height: 34px; }

    .camada-estado svg { width: 17px; height: 17px; }

    .camada { padding: 4px 6px; }
}

/* ============================================ galeria de designs (galeria.php)

   Porta de entrada da rota: a lista do que já foi criado. Reaproveita os
   tokens e o cabeçalho do editor — só o miolo é próprio. */

/* Centro de conta (perfil.php, orders.php): identidade, dados, plano e
   segurança. Skin/layout migrados para utilities Tailwind no markup
   (2026-08-06); só fica em CSS o que usa color-mix() (estados de badge/
   alerta/sinal, sem token) — o resto das classes conta-* continua no
   markup como GANCHO para estes seletores compostos. */
/* MW: SPECIFIC */
.conta-badge.sucesso,
.conta-estado.ativo { border-color: color-mix(in srgb, #24a36a 50%, var(--borda)); background: color-mix(in srgb, #24a36a 14%, var(--bg-elevado)); color: #3fc486; }
/* MW: SPECIFIC */
.conta-badge.aviso { border-color: color-mix(in srgb, #e6a23c 50%, var(--borda)); color: #e6a23c; }
/* MW: SPECIFIC */
.conta-badge.perigo { border-color: color-mix(in srgb, var(--perigo) 50%, var(--borda)); color: var(--perigo); }

/* MW: SPECIFIC */
.conta-email-pendente {
    border-color: color-mix(in srgb, #e6a23c 45%, var(--borda));
    background: color-mix(in srgb, #e6a23c 9%, var(--bg-elevado));
}

/* MW: SPECIFIC */
.conta-alerta.sucesso { border-color: color-mix(in srgb, #24a36a 45%, var(--borda)); background: color-mix(in srgb, #24a36a 10%, var(--bg)); color: #3fc486; }
/* MW: SPECIFIC */
.conta-alerta.erro { border-color: color-mix(in srgb, var(--perigo) 45%, var(--borda)); background: color-mix(in srgb, var(--perigo) 9%, var(--bg)); color: var(--perigo); }

/* max-width base (1100px) vem de .galeria mais abaixo; aqui só o gap — a
   compound-selector (especificidade 2) precisa de ficar em CSS para continuar
   a vencer o `.galeria { gap: 16px }` do media query de 900px (fora deste
   bloco), que uma utility Tailwind (especificidade 1) perderia. */
/* MW: SPECIFIC */
.galeria.conta-pagina { gap: 18px; }

/* MW: SPECIFIC */
.conta-extrato-sinal { background: color-mix(in srgb, #24a36a 14%, var(--bg)); color: #3fc486; }
/* MW: SPECIFIC */
.conta-extrato-sinal.saida { background: color-mix(in srgb, var(--perigo) 11%, var(--bg)); color: var(--perigo); }

/* Galeria/loja: overflow/bg do body, setas, template-item e paginação em
   utilities (galeria.php + cartao_desenho.php). Fica: [hidden]/[aria-busy]
   (especificidade), pseudos, e .tpl-mini partilhado com TemplatesPreview. */
/* MW: REMOVE (Tailwind cobre) */
.galeria-seta[hidden] { display: none; }

/* MW: SPECIFIC */
.galeria-carrossel::-webkit-scrollbar { display: none; }

/* MW: REMOVE (Tailwind cobre) */
.tpl-mini {
    display: block;
    width: 100%;
    height: 100%;
}

/* MW: REMOVE (Tailwind cobre) */
.tpl-mini-svg {
    display: block;
    width: 100%;
    height: auto;
    filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .35));
}

/* MW: REMOVE (Tailwind cobre) */
.tpl-mini-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 42%;
    filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .35));
}

/* MW: SPECIFIC */
.template-preview--personalizado::after {
    content: '+';
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    font-size: 1.6rem;
    font-weight: 300;
    line-height: 1;
    color: var(--accent-texto);
    border: 2px solid var(--accent-fraco);
    border-radius: 50%;
}

/* MW: REMOVE (Tailwind cobre) */
.galeria-paginacao[hidden] { display: none; }
/* MW: REMOVE (Tailwind cobre) */
.galeria-paginacao[aria-busy="true"] { opacity: .6; pointer-events: none; }

/* Montra de produtos (/shop), /shop/<modelo> e /store/<slug> — skin migrado
   para utilities Tailwind em shop.php, shop-modelo.php e store.php
   (2026-08-08). Fica só aqui o que utilities não exprimem. */

/* Editor em modo simplificado (.modo-simples no <body>, index.php) — pedido
   do Wermerson 2026-08-06: quem chega via /shop/<modelo> vê uma pele mais
   limpa sobre o MESMO editor, sem tocar no motor JS partilhado com quem vem
   da galeria pessoal (risco zero para o editor completo). Só CSS: esconde o
   2D e secções avançadas, reordena o rail nas 4 categorias pedidas
   (Cores/Estampas/Imagem/Texto/Tecido), e mostra o botão de validar. */
/* MW: SPECIFIC */
.modo-simples #alternador-vista .alt-btns,
.modo-simples [data-secao="cores"],
/* "Partes" (grelha de peças → escolher uma → só depois a cor) é o MESMO
   fluxo de duas etapas que "cores" acima — supersedido pela lista
   #zonas-simples/cores-simples desde 2026-08-07. Ficou para trás quando essa
   lista saiu: nem é a secção inicial no shop mobile (painel.js já arranca em
   "cores"), só sobrava no rail sem fazer falta (chamado do Wermerson,
   2026-08-08). */
.modo-simples [data-secao="partes"],
.modo-simples [data-secao="camadas"],
/* Fluxo antigo de duas etapas (grelha de peças → só depois a cor, num
   painel à parte) — troca pela lista #zonas-simples abaixo, cor logo na
   linha. #painel-peca também aparece ao clicar na peça no 3D — sem isto
   ainda pipocava por cima da lista nova. */
.modo-simples #zonas,
.modo-simples #painel-peca,
/* O 💾 do topo grava o design e abre o modal de exportar/galeria — na Shop
   isso já é "Validar design" (pedido do Wermerson 2026-08-08): dois botões
   a gravar a mesma coisa. O editor completo mantém o 💾 tal como está. */
.modo-simples #bt-guardar {
    display: none !important;
}

/* Exceção: no desktop da Shop o #painel-peca vive ancorado no aside
   #shop-props (propriedades da estampa selecionada) — aí tem de aparecer,
   incluindo as secções interiores que o motor de secções não gere fora do
   #painel-conteudo. */
.modo-simples #shop-props #painel-peca,
.modo-simples #shop-props #painel-peca .secao-painel {
    display: block !important;
}

/* Na Store os controlos de imagem vivem na própria secção "Imagem"; o
   inspetor completo da direita é do editor avançado. */
.modo-simples #painel-camadas {
    display: none !important;
}

/* Secção "Cores" da página simplificada (botão + painel): só existe no
   shop. Fora do .modo-simples fica sempre escondida — o editor completo já
   tem o seu próprio "Cores" (data-secao="cores") — dentro do .modo-simples
   segue o motor genérico de secções (aplicarSecaoDesktop/aplicarSecaoMobile
   em painel.js), como qualquer outra. !important porque tem de vencer o
   `:not(.secao-ativa-desktop) { display:none }` genérico quando NÃO está em
   modo-simples, mesmo que a classe ativa lá esteja por engano. */
/* MW: SPECIFIC */
body:not(.modo-simples) [data-secao="cores-simples"] {
    display: none !important;
}

/* Cores simples (shop): skin em utilities (app.js). Xadrez da bola quando
   ainda não há cor inline. */
/* MW: SPECIFIC */
.zonas-simples-bola {
    background-image: conic-gradient(var(--xadrez) 90deg, transparent 90deg 180deg, var(--xadrez) 180deg 270deg, transparent 270deg);
    background-size: 8px 8px;
}

/* Sliders numéricos de posição/tamanho/rotação/opacidade do modal de texto
   (pedido do Wermerson 2026-08-07, referência TeamPulse) — exclusivos da
   Shop: no editor completo o painel Propriedades já cobre isto (arrasto no
   canvas + sliders próprios), duplicar seria redundante (decisão
   2026-08-08). O resto do fluxo guiado ("Usar para", limites de carateres,
   lista de itens) deixou de estar atrás desta classe — está sempre visível,
   ver index.php. !important pela mesma razão do bloco de "cores-simples"
   acima. */
/* MW: SPECIFIC */
body:not(.modo-simples) .so-loja {
    display: none !important;
}

/* MW: SPECIFIC */
.texto-item-loja {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 4px 4px 4px 10px;
    margin-bottom: 6px;
    background: var(--bg-elevado);
    border: 1px solid var(--borda);
    border-radius: 10px;
    transition: border-color 0.15s;
}
/* MW: SPECIFIC */
.texto-item-loja.ativo {
    border-color: var(--accent);
    background: var(--accent-fraco-2, color-mix(in srgb, var(--accent) 12%, transparent));
}
/* MW: SPECIFIC */
.texto-item-loja-area {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 4px 4px 0;
    background: transparent;
    border: 0;
    cursor: pointer;
    text-align: left;
    color: inherit;
    font: inherit;
    border-radius: 7px;
}
/* MW: SPECIFIC */
.texto-item-loja-area:hover {
    background: var(--fundo-fraco-2);
}

/* MW: REMOVE (Tailwind cobre) */
.texto-item-loja-bola {
    flex: none;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 1px solid var(--borda-campo);
}

/* MW: REMOVE (Tailwind cobre) */
.texto-item-loja-rotulo {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12.5px;
}

/* MW: REMOVE (Tailwind cobre) */
.texto-item-loja-acao {
    flex: none;
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    font-size: 13px;
    line-height: 1;
    color: var(--texto-fraco-2);
    background: transparent;
    border: 0;
    border-radius: 7px;
    cursor: pointer;
}

/* MW: REMOVE (Tailwind cobre) */
.texto-item-loja-acao:hover {
    color: var(--texto);
    background: var(--fundo-fraco-2);
}

/* MW: SPECIFIC */
.modo-simples #nav-secoes [data-secao="texturas"] { order: 2; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes #rail-add-imagem { order: 3; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes #rail-add-texto { order: 4; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes [data-secao="tecido"] { order: 5; }
/* "Cores" logo a seguir a "Tecido" (pedido do Wermerson, 2026-08-07) —
   antes vivia dentro de "Partes" (#zonas-simples). */
/* MW: SPECIFIC */
.modo-simples #nav-secoes [data-secao="cores-simples"] { order: 6; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes #rail-abrir-ia { order: 7; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes #rail-configuracoes { order: 8; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes #nav-adicionar { order: 9; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes #nav-encomenda { order: 10; }
/* MW: SPECIFIC */
.modo-simples #nav-secoes .rail-ajuda { order: 11; }

/* MW: REMOVE (Tailwind cobre) */
.bt-validar-design { display: none; }

/* Skin (cor/borda/tipografia/sombra) migrado para utilities Tailwind no
   próprio botão (index.php) — aqui fica só o posicionamento fixo, que
   depende do .modo-simples. Em desktop encosta à direita (right/largura das
   colunas laterais); em mobile centra horizontalmente sobre o #nav-secoes. */
/* MW: SPECIFIC */
.modo-simples .bt-validar-design {
    display: flex;
    position: fixed;
    right: var(--margem);
    width: var(--camadas-largura);
    bottom: max(20px, env(safe-area-inset-bottom, 0px));
    z-index: 20;
}

/* No telemóvel a barra inferior (#nav-secoes) já vive em baixo — o botão de
   validar sobe-se para não empilhar em cima dela, e centra-se horizontalmente
   para ficar alinhado com a faixa de navegação (full-width).
   bottom em max(), não fixo (2026-08-08): a folha de baixo muda de altura
   consoante a secção ("Cores" é bem mais alta que "Tecido") — um valor fixo
   ficava bem numa secção baixa e o botão caía a meio da folha noutra mais
   alta, tapando o conteúdo (ex.: "Cor base"). O max() mantém a posição de
   sempre quando a folha é baixa/recolhida, e só sobe mais quando a folha
   precisar de mais espaço — nunca cai abaixo do topo dela.
   +38px e não +14px (2026-08-08, 2.ª ronda): #bt-recolher-folha vive a
   nav-h+painel-h+4px e tem 24px de altura, ou seja o TOPO dele já está a
   nav-h+painel-h+28px. Com +14px o botão ficava por baixo desse topo — no
   1.º carregamento (--m-painel-h ainda no valor de CSS, 30vh, antes de
   painel.js medir a folha a sério) isso empurrava o botão para cima da
   pastilha, escondendo-a a meio por trás dele (z-index do botão é maior).
   +38px garante sempre pelo menos 10px de folga acima do topo da pastilha,
   em qualquer combinação de nav-h/painel-h. */
/* MW: SPECIFIC */
@media (max-width: 900px) {
    .modo-simples .bt-validar-design {
        right: auto;
        left: 50%;
        transform: translateX(-50%);
        width: auto;
        bottom: max(98px, calc(var(--m-nav-h) + var(--m-painel-h) + 38px));
    }

    /* Em modo simples o painel está escondido (--m-painel-h ≈ 0), por isso o
       #palco-principal termina a ~62px do fundo. Os controlos 3D ficam a
       bottom:10px do palco = ~72px do ecrã, colando ao botão de validar
       (bottom:84px, ~50px altura). Este override separa-os em duas faixas
       distintas: controlos em cima, validar por baixo. */
    .modo-simples #controlos-3d {
        bottom: 82px;
    }

    /* Havendo loja (pedido do Wermerson, 2026-08-08): "Validar design" e a
       aba "Order" (#nav-encomenda) passaram a levar ao MESMO modal — dois
       botões para o mesmo sítio. No telemóvel esconde-se "Validar design" e
       fica só a aba, mais simples. .tem-encomenda-mobile é posta no body
       por iniciarEncomendaSimples() (app.js) só quando window.MVP_LOJA
       existe — sem loja o botão continua a ser a única forma de gravar o
       design pessoal, não pode desaparecer. */
    body.tem-encomenda-mobile .bt-validar-design {
        display: none;
    }
}

/* Mesma sobreposição em paisagem (landscape): controlos legados a
   calc(var(--m-nav-h)+12px)=58px do palco vs validar a 84px do ecrã. */
/* MW: SPECIFIC */
@media (max-width: 900px) and (orientation: landscape) {
    .modo-simples #controlos-3d {
        bottom: 88px;
    }
}

/* Encomenda (vive só no #modal-encomenda desde 2026-08-10) — skin em
   utilities (order.php / app.js). Aqui fica só tirar as setas nativas do
   <input type=number>. */
/* MW: REMOVE (Tailwind cobre) */
.encomenda-qtd-caixa input::-webkit-outer-spin-button,
.encomenda-qtd-caixa input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* MW: REMOVE (Tailwind cobre) */
.encomenda-qtd-caixa input { -moz-appearance: textfield; }

/* Botões do modo iguais/personalizadas: padding reduzido para caber no
   painel estreito (260px) sem os textos ficarem cortados. */
/* MW: REMOVE (Tailwind cobre) */
#encomenda-modo .alt-btn {
    padding: 5px 6px;
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Linha de encomenda — validação visual de tamanho em falta */
/* MW: REMOVE (Tailwind cobre) */
.encomenda-linha.sem-tamanho > select:first-of-type {
    border-color: var(--erro, #e53e3e);
}

/* Loja de assinante (/store/<slug>) e /account/orders */

/* .desenho-* (cartão de design, /account/orders e galeria): skin migrado
   para utilities Tailwind em partials/cartao_desenho.php (2026-08-08).
   .desenho-cartao e .desenho-nome ficam como hooks de JS (galeria.php),
   sem estilo próprio aqui. */

/* modal de criar (#modal-criar) e diálogo de renomear/apagar (#modal-dialogo)
   — skin migrado para utilities Tailwind em galeria.php (2026-08-08). Fica
   só o estado que o JS liga por classe (abrirDialogo() em galeria.php
   alterna .perigo no botão OK — apagar usa vermelho em vez do accent).
   --perigo-forte e não --perigo: o vermelho claro com texto branco por cima
   fica em 2,5:1, abaixo do mínimo legível. */
/* MW: SPECIFIC */
#bt-dialogo-ok.perigo {
    background: var(--perigo-forte);
    border-color: var(--perigo-forte);
    color: #fff;
}

/* MW: SPECIFIC */
@media (max-width: 900px) {
    .galeria { padding: 64px 12px 28px; gap: 16px; }

    .galeria-hero { padding: 22px 16px; border-radius: 16px; }

    .galeria-hero-acoes { flex-direction: column; }

    .galeria-hero-acoes .botao-criar,
    .galeria-hero-acoes .botao-criar-ia { width: 100%; }

    /* No telemóvel as secções passam para o lado em vez de empilharem ecrãs
       e ecrãs de cartões (pedido 2026-08-02): mesma mecânica do carrossel
       «Continuar onde ficaste» — deslizar com o dedo, ou as setas.
       Exceto a grelha PAGINADA de "My designs" (2026-08-06): essa já limita
       quantos cartões aparecem de uma vez (12/24/48 com paginação), por isso
       fica em grelha vertical de 2 colunas em vez de faixa horizontal — pedido
       explícito do Wermerson para o telemóvel. */
    .galeria-grelha:not(.galeria-grelha--paginada),
    .template-grelha {
        display: flex;
        gap: 10px;
        overflow-x: auto;
        padding-bottom: 4px;
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    .galeria-grelha:not(.galeria-grelha--paginada)::-webkit-scrollbar,
    .template-grelha::-webkit-scrollbar { display: none; }

    .galeria-grelha:not(.galeria-grelha--paginada) > * { flex: 0 0 min(190px, 62vw); scroll-snap-align: start; }
    .template-grelha > * { flex: 0 0 min(150px, 46vw); scroll-snap-align: start; }

    .galeria-grelha--paginada { gap: 10px; }

    .galeria-secao-cabecalho:has(.galeria-por-pagina) { flex-wrap: wrap; }

}
