/* seleção de texto (arrastar o mouse em cima de um texto) usa o azul do
   site em vez do azul padrão do sistema operacional/navegador. */
::selection {
  background: var(--accent-2);
  color: var(--accent-text);
}

/* some texto pra tela mas continua no DOM/acessibility tree — usado pro
   <h1> da página (o site em si é só logo/ícone, sem título visível). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

:root {
  /* paleta espelhada no kenjilambert.com: preto quase puro, creme como
     "texto principal" (não branco puro) e os dois acentos que se revezam
     lá (vermelho pros CTAs/estado ativo, azul como acento secundário). */
  --bg: #0a0a0a;
  --panel: #141414;
  --border: rgba(241, 238, 192, 0.16);
  --text: #f1eec0;
  --text-muted: rgba(241, 238, 192, 0.62);
  --accent: #ea4530;
  --accent-2: #3aa1d8;
  --accent-text: #f1eec0;
  --track-off: rgba(241, 238, 192, 0.14);
  /* 0.4 media falhava contraste (WCAG AA) em texto pequeno — subido pra
     0.55, ainda claramente "mais apagado" que --text-muted mas legível. */
  --text-faint: rgba(241, 238, 192, 0.55);
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  /* curva "com mola" (aproximação de spring physics via cubic-bezier — CSS
     não tem um motor de física de verdade) inspirada no Material 3
     Expressive: troca curvas de duração+ease neutras por um pequeno
     "overshoot" (passa um pouco do alvo e volta), pra estados de seleção
     (aba ativa, switch, indicador de nav) parecerem mais vivos/elásticos
     em vez de mecânicos. Usada só em transições de UI curtas — nada no
     preview/geração do padrão em si. */
  --ease-expressive: cubic-bezier(0.34, 1.56, 0.64, 1);
  font-family: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  /* troca de fonte (fallback -> Space Grotesk carregada) sem re-layout —
     ajusta o tamanho do fallback pra casar a métrica, evita CLS. */
  font-size-adjust: from-font;
  /* site é dark-only, não adapta a light mode — isso avisa o browser pra
     colorir scrollbar/inputs nativos combinando, em vez de usar o padrão
     claro do SO. */
  color-scheme: dark;
}

/* entrada de painel/galeria ao trocar de aba (categoria de ajuste, ou
   Resultado/Variações/Histórico) — "sobe" de leve em vez de só aparecer
   estático, dá a mesma resposta de um bottom sheet abrindo. */
@keyframes gi-section-enter {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

* {
  box-sizing: border-box;
}

/* o app depende de vários painéis com scroll interno próprio (barra
   lateral, stage, galeria, dropdowns) — sem isso, cada um deles rola com a
   barra de scroll padrão do sistema (cinza/branca, grossa), que destoa
   completamente do tema escuro. */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(241, 238, 192, 0.25) transparent;
}

*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background-color: rgba(241, 238, 192, 0.25);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}

*::-webkit-scrollbar-thumb:hover {
  background-color: var(--accent-2);
}

/* tira as setinhas de cima/baixo dos cantos da barra de rolagem (Chrome
   no Windows costuma desenhar por padrão) — só a barra em si, sem elas.
   Os seletores :start/:end/:increment/:decrement são mais específicos
   que ::-webkit-scrollbar-button sozinho e o navegador aplica um estilo
   próprio neles por padrão — sem repetir display:none em cada um, essa
   regra geral sozinha não bastava pra apagar as setinhas em todo canto. */
*::-webkit-scrollbar-button,
*::-webkit-scrollbar-button:start,
*::-webkit-scrollbar-button:end,
*::-webkit-scrollbar-button:vertical:start:decrement,
*::-webkit-scrollbar-button:vertical:end:increment,
*::-webkit-scrollbar-button:horizontal:start:decrement,
*::-webkit-scrollbar-button:horizontal:end:increment,
*::-webkit-scrollbar-button:single-button,
*::-webkit-scrollbar-button:single-button:vertical:decrement,
*::-webkit-scrollbar-button:single-button:vertical:increment,
*::-webkit-scrollbar-button:single-button:horizontal:decrement,
*::-webkit-scrollbar-button:single-button:horizontal:increment {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
  background: transparent !important;
  -webkit-appearance: none !important;
}

button,
input,
select,
textarea {
  font-family: inherit;
}

/* sem isso, todo botão/input/label usa o anel de foco padrão do navegador
   — um azul que não é o --accent-2 do site, e que aparecia até depois de
   um clique de mouse (não só navegação por teclado). :focus-visible only
   mantém a acessibilidade (teclado) usando a cor certa da paleta, e tira o
   anel de clique de mouse, que só "sujava" a UI com um azul fora do tema. */
button:focus,
input:focus,
select:focus,
textarea:focus,
[tabindex]:focus {
  outline: none;
}

button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.app-header {
  /* mesmo preto da página inteira — sem essa distinção de tom que existia
     antes entre o topo e o resto. position:relative é o que permite a
     pílula de navegação (.brand) flutuar centralizada de verdade (ver
     abaixo), independente da largura do seletor de idioma do outro lado. */
  position: relative;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  padding: 16px 28px;
  background: var(--bg);
  min-height: 64px;
}

/* nav em pílula flutuante, inspirada no componente "PillNav" do React
   Bits — só que reimplementada aqui em CSS/JS puro (ver
   ui/module-switcher.js): esse projeto é estático, sem build step nem
   dependência nenhuma, então em vez de trazer React+GSAP pro site inteiro
   só por causa de 1 componente de navegação, a MESMA ideia visual (pílula
   com um círculo que sobe de baixo ao ativar/passar o mouse) foi recriada
   com CSS puro, dentro da paleta do site. */
.brand {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 4px;
}

.app-logo {
  /* +15% (era 40px) — o menu do topo inteiro estava pequeno demais. */
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--panel);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.app-logo svg {
  display: block;
  height: 28px;
  width: 28px;
}

.lang-switcher {
  /* fica de fora da pílula centralizada, sempre no canto — .brand não
     ocupa mais espaço no fluxo normal do header (position:absolute), esse
     margin-left:auto é o que continua empurrando o seletor de idioma pro
     canto direito sozinho. */
  margin-left: auto;
  position: relative;
  z-index: 1;
  display: flex;
  gap: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 2px;
}

.lang-button {
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  transform: scale(1);
  transition: background-color 0.2s ease, color 0.2s ease, transform 0.15s var(--ease-expressive);
}

.lang-button.active {
  background: var(--accent-2);
  color: var(--accent-text);
}

#app {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.module-tabs {
  /* a "pílula" de fundo da nav — os botões ficam soltos dentro dela, cada
     um vira sua PRÓPRIA pílula por cima ao ativar/hover (ver
     .pill-hover-circle abaixo), igual ao PillNav do React Bits. */
  flex-shrink: 0;
  display: flex;
  align-items: center;
  /* +15% (era 40px) */
  height: 46px;
  gap: 3px;
  padding: 3px;
  background: var(--panel);
  border-radius: var(--radius-pill);
}

.module-tabs button,
.module-tab-disabled {
  /* cada aba é a própria pílula: círculo colorido escondido atrás do
     texto, que "sobe" preenchendo o botão inteiro ao ativar/passar o
     mouse (isolation:isolate + z-index no texto garantem que ele sempre
     fique por cima do círculo, mesmo depois que ele cobre o botão). */
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  border: none;
  margin: 0;
  background: transparent;
  /* +15% (era 0 18px / clamp(13px,1.1vw,14px)) */
  padding: 0 21px;
  border-radius: var(--radius-pill);
  font-size: clamp(15px, 1.27vw, 16px);
  font-family: inherit;
  font-weight: 700;
  white-space: nowrap;
  text-align: center;
  color: var(--text);
  cursor: pointer;
}

.pill-hover-circle {
  position: absolute;
  left: 50%;
  bottom: -30%;
  width: 220%;
  aspect-ratio: 1;
  border-radius: 50%;
  /* vermelho por padrão (Azulejo) — Mosaico troca pra azul, ver
     [data-id='mosaic'] abaixo, mesmo revezamento de cor que o resto do
     app já usa pra diferenciar os 2 módulos. */
  background: var(--accent);
  transform: translateX(-50%) scale(0);
  transform-origin: center bottom;
  transition: transform 0.4s var(--ease-expressive);
  z-index: 0;
  pointer-events: none;
}

.module-tabs button[data-id='mosaic'] .pill-hover-circle {
  background: var(--accent-2);
}

.pill-label {
  position: relative;
  z-index: 1;
  transition: color 0.2s ease;
}

.module-tabs button:hover .pill-hover-circle,
.module-tabs button.active .pill-hover-circle {
  transform: translateX(-50%) scale(1);
}

.module-tabs button:hover .pill-label,
.module-tabs button.active .pill-label {
  color: var(--accent-text);
}

.module-tab-disabled {
  color: var(--text-faint);
  cursor: not-allowed;
}

.module-content {
  flex: 1;
  min-height: 0;
  padding: 28px;
  display: flex;
  flex-direction: column;
}

/* grid-icons module */

.gi-layout {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 32px;
  align-items: stretch;
}

/* coluna da sidebar — só o painel de ajustes mesmo (.gi-controls). Já foi
   uma barra de formato própria aqui em cima, fora do painel, tentando
   ficar alinhada com o preview do lado (nunca batia certo em toda largura
   de tela) — "Formato" agora é só a primeira seção do painel, igual
   Tema/Grade/etc. */
.gi-sidebar-column {
  width: 320px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
}

.gi-controls {
  width: 100%;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 26px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  overflow-y: auto;
  overflow-x: hidden;
  /* rolagem suave (o scrollTo({behavior:'smooth'}) que leva de volta pro
     topo ao trocar de categoria já pede isso, mas vale de qualquer forma
     pra qualquer outro scroll programático) + inércia nativa no touch. */
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
}

.gi-stage {
  /* moldura igual à do Mosaico (.mo-stage) — mesmo painel/borda/raio, pros
     dois módulos ficarem visualmente consistentes (antes o Azulejo ficava
     "solto", sem caixa nenhuma em volta do preview+botões).
     Coluna sempre (conteúdo, depois a trilha Resultado/Variações/
     Histórico + Editar embaixo) — era linha (trilha ao LADO do preview)
     só no desktop, mas essa trilha lateral competia por largura e sobrava
     espaço vazio embaixo em janelas mais estreitas; embaixo ela vira uma
     barra horizontal cheia, igual já era no mobile. */
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  overflow: hidden;
}

.gi-stage-content {
  /* NUNCA rola inteiro — o preview e os botões ficam sempre parados na
     tela; só a galeria (Variações/Histórico, ver .gi-gallery) tem scroll
     próprio, porque aquilo sim é uma lista que pode crescer. Só um dos 2
     filhos (.gi-result-block ou .gi-gallery) fica visível por vez — ver
     regras :has() mais abaixo.
     flex:1 esticava isso pra preencher a altura toda do .gi-stage (que por
     sua vez estica pra bater com a sidebar, bem mais alta quando tem muita
     seção aberta) — sobrava um vão gigante DEPOIS do preview e ANTES da
     trilha Resultado/Variações/Histórico (que é irmã daqui, vem logo
     abaixo), porque essa trilha só se move junto com o fim REAL do
     conteúdo, não com a altura esticada. Sem flex:1, o bloco fica do
     tamanho do próprio conteúdo — a trilha cola direto embaixo do preview,
     acompanhando o redimensionamento dele; qualquer sobra de altura (de
     uma sidebar mais alta) fica como espaço vazio no fim do .gi-stage
     inteiro, não espremida entre o preview e o menu. */
  flex: 0 0 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  overflow: hidden;
}

.gi-result-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  width: 100%;
  min-height: 0;
}

.gi-preview-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-width: 0;
  justify-content: center;
}

/* trilha de ícones Novo azulejo/Criar variações/Estou com sorte — só
   existe (visualmente) no mobile, ao lado do preview (ver media query
   mais abaixo). No desktop fica display:none; quem aparece lá é a pílula
   com texto de sempre (.gi-stage-toolbar), sem mudança nenhuma. */
.gi-stage-toolbar-rail {
  display: none;
}

.gi-toolbar-rail-button {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text-muted);
  cursor: pointer;
  transform: scale(1);
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease,
    transform 0.15s var(--ease-expressive);
}

.gi-toolbar-rail-button:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.gi-toolbar-rail-button:active {
  transform: scale(0.9);
}

.gi-toolbar-rail-button-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}

/* trilha Editar + abas Resultado/Variações/Histórico juntos numa linha só,
   ACIMA do preview (a pílula de ação — Novo azulejo/Criar variações/
   Estou com sorte — fica abaixo dele, ver .gi-stage-toolbar). Editar
   sempre o primeiro (mais à esquerda), ver ordem de appendChild em
   index.js. Só um dos 2 blocos da .gi-stage-content aparece por vez,
   escolhido por qual aba está marcada — ver as regras :has() logo abaixo,
   que usam o id de cada input direto (funcionam não importa onde a
   trilha esteja no DOM/CSS). */
.gi-stage-tabs-row {
  /* width:100% (do .gi-stage-content inteiro) esticava mais que o
     próprio preview — encostava nas bordas da moldura em vez de
     acompanhar a largura real do preview logo abaixo, como a pílula de
     botões (.gi-stage-toolbar) já faz. */
  flex-shrink: 0;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: var(--gi-preview-w, 540px);
  /* .gi-stage é align-items:stretch (pro .gi-stage-content esticar cheio
     e centralizar o preview por dentro) — mas essa trilha é IRMÃ dele,
     fora desse contexto, e como tem largura própria definida (max-width
     acima), o stretch não centraliza sozinho: sem isso ela colava na
     ESQUERDA (fallback de align-items:stretch pra um item com tamanho
     definido é flex-start, não center). */
  align-self: center;
}

.gi-stage-tabs {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: row;
  gap: 4px;
  padding: 3px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg);
}

.gi-stage-tabs-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* o input em si é invisível (1px, ver acima) — sem isso, navegar por
   teclado até essas abas não mostrava nenhum indício visual de qual
   delas está com foco (o anel de foco ficava preso num elemento
   invisível). Aqui o anel aparece no RÓTULO (o que a pessoa vê) quando o
   input escondido correspondente ganha foco via teclado. */
.gi-stage-tabs-input:focus-visible + .gi-stage-tabs-label {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}

.gi-stage-tabs-label {
  /* sem borda própria — o container (.gi-stage-tabs) já é a pílula com
     fundo/borda; o rótulo é só o "miolo" que troca de cor quando marcado.
     Mesmo clamp de fonte que .gi-stage-toolbar .control-button (a pílula
     de ação logo abaixo) — as duas trilhas de pílula precisam crescer
     junto, do mesmo jeito, senão uma fica visivelmente maior que a outra
     dependendo da largura da tela. */
  flex: 1;
  text-align: center;
  white-space: nowrap;
  padding: 8px 6px;
  border-radius: var(--radius-pill);
  font-size: clamp(10px, 1.1vw, 12px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  cursor: pointer;
  transform: scale(1);
  transition: background-color 0.25s var(--ease-expressive), color 0.2s ease,
    transform 0.25s var(--ease-expressive);
}

.gi-stage-tabs-input:checked + .gi-stage-tabs-label {
  background: var(--accent);
  color: var(--accent-text);
  /* mesma "molinha" do indicador das categorias — a pílula ativa dá uma
     leve estufada em vez de só trocar de cor. */
  transform: scale(1.04);
}

/* só um "conteúdo" da área principal aparece por vez — Resultado (preview +
   toggle + botões) por padrão; Variações/Histórico mostram a galeria no
   lugar dele. */
.gi-gallery {
  display: none;
}

.gi-stage:has(#gi-stage-tab-variations:checked) .gi-gallery,
.gi-stage:has(#gi-stage-tab-history:checked) .gi-gallery {
  display: flex;
  animation: gi-section-enter 0.32s var(--ease-expressive);
}

.gi-stage:has(#gi-stage-tab-variations:checked) .gi-result-block,
.gi-stage:has(#gi-stage-tab-history:checked) .gi-result-block {
  display: none;
}

.gi-stage:has(#gi-stage-tab-variations:checked) .gi-gallery-block--history,
.gi-stage:has(#gi-stage-tab-history:checked) .gi-gallery-block--variations {
  display: none;
}

/* o cabeçalho colapsável (▾ VARIAÇÕES / ▾ HISTÓRICO) fazia sentido quando os
   2 blocos dividiam o mesmo espaço — agora a aba já escolhe qual aparece,
   então o chevron interno só duplicaria o nome da aba. */
.gi-gallery-header {
  display: none;
}

.gi-result-title {
  /* "RESULTADO" tirado — o preview já fala por si, sem precisar de rótulo
     em cima dele. */
  display: none;
}

.gi-stage-toolbar {
  flex-shrink: 0;
  width: 100%;
  /* acompanha a largura ATUAL do preview (--gi-preview-w, setada via JS em
     updatePreviewFrame — grid-icons/index.js), não um teto fixo de 540px:
     quando o preview encolhe (janela baixa, formato retrato/story...) a
     pílula de botões encolhe junto, em vez de sobrar enorme e
     desproporcional do lado de um preview pequeno. */
  max-width: var(--gi-preview-w, 540px);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

.gi-stage-toolbar .control-button {
  flex: 1;
  /* MESMO clamp de fonte que .gi-stage-tabs-label (a trilha Resultado/
     Variações/Histórico logo em cima) — tinha um clamp bem maior aqui
     antes (11-14px contra 11px fixo lá), então essa pílula sempre
     aparecia visivelmente maior que a de cima em qualquer largura de
     tela; agora as duas crescem junto, do mesmo jeito. */
  font-size: clamp(10px, 1.1vw, 12px);
  padding: 8px 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  /* pílula cheia (raio total), igual a trilha de cima — o variant
     "default" (Criar variações/Estou com sorte) usava --radius-md
     (cantos só arredondados) por padrão, ficando com formato diferente
     do "Novo azulejo" (variant primary, que já ganha --radius-pill). */
  border-radius: var(--radius-pill);
}

/* botão "Novo azulejo" (regenerar) — o único da trilha que fica AZUL (não
   vermelho como os outros botões "primary" do site, ex.: Exportar SVG/PNG)
   pra se destacar como a ação principal desse trio específico, sem herdar
   a cor de destaque padrão usada em todo canto. Mesma troca no ícone do
   mobile (.gi-toolbar-rail-button-primary), que é a mesma ação. */
.gi-stage-toolbar .control-button-primary,
.gi-toolbar-rail-button-primary {
  background: var(--accent-2);
  border-color: var(--accent-2);
}

.gi-preview-wrap {
  /* o tamanho (width/height, já calculado pra nunca estourar a tela) e o
     fundo são ajustados via JS (updatePreviewFrame + computePreviewBoxSize,
     em grid-icons/index.js) toda vez que o "Formato de exportação" muda ou
     a janela redimensiona — aqui só a transição suave e o container que
     centraliza o preview dentro da moldura escolhida. */
  flex-shrink: 0;
  /* position:relative é o que faltava: sem isso, o overlay de edição
     (.gi-grid-editor, position:absolute; inset:0) não tinha um ancestral
     posicionado aqui perto e ia se ancorar em qualquer um mais acima na
     árvore — cobrindo a tela inteira em vez de só a arte do preview. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* sem cantos arredondados aqui — mesmo um raio pequeno recorta uma
     lasca do próprio padrão bem na borda (formas que a gente quer
     ver inteiras até a beirada). */
  transition: width 0.35s ease, height 0.35s ease, background-color 0.35s ease;
}

.gi-preview {
  overflow: hidden;
  background: repeating-conic-gradient(rgba(255, 255, 255, 0.07) 0% 25%, rgba(255, 255, 255, 0.02) 0% 50%) 0 0 / 20px 20px;
  transition: width 0.35s ease, height 0.35s ease;
}

.gi-preview svg {
  width: 100%;
  max-width: 540px;
  height: auto;
  display: block;
  margin: 0 auto;
}

/* botão "Editar" — física de botão de verdade, não switch nem pílula (as
   abas Resultado/Variações/Histórico do lado já são pílulas): desligado
   parece levantado (sombra por baixo dá volume), ligado "afunda" (sombra
   some, desloca 1px pra baixo) — o oposto de simplesmente trocar de cor. */
.press-button {
  flex-shrink: 0;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--panel);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 2px 0 0 var(--border);
  transform: translateY(0);
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease,
    box-shadow 0.1s ease, transform 0.1s ease;
}

.press-button:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.press-button.pressed {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
  box-shadow: none;
  transform: translateY(2px);
}

.gi-grid-editor {
  /* top/left/width/height vêm sempre de updateGridEditorOverlayGeometry()
     (grid-icons/index.js) — o overlay cobre só UM ladrilho do ícone-base
     (sempre quadrado), nunca o retângulo inteiro quando o formato de
     exportação repete o ícone (ver comentário lá). */
  position: absolute;
  display: grid;
  cursor: grab;
}

.gi-grid-editor-cell {
  border: 1px dashed rgba(234, 69, 48, 0.4);
  touch-action: none;
}

.gi-grid-editor-cell:hover {
  background: rgba(58, 161, 216, 0.16);
}

.gi-grid-drag-ghost {
  position: fixed;
  z-index: 1000;
  transform: translate(-50%, -50%);
  pointer-events: none;
  opacity: 0.85;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.35));
}

.gi-grid-drag-ghost svg {
  width: 100%;
  height: 100%;
  display: block;
}

.gi-color-menu {
  position: fixed;
  z-index: 1001;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 210px;
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transform: translate(-50%, 10px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}

.gi-color-menu-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.gi-color-menu-divider {
  height: 1px;
  background: var(--border);
  opacity: 0.4;
}

.gi-color-menu-swatch {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
}

.gi-color-menu-swatch:hover {
  outline: 2px solid var(--accent-2);
  outline-offset: 1px;
}

.gi-color-menu-shape {
  width: 24px;
  height: 24px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.gi-color-menu-shape:hover {
  outline: 2px solid var(--accent-2);
  outline-offset: 1px;
}

.gi-color-menu-shape svg {
  width: 100%;
  height: 100%;
}

.gi-gallery {
  width: 100%;
  /* mesma largura do preview/toolbar (ver --gi-preview-w, setada via JS em
     updatePreviewFrame) — sem isso a galeria esticava pra largura inteira
     do stage (ou ficava presa num teto fixo de 540px, menor que um
     preview que cresceu além disso numa janela larga) e os cartões/
     cabeçalhos ficavam desalinhados do preview, que fica centralizado. */
  max-width: var(--gi-preview-w, 540px);
  /* única parte do .gi-stage que rola — preenche o espaço que sobra
     abaixo do preview/botões (flex:1) e nunca deixa o .gi-stage inteiro
     rolar (min-height:0 é o que permite ela encolher e ativar o próprio
     scroll em vez de estourar o pai). O teto de 42vh evita que ela tome
     conta da tela inteira quando tem espaço de sobra. */
  flex: 1 1 auto;
  min-height: 0;
  max-height: 42vh;
  overflow-y: auto;
  /* display fica só na regra de cima (default none, flex quando a aba
     Variações/Histórico está marcada) — repetir "flex" aqui destravava a
     galeria e ela aparecia junto com o Resultado o tempo todo. */
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.gi-gallery-block {
  /* header + grid de miniaturas de cada bloco (Variações, Histórico) —
     mesmo gap que .gi-gallery já usava entre os 4 filhos soltos, só que
     agrupados de dois em dois agora (ver comentário no index.js). */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  width: 100%;
}

.gi-gallery-header {
  align-self: flex-start;
}

.gi-gallery-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
}

.gi-gallery-chevron {
  font-size: 14px;
  color: var(--text-faint);
  transition: transform 0.15s ease;
}

.gi-gallery-toggle.collapsed .gi-gallery-chevron {
  transform: rotate(-90deg);
}

.gi-section-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.gi-variations,
.gi-history {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  width: 100%;
  flex-shrink: 0;
}

.gi-variation-thumb,
.gi-history-thumb {
  width: 96px;
  height: 96px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: repeating-conic-gradient(rgba(255, 255, 255, 0.07) 0% 25%, rgba(255, 255, 255, 0.02) 0% 50%) 0 0 / 12px 12px;
  cursor: pointer;
  overflow: hidden;
}

.gi-history-thumb {
  width: 72px;
  height: 72px;
}

.gi-variation-thumb:hover,
.gi-history-thumb:hover {
  border-color: var(--accent-2);
}

.gi-variation-thumb svg,
.gi-history-thumb svg {
  width: 100%;
  height: 100%;
  display: block;
}

.gi-actions {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.frame-picker-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.frame-picker-tile {
  flex: 1;
  min-width: 50px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  padding: 6px 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text-muted);
  cursor: pointer;
  height: 48px;
  transform: scale(1);
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease, transform 0.15s var(--ease-expressive);
}

.frame-picker-tile:hover {
  border-color: var(--accent-2);
}

.frame-picker-tile.active {
  /* mesmo estilo da opção marcada no seletor de idioma (.lang-button.active)
     — fundo azul cheio, não um vermelho fraco. */
  border-color: var(--accent-2);
  color: var(--accent-text);
  background: var(--accent-2);
}

.frame-picker-shape {
  flex-shrink: 0;
  border: 1.5px solid currentColor;
  border-radius: 2px;
}

.frame-picker-caption {
  font-size: 10px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* shared controls */

.control-section {
  /* sem o traço aqui a lista de seções ficava confusa (onde uma acaba e a
     outra começa) — volta o divisor, só que bem mais discreto que o
     original (opacidade baixa em vez da cor cheia do tema). */
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  padding-top: 28px;
  border-top: 1px solid var(--border);
}

.control-section:first-of-type {
  padding-top: 0;
  border-top: none;
}

.control-section-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.control-section-toggle {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
}

.control-section-chevron {
  flex-shrink: 0;
  font-size: 16px;
  line-height: 1;
  color: var(--text-faint);
  transition: transform 0.15s ease;
}

.control-section.collapsed .control-section-chevron {
  transform: rotate(-90deg);
}

.control-section-randomize {
  /* ícone solto, sem caixinha com borda — menos "formulário", mais leve.
     min-width/height (não width/height) pra alvo de toque ficar >=24px
     (WCAG 2.5.8) sem crescer o ícone visual em si. */
  flex-shrink: 0;
  min-width: 24px;
  min-height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: none;
  color: var(--text-muted);
  font-size: 14px;
  cursor: pointer;
}

.control-section-randomize:hover {
  color: var(--accent-2);
}

/* colapsa/expande com animação de verdade (grid-template-rows 1fr↔0fr —
   truque que anima "altura automática" sem JS medindo pixel nenhum) em
   vez de só aparecer/sumir com display:none instantâneo. O conteúdo de
   verdade mora em .control-section-body-inner (overflow:hidden, ver
   abaixo) — precisa desse wrapper porque o grid só some por completo
   quando tem UMA linha só encolhendo, não vários filhos soltos. */
.control-section-body {
  display: grid;
  grid-template-rows: 1fr;
  margin-top: 20px;
  transition: grid-template-rows 0.35s var(--ease-expressive), margin-top 0.35s var(--ease-expressive),
    opacity 0.25s ease;
}

.control-section-body-inner {
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-height: 0;
  overflow: hidden;
}

.control-section.collapsed .control-section-body {
  grid-template-rows: 0fr;
  margin-top: 0;
  opacity: 0;
}

.control-section-title {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-faint);
  letter-spacing: 0.02em;
}

.control {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.control-label {
  /* rótulo curto (ex. "Contorno") ainda pode carregar um pouco de peso,
     mas texto de explicação mais longo (ex. a legenda do controle fino de
     blocos) usa a mesma classe — 500 em vez de 600 deixa isso passável nos
     dois casos, sem parecer um textão em negrito. */
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
}

.control-hint {
  font-size: 11px;
  color: var(--text-muted);
}

.reference-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  max-width: 100%;
}

.reference-thumb {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  object-fit: cover;
}

.slider-row {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* --value-proximity (0 no mínimo → 1 no máximo, setado via JS em
   updateValueLabel — ui/controls/slider.js) esquenta o fundo pra
   vermelho conforme o valor se aproxima do teto — reforço visual de
   "quase no máximo" sem precisar ler o número. Número sempre em negrito
   e branco/creme (nunca preto) — só o fundo muda. */
.slider-value-box {
  --value-proximity: 0;
  flex-shrink: 0;
  min-width: 52px;
  text-align: center;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  background: color-mix(in srgb, var(--panel) calc((1 - var(--value-proximity)) * 100%), var(--accent));
  transition: background-color 0.15s ease;
}

/* slider 100% customizado (era <input type="range"> nativo) — nenhum
   pedaço dele é desenhado pelo sistema operacional agora, então o cursor
   customizado do site (ver ui/customCursor.js) nunca mais perde o lugar
   pra mãozinha nativa, problema que persistia mesmo com cursor:none nos
   pseudo-elementos do range nativo (parte do controle nativo é fora do
   alcance do CSS em alguns navegadores). Ver ui/controls/slider.js pra a
   mecânica de arraste/teclado. */
.slider-track-wrap {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  height: 20px;
  cursor: pointer;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.slider-track-wrap:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 4px;
  border-radius: var(--radius-pill);
}

.slider-track-inner {
  position: relative;
  /* recuada 8px de cada lado (não a técnica antiga de padding+margin
     negativa no wrap, que "vazava" pra fora do próprio container e
     cortava — ficava dependendo de sobrar espaço/gutter no pai, que nem
     sempre tinha) — a bolinha (8px de raio) nunca ultrapassa a borda do
     PRÓPRIO wrap agora, então nenhum ancestral com overflow:hidden (ex.:
     .control-section-body-inner, usado pro colapso animado das seções)
     tem o que cortar. */
  width: calc(100% - 16px);
  margin: 0 8px;
}

.slider-track {
  position: relative;
  width: 100%;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--track-off);
  overflow: visible;
}

.slider-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--accent-2);
}

.slider-thumb {
  position: absolute;
  top: 50%;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--text);
  transform: translate(-50%, -50%) scale(1);
  transition: transform 0.15s var(--ease-expressive);
}

.slider-track-wrap:hover .slider-thumb,
.slider-track-wrap:focus-visible .slider-thumb,
.slider-track-wrap.dragging .slider-thumb {
  transform: translate(-50%, -50%) scale(1.15);
}

.control-toggle-switch {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.toggle-switch {
  /* desligado = vermelho sólido, ligado = azul sólido — as duas cores da
     paleta, cheias as duas (nada de meio-tom/transparência num estado e
     cor cheia no outro). */
  flex-shrink: 0;
  position: relative;
  width: 40px;
  height: 22px;
  padding: 2px;
  border: 1px solid var(--accent);
  background: var(--accent);
  border-radius: var(--radius-md);
  cursor: pointer;
  transform: scale(1);
  transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.15s var(--ease-expressive);
}

.toggle-switch-knob {
  display: block;
  width: 16px;
  height: 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 50%;
  /* mola leve em vez de deslizar seco — pequeno "estica e assenta"
     (--ease-expressive) toda vez que liga/desliga. Cor/borda também
     suavizadas (antes só a posição deslizava, a cor trocava seca). */
  transition: transform 0.3s var(--ease-expressive), background-color 0.2s ease, border-color 0.2s ease;
}

.toggle-switch.on {
  background: var(--accent-2);
  border-color: var(--accent-2);
}

.toggle-switch.on .toggle-switch-knob {
  transform: translateX(18px);
  background: var(--accent-text);
  border-color: var(--accent-2);
}

.preset-save-row {
  display: flex;
  gap: 8px;
}

.preset-name-input {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  font-size: 13px;
}

.preset-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.preset-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.preset-load-button {
  flex: 1;
  min-width: 0;
  text-align: left;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preset-load-button:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.preset-delete-button {
  flex-shrink: 0;
  /* era 26px — abaixo do mínimo recomendado de alvo de toque (~32-44px);
     32px ainda cabe ao lado do nome do preset sem dominar a linha. */
  width: 32px;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text-muted);
  font-size: 13px;
  cursor: pointer;
  transform: scale(1);
  transition: border-color 0.15s ease, color 0.15s ease, transform 0.15s var(--ease-expressive);
}

.preset-delete-button:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.theme-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.theme-arrow-button {
  flex-shrink: 0;
  width: 30px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--accent-2);
  border-radius: var(--radius-md);
  background: var(--accent-2);
  color: var(--accent-text);
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  padding: 0;
  transition: transform 0.15s var(--ease-expressive);
}

.theme-arrow-button:hover {
  transform: scale(1.08);
}

.theme-combo {
  position: relative;
  flex: 1;
  min-width: 0;
}

.theme-combo-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
}

.theme-combo-trigger:hover {
  border-color: var(--accent-2);
}

.theme-combo-label {
  flex: 1;
  min-width: 0;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.theme-combo-chevron {
  flex-shrink: 0;
  font-size: 11px;
  color: var(--text-muted);
}

.theme-combo-preview {
  flex-shrink: 0;
  display: flex;
  gap: 2px;
}

.theme-combo-dot {
  width: 12px;
  height: 12px;
  border: 1px solid var(--border);
  /* --radius-md (10px) num quadrado de 12px vira quase um círculo mole —
     --radius-sm mantém a bolinha reconhecível como indicador de cor. */
  border-radius: var(--radius-sm);
}

.theme-combo-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 10;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* semitransparente + blur, igual ao painel de ajustes mobile e ao
     dropdown de select (.control-select-panel). */
  background: rgba(20, 18, 16, 0.9);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
  animation: gi-section-enter 0.2s var(--ease-expressive);
}

.theme-combo-row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: none;
  border-bottom: 1px solid var(--track-off);
  /* transparente (era --panel sólido) — o painel já é semitransparente +
     blur (ver .theme-combo-panel); um fundo opaco por linha escondia
     esse efeito atrás do texto de cada opção. */
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  animation: control-select-option-in 0.15s var(--ease-expressive) both;
}

.theme-combo-row:last-child {
  border-bottom: none;
}

.theme-combo-row:hover {
  /* --track-off (0.14 opacity) sozinho é sutil demais pra sinalizar hover
     num dropdown — mais visível aqui sem mudar a variável compartilhada
     (ela também é usada como cor de borda em outros lugares). */
  background: rgba(241, 238, 192, 0.22);
}

.theme-combo-row.active {
  color: var(--accent);
  font-weight: 600;
}

/* dropdown customizado (ver ui/controls/select.js) — mesmo visual do
   combo de tema (.theme-combo-*), só genérico. .control-select-combo é o
   ancestral posicionado (position:relative) que o painel absoluto usa de
   referência. */
.control-select-combo {
  position: relative;
}

.control-select-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease;
}

.control-select-trigger:hover {
  border-color: var(--accent-2);
}

/* aberto: o canto de baixo do campo "desarredonda" (fica reto) pra
   emendar direto no painel logo abaixo — só o topo do painel arredondado
   ficaria estranho colado num campo ainda com canto de baixo arredondado. */
.control-select.open .control-select-trigger {
  border-color: var(--accent-2);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.control-select-trigger-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.control-select-chevron {
  flex-shrink: 0;
  font-size: 11px;
  color: var(--text-muted);
  transition: transform 0.15s var(--ease-expressive);
}

.control-select.open .control-select-chevron {
  transform: rotate(180deg);
}

.control-select-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 10;
  max-height: 280px;
  overflow-y: auto;
  border: 1px solid var(--accent-2);
  border-top: none;
  /* só embaixo arredondado — o de cima fica reto, emendado no campo
     (ver .control-select.open .control-select-trigger acima). */
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  /* semitransparente + blur, igual ao painel de ajustes mobile — dá pra
     ver um pouco do que tá atrás em vez de um painel 100% opaco. */
  background: rgba(20, 18, 16, 0.9);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}

.control-select.open .control-select-panel {
  display: block;
  /* mesma entrada "sobe de leve" usada em outros painéis que abrem no
     site (galeria, seção mobile) — nunca só aparece estático. */
  animation: gi-section-enter 0.2s var(--ease-expressive);
}

/* cascata rápida ao abrir — cada opção entra um pouquinho depois da
   anterior (animation-delay setado via JS, ver renderOptions em
   select.js), começando pela primeira; "both" mantém o estado inicial
   (opacity:0) durante o delay, sem piscar tudo visível antes da vez de
   cada uma. */
@keyframes control-select-option-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.control-select-option {
  width: 100%;
  display: block;
  padding: 8px 10px;
  border: none;
  border-bottom: 1px solid var(--track-off);
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  animation: control-select-option-in 0.15s var(--ease-expressive) both;
}

.control-select-option:last-child {
  border-bottom: none;
  /* o painel já corta com overflow:hidden implícito do border-radius,
     mas sem isso o último item quadrado "vazava" um cantinho reto por
     baixo do canto arredondado do painel. */
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}

.control-select-option:hover {
  background: rgba(241, 238, 192, 0.12);
}

/* selecionado — mesma cor cheia usada em outros "ativos" do site (aba de
   formato marcada, categoria marcada), não só o texto colorido. */
.control-select-option.active {
  background: var(--accent-2);
  color: var(--accent-text);
}

.swatch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

.swatch-chip {
  position: relative;
}

.swatch-chip input[type="color"],
.harmony-row input[type="color"],
.background-color-input,
.stroke-color-input {
  -webkit-appearance: none;
  appearance: none;
  width: 34px;
  height: 34px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0;
  background: none;
  cursor: pointer;
}

.swatch-chip input[type="color"]::-webkit-color-swatch-wrapper,
.harmony-row input[type="color"]::-webkit-color-swatch-wrapper,
.background-color-input::-webkit-color-swatch-wrapper,
.stroke-color-input::-webkit-color-swatch-wrapper {
  padding: 0;
}

.swatch-chip input[type="color"]::-webkit-color-swatch,
.harmony-row input[type="color"]::-webkit-color-swatch,
.background-color-input::-webkit-color-swatch,
.stroke-color-input::-webkit-color-swatch {
  border: none;
}

.swatch-chip input[type="color"]::-moz-color-swatch,
.harmony-row input[type="color"]::-moz-color-swatch,
.background-color-input::-moz-color-swatch,
.stroke-color-input::-moz-color-swatch {
  border: none;
}

.swatch-remove {
  /* badge de canto — visual continua discreto, mas a área clicável real
     cresce pra perto do mínimo de 24px (WCAG 2.5.8) via ::before,
     sem empurrar os chips vizinhos (que são apertados, com gap curto). */
  position: absolute;
  top: -7px;
  right: -7px;
  width: 18px;
  height: 18px;
  line-height: 16px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--panel);
  color: var(--text-muted);
  font-size: 11px;
  cursor: pointer;
}

.swatch-remove::before {
  content: "";
  position: absolute;
  inset: -3px;
}

.swatch-remove:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.harmony-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.harmony-row input[type="color"] {
  flex-shrink: 0;
}

.swatch-add {
  width: 34px;
  height: 34px;
  border: 1px dashed var(--border);
  /* faltava — os outros chips de cor ao lado (34x34px também) já usam
     --radius-md, esse ficava com canto reto destoando da fileira. */
  border-radius: var(--radius-md);
  background: none;
  color: var(--text-muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

.swatch-add:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.checkbox-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.checkbox-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}

.checkbox-chip input[type="checkbox"] {
  accent-color: var(--accent-2);
}

.shape-toggle-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.shape-toggle-chip {
  position: relative;
}

.shape-toggle {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* era var(--bg) — a MESMA cor do fundo da página, então o botão
     inativo ficava invisível (só a borda fininha denunciava que ele
     existe). --panel destaca ele do fundo mesmo sem estar selecionado. */
  background: var(--panel);
  color: var(--text-muted);
  cursor: pointer;
}

.shape-toggle svg {
  width: 22px;
  height: 22px;
}

.shape-toggle:hover {
  border-color: var(--accent-2);
}

.shape-toggle.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}

.color-toggle-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.color-toggle-swatch {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border);
  /* --radius-md era grande demais pros 26px do swatch, deixava ele
     "borrado" em vez de um quadradinho de cor nítido. */
  border-radius: var(--radius-sm);
  cursor: pointer;
  /* 0.35 deixava a cor de verdade quase irreconhecível no estado
     desmarcado — 0.6 ainda diferencia claramente do "active" (opacity:1 +
     borda grossa), mas dá pra ver a cor real sem apertar os olhos. */
  opacity: 0.6;
}

.color-toggle-swatch:hover {
  border-color: var(--accent-2);
}

.color-toggle-swatch.active {
  opacity: 1;
  border: 2px solid var(--accent);
}

.gradient-stops-bar {
  position: relative;
  height: 28px;
  margin: 6px 12px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: copy;
}

.gradient-stop-handle {
  position: absolute;
  top: 100%;
  /* era 14px — pequeno demais pra arrastar com o dedo com precisão;
     18px/margem -9px mantém centralizado na mesma posição. */
  width: 18px;
  height: 18px;
  margin-left: -9px;
  padding: 0;
  border: 1px solid var(--border);
  /* --radius-md arredondava demais os "cantos" do losango (é um quadrado
     rotacionado 45°) — virava um blob em vez de um losango reconhecível
     como alça de arraste. --radius-sm mantém a ponta nítida. */
  border-radius: var(--radius-sm);
  background: var(--panel);
  cursor: grab;
  transform: rotate(45deg);
  transform-origin: center;
  margin-top: 2px;
}

.gradient-stop-handle:active {
  cursor: grabbing;
}

.gradient-stop-handle::before {
  /* área de toque perto do mínimo de 24px (WCAG 2.5.8) sem alargar o
     losango visual — que precisa ficar fino pra arrastar com precisão
     ao longo da barra de gradiente. */
  content: "";
  position: absolute;
  inset: -3px;
}

.gradient-stop-handle.selected {
  border: 2px solid var(--accent);
  z-index: 1;
}

.gradient-stop-editor {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
}

.gradient-stop-remove {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 4px 10px;
  font-size: 12px;
}

.gradient-stop-remove:hover:not(:disabled) {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.gradient-stop-remove:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.control-button {
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
  transform: scale(1);
  transition: border-color 0.15s ease, transform 0.15s var(--ease-expressive);
  /* label sempre numa span própria agora (ícone opcional antes dela, ver
     createButton em ui/controls/button.js) — flex centraliza os dois
     juntos mesmo nos botões sem ícone nenhum (a span sozinha no meio). */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.control-button-icon {
  display: flex;
  flex-shrink: 0;
}

.control-button-icon svg {
  width: 16px;
  height: 16px;
  display: block;
}

.control-button:hover {
  border-color: var(--accent-2);
}

/* feedback de toque — encolhe de leve ao pressionar (dedo ou clique) em
   tudo que é botão/aba/pílula clicável do app. Sem isso, num touchscreen
   não tem confirmação nenhuma de "eu toquei aqui" até o efeito (a troca de
   painel, o preview mudando) acontecer — parece menos responsivo/vivo do
   que realmente é. */
.control-button:active,
.gi-stage-tabs-label:active,
.gi-mobile-category-tabs-label:active,
.frame-picker-tile:active,
.lang-button:active,
.toggle-switch:active,
.draw-close-btn:active,
.preset-delete-button:active {
  transform: scale(0.94);
}

.control-button-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
  font-weight: 700;
  border-radius: var(--radius-pill);
}

/* variante azul (--accent-2) — usada nos botões "Enviar imagem", pra
   destacar essa ação sem usar o vermelho padrão (já reservado pras ações
   "principais" tipo Exportar/Novo azulejo). */
.control-button-accent2 {
  background: var(--accent-2);
  border-color: var(--accent-2);
  color: var(--accent-text);
  font-weight: 700;
}

/* confirmação visual de exportação — o botão "enche" de branco da
   esquerda pra direita e some (ver flashExportSuccess em
   ui/controls/button.js, chamado depois de Exportar SVG/PNG). */
@keyframes export-fill-sweep {
  0% {
    transform: scaleX(0);
    opacity: 0.85;
  }
  70% {
    transform: scaleX(1);
    opacity: 0.85;
  }
  100% {
    transform: scaleX(1);
    opacity: 0;
  }
}

.control-button.export-flash {
  position: relative;
  overflow: hidden;
}

.control-button.export-flash::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--accent-2);
  transform-origin: left center;
  animation: export-fill-sweep 0.6s ease-out;
  pointer-events: none;
}

/* o texto (e ícone, quando tem) precisam ficar POR CIMA do azul que
   varre o botão (::after acima) — sem isso, o texto ficava tingido de
   azul enquanto a animação passava por baixo dele. */
.control-button-label,
.control-button-icon {
  position: relative;
  z-index: 1;
}

.draw-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
}

.draw-modal {
  background: var(--panel);
  border: 2px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: calc(100vw - 40px);
}

.draw-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.draw-modal-title {
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.draw-close-btn {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  font-size: 18px;
  line-height: 1;
  width: 28px;
  height: 28px;
  cursor: pointer;
  transform: scale(1);
  transition: border-color 0.15s ease, color 0.15s ease, transform 0.15s var(--ease-expressive);
}

.draw-close-btn:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}

.draw-canvas {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: #ffffff;
  cursor: crosshair;
  touch-action: none;
  max-width: 100%;
  height: auto;
}

.draw-brush-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* mesmo tratamento do slider padrão (sem contorno branco nativo, bolinha
   clara) — só sem o preenchimento progressivo (--fill-percent), que é
   específico do componente createSlider; esse aqui é um input solto. */
.draw-brush-row input[type="range"] {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 20px;
  background: transparent;
  cursor: pointer;
}

.draw-brush-row input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--track-off);
}

.draw-brush-row input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--track-off);
}

.draw-brush-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6px;
  border: none;
  border-radius: 50%;
  background: var(--text);
  cursor: pointer;
}

.draw-brush-row input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: none;
  border-radius: 50%;
  background: var(--text);
  cursor: pointer;
}

.draw-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}

/* mosaic module — mesmo shell de painel-duplo-com-scroll-independente do
   grid-icons (.gi-*), prefixo próprio (.mo-*) */

.mo-layout {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 32px;
  align-items: stretch;
}

.mo-controls {
  width: 320px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 26px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  overflow-y: auto;
  overflow-x: hidden;
}

.mo-stage {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  overflow-y: auto;
  overflow-x: hidden;
}

.mo-result-title {
  display: none;
}

.mo-preview-wrap {
  flex-shrink: 0;
  width: 100%;
}

.mo-preview {
  width: 100%;
  overflow: hidden;
  background: repeating-conic-gradient(rgba(255, 255, 255, 0.07) 0% 25%, rgba(255, 255, 255, 0.02) 0% 50%) 0 0 / 20px 20px;
}

.mo-preview svg {
  width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
}

.mo-repeat-preview-wrap {
  flex-shrink: 0;
  width: 100%;
  max-width: 720px;
}

.mo-repeat-preview-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 8px;
}

/* estilo "prévia de padrão" do Illustrator: grid 3x3 com a MESMA arte em
   cada célula — a célula do meio (a real) em opacidade cheia, marcada com o
   mesmo pontilhado laranja do modo de edição do Azulejo (.gi-grid-editor-cell)
   pra deixar claro onde termina o mosaico original; as 8 ao redor em 40%,
   deixando claro que aquilo é o padrão se repetindo, não um mosaico
   diferente ao lado. */
.mo-repeat-preview {
  width: 100%;
  aspect-ratio: 1;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  overflow: hidden;
}

.mo-repeat-preview-cell {
  background-repeat: no-repeat, repeat;
  background-position: center, 0 0;
  background-size: 100% 100%, 20px 20px;
  opacity: 0.4;
}

.mo-repeat-preview-cell-center {
  opacity: 1;
  position: relative;
  z-index: 1;
  border: 1px dashed rgba(234, 69, 48, 0.4);
}

.mo-stage-toolbar {
  flex-shrink: 0;
  width: 100%;
  max-width: 720px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

.mo-stage-toolbar .control-button {
  flex: 1;
}

.mo-actions {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 24px;
}

.mo-file-input {
  font-size: 12px;
  color: var(--text-muted);
}

.mo-gradient-icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* icon-select — mesmo visual do theme-combo (Azulejo), genérico: cada opção
   mostra o próprio ícone (renderIcon) em vez de bolinhas de cor fixas. */

.icon-select-combo {
  position: relative;
}

.icon-select-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
}

.icon-select-label {
  flex: 1;
  min-width: 0;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.icon-select-chevron {
  flex-shrink: 0;
  font-size: 11px;
  color: var(--text-muted);
}

.icon-select-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 10;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* semitransparente + blur, igual aos outros dropdowns (combo de tema,
     select customizado). */
  background: rgba(20, 18, 16, 0.9);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
  animation: gi-section-enter 0.2s var(--ease-expressive);
}

.icon-select-row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: none;
  border-bottom: 1px solid var(--track-off);
  /* transparente (era --panel sólido) — mesma razão do .theme-combo-row:
     um fundo opaco por linha escondia o blur/transparência do painel. */
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  animation: control-select-option-in 0.15s var(--ease-expressive) both;
}

.icon-select-row:last-child {
  border-bottom: none;
}

.icon-select-row:hover {
  /* mesmo motivo do .theme-combo-row:hover acima. */
  background: rgba(241, 238, 192, 0.22);
}

.icon-select-row.active {
  color: var(--accent);
  font-weight: 600;
}

/* ==========================================================================
   mobile — abaixo de 768px os dois painéis (controles + área de trabalho)
   não cabem lado a lado.

   1ª tentativa: empilhar os dois e deixar a página inteira rolar — ruim,
   virava "rolar tudo pra ver o preview". 2ª tentativa: abas Preview/
   Controles alternando um painel por vez — também ruim, do jeito oposto
   (pra ver o efeito de um ajuste você tinha que trocar de aba toda hora).
   3ª tentativa: preview fixo (sem crescer) + controles ocupando o resto,
   mas com teto de altura + scroll no preview pra quando a galeria de
   variações/histórico tava aberta — ruim de novo, um preview que também
   rola é desconfortável de mexer.

   Solução final: o preview em si (resultado + toggle de controle fino +
   botões) NUNCA rola — sem título "Resultado", sem a legenda do controle
   fino (só o switch), cabe inteiro sem precisar de scroll, do tamanho do
   próprio conteúdo (.gi-stage/.mo-stage com flex:0 0 auto, sem
   max-height). Variações e Histórico saem de dentro dessa área e viram
   abas próprias (.gi-mobile-tabs, só existe/aparece no mobile) — só uma
   dessas 3 "telas" (Resultado/Variações/Histórico) aparece por vez dentro
   do preview, com :has() (ver adiante), sem JS de estado. Só a aba de
   Variações/Histórico tem scroll interno (é uma lista, faz sentido rolar;
   .gi-gallery já limitava a própria altura antes disso, reaproveitado
   aqui). Os controles (.gi-controls/.mo-controls) continuam ocupando o
   resto da tela, sempre visíveis, sempre roláveis, e no mobile já entram
   com todas as seções abertas (menos espaço sobrando = mais fácil perder
   de vista o que existe se começar tudo fechado; ver isMobileViewport em
   grid-icons/index.js).
   ========================================================================== */

@media (max-width: 768px) {
  /* header: uma linha só, sempre — logo + abas de módulo de um lado,
     PT/EN do outro, tudo mais compacto (menos gap/padding/fonte) pra
     caber sem quebrar. Quebrar em 2 linhas tomava espaço vertical demais
     numa tela já apertada. A pílula flutuante centralizada (.brand no
     desktop) não cabe ao lado do seletor de idioma numa tela estreita —
     aqui ela volta a ficar no fluxo normal, encostada no logo à esquerda,
     como era antes; só o visual em pílula das abas (ganho novo) continua. */
  .app-header {
    justify-content: space-between;
    padding: 12px 14px;
  }

  .brand {
    position: static;
    transform: none;
    gap: 8px;
  }

  .module-tabs {
    height: 34px;
    gap: 2px;
    padding: 2px;
  }

  .module-tabs button,
  .module-tab-disabled {
    padding: 0 12px;
    font-size: 12px;
  }

  .app-logo {
    width: 32px;
    height: 32px;
  }

  .app-logo svg {
    height: 20px;
    width: 20px;
  }

  .lang-switcher {
    margin-left: 0;
    gap: 1px;
  }

  .lang-button {
    padding: 4px 8px;
    font-size: 11px;
  }

  .module-content {
    padding: 12px;
  }

  .gi-layout,
  .mo-layout {
    flex-direction: column;
    gap: 10px;
    /* rede de segurança: nas contas de tamanho (ver computePreviewBoxSize
       em grid-icons/index.js) o preview sempre deixa espaço reservado pro
       painel de ajustes, mas numa tela realmente curta demais (bem menor
       que qualquer celular de verdade) ainda pode sobrar menos do que o
       necessário — com overflow:auto aqui, dá pra rolar a página inteira
       até o menu de categorias em vez dele ficar impossível de alcançar. */
    overflow-y: auto;
  }

  .gi-stage,
  .mo-stage {
    /* preview antes dos controles no visual (a pessoa quer ver o ícone
       primeiro), mesmo os controles vindo antes no DOM. */
    order: -1;
    width: 100%;
    max-width: none;
    /* do tamanho do próprio conteúdo, nunca com scroll próprio — ver
       comentário grandão acima. Padding vertical menor que no desktop —
       numa tela de celular mais baixa (tipo iPhone SE) cada pixel de
       sobra aqui é espaço a menos brigando com o mínimo do painel de
       ajustes embaixo (ver min-height em .gi-sidebar-column/.mo-controls
       e controlsMinHeight em computePreviewBoxSize, grid-icons/index.js). */
    flex: 0 0 auto;
    /* era "visible" — sobrou de quando essa moldura (painel/borda) não
       existia ainda e não tinha o que "vazar" pra fora dela; agora com a
       moldura de verdade (ver regra base .gi-stage), overflow:visible
       deixava conteúdo mais alto que o esperado (ex.: a galeria de
       Histórico com muita miniatura) escapar visualmente pra fora do
       cantinho arredondado em vez de ficar contido/rolável lá dentro. */
    overflow: hidden;
    /* era "12px 0" (sem padding lateral nenhum) — sobrou de quando essa
       moldura não existia ainda; com o painel/borda de verdade (ver regra
       base .gi-stage acima), isso encostava a trilha de abas e o preview
       direto na borda da moldura, sem respiro nenhum nas laterais. */
    padding: 16px;
  }

  /* no desktop .gi-stage é uma linha (conteúdo + trilha de abas lado a
     lado) — no celular vira coluna, com a trilha virando uma barra de
     pílulas horizontal em cima (ver .gi-stage-tabs abaixo). */
  .gi-stage {
    flex-direction: column;
  }

  /* botões abaixo do preview (Mosaico, que não tem trilha de ícones — o
     Azulejo já esconde .gi-stage-toolbar aqui embaixo, ver regra
     seguinte, então essa regra não afeta ele): menores que no desktop, o
     preview é o protagonista da tela. */
  .mo-stage-toolbar .control-button {
    padding: 8px 10px;
    font-size: 12px;
  }

  /* trio "Novo azulejo / Criar variações / Estou com sorte" — no mobile
     vira ícones numa trilha ABAIXO do preview (era ao lado — tomava
     espaço horizontal da própria arte, deixando o preview menor à toa); a
     pílula com texto (só existe pro desktop) some daqui. */
  .gi-stage-toolbar {
    display: none;
  }

  .gi-stage-toolbar-rail {
    display: flex;
    flex-direction: row;
    justify-content: center;
    gap: 8px;
    width: 100%;
  }

  /* painel de ajustes do Azulejo virou um "bottom sheet" flutuante, estilo
     Lightroom mobile: fixo na parte de baixo da TELA (não mais no fluxo
     normal, empurrando o preview pra cima) — só a barra de categorias
     fica sempre visível (o painel some inteiro quando nenhuma categoria
     está marcada, já que cada seção some via display:none e não sobra
     mais nada além da barra). Isso libera a tela quase inteira pro
     preview em vez de sempre reservar um pedaço fixo de altura pro
     painel, mesmo fechado. Fundo semitransparente + blur — dá pra ver um
     pouco do preview por trás quando o painel abre, em vez de tapar tudo
     com uma cor opaca. */
  .gi-controls {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    width: 100%;
    max-height: 70vh;
    /* +10% mais transparente/blur que a primeira versão (era 0.88/14px). */
    background: rgba(20, 18, 16, 0.78);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border: none;
    border-top: 1px solid var(--border);
    border-radius: 16px 16px 0 0;
    /* menos padding que no desktop — libera espaço vertical extra numa
       tela de celular mais baixa (ver comentário em .gi-stage acima). */
    padding: 16px;
    overflow: hidden;
  }

  .gi-sidebar-column {
    width: 100%;
    max-width: none;
    /* .gi-controls saiu do fluxo normal (position:fixed acima) — essa
       coluna não precisa mais reservar altura nenhuma pra ele. */
    flex: 0 0 auto;
    min-height: 0;
  }

  .mo-controls {
    width: 100%;
    max-width: none;
    /* ocupa todo o resto da altura disponível — sempre visível junto com
       o preview, nunca escondido atrás de uma aba (quem rola por dentro é
       o painel em si). */
    flex: 1 1 auto;
    /* nunca menor que isso, nem numa tela curta — sem esse piso, flex:1 1
       auto encolhia esse painel até sobrar menos altura do que ele precisa
       pra existir, e como flex-shrink não gera overflow, não tinha nem
       como rolar até ele (ver overflow-y no .mo-layout acima). min() com
       vh em vez de um número fixo: numa tela realmente curta (celular
       deitado, por ex.) o piso encolhe proporcionalmente em vez de forçar
       overflow/scroll sempre que 260px não caberia de jeito nenhum. */
    min-height: min(260px, 40vh);
  }

  /* trilha de abas Resultado/Variações/Histórico + botão Editar — a regra
     base já deixa isso uma linha horizontal cheia (mesmo visual dos dois
     tamanhos de tela agora); só a POSIÇÃO muda: no desktop fica embaixo do
     preview (order:1, ver regra base), no mobile continua em cima de tudo,
     antes do preview aparecer. */
  .gi-stage-tabs-row {
    margin-bottom: 12px;
  }

  .theme-combo-panel,
  .icon-select-panel {
    max-height: 45vh;
  }

  /* aba de categorias da sidebar (Tema/Grade/Detalhe/Cores/...) — estilo
     Lightroom mobile de verdade: ícone em cima, nome embaixo (não só
     texto — bem mais fácil de reconhecer de relance rolando a barra
     horizontal). Antes era sticky pra ficar visível dentro do painel que
     rolava; agora .gi-controls inteiro é fixo (ver regra dela acima) e é a
     SEÇÃO aberta que rola (ver overflow-y no .control-section[data-
     section-id] mais abaixo) — essa barra já fica sempre visível por ser
     o último item da coluna, sem precisar de sticky. Ver grid-icons/
     index.js (monta o markup, ui/categoryIcons.js os ícones) e
     ui/controls/section.js (data-section-id em cada .control-section). */
  .gi-mobile-category-tabs {
    /* os -16px (bottom/margin) batem com o padding de .gi-controls no
       mobile (16px, ver regra dela acima) — sem isso alinhado, a barra
       ficava com uma folga (ou cortava) embaixo em vez de grudar rente. */
    margin: 12px -16px -16px;
    display: flex;
    flex-shrink: 0;
    gap: 4px;
    overflow-x: auto;
    /* rolagem horizontal com "click" de parada em cada ícone — parece
       nativo (roda com inércia e sempre assenta alinhada em vez de parar
       no meio de um ícone). */
    scroll-snap-type: x proximity;
    padding: 8px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    background: var(--panel);
    border-top: 1px solid var(--border);
    /* sombra sutil pra cima — sinaliza que essa barra "flutua" por cima
       do conteúdo que rola por trás dela, não é só mais uma linha na
       lista (mesma linguagem de elevação do Material). */
    box-shadow: 0 -8px 16px rgba(0, 0, 0, 0.35);
    /* sem barra de rolagem visível — o esmaecido nas pontas (mask-image
       abaixo) já avisa que tem mais ícone pra rolar, sem precisar de uma
       barrinha cinza embaixo (que também brigava com a sombra acima). */
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-mask-image: linear-gradient(
      to right,
      transparent,
      black 16px,
      black calc(100% - 16px),
      transparent
    );
    mask-image: linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent);
  }

  .gi-mobile-category-tabs::-webkit-scrollbar {
    display: none;
  }

  .gi-mobile-category-tabs-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
  }

  /* mesma razão do .gi-stage-tabs-input:focus-visible acima — o anel de
     foco (teclado) precisa aparecer no rótulo visível, não no input de
     1px escondido atrás dele. */
  .gi-mobile-category-tabs-input:focus-visible + .gi-mobile-category-tabs-label {
    outline: 2px solid var(--accent-2);
    outline-offset: 2px;
  }

  .gi-mobile-category-tabs-label {
    scroll-snap-align: center;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* 3px/9px ficava apertado demais pra ler de relance — 4px/10px ainda
       cabe nos 56px de largura, mais confortável. */
    gap: 4px;
    width: 56px;
    padding: 6px 4px;
    border-radius: var(--radius-sm);
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.01em;
    text-align: center;
    color: var(--text-muted);
    cursor: pointer;
    transform: scale(1);
    transition: color 0.2s ease, transform 0.15s var(--ease-expressive);
  }

  .gi-mobile-category-tabs-icon {
    /* "indicador ativo" em pílula atrás do ícone — como a barra de
       navegação do Material 3: a categoria marcada não muda só a cor, ela
       ganha uma pílula cheia atrás do ícone, que "estica" pra dentro com
       uma leve mola (ver --ease-expressive) em vez de só aparecer/sumir. */
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 22px;
    border-radius: var(--radius-pill);
    background: transparent;
    /* era 0.85 — o "pop" ficava exagerado/desconectado do resto do
       feedback de toque do app (que usa 0.94 no :active); 0.92 ainda
       deixa o morph do indicador perceptível, sem destoar tanto. */
    transform: scale(0.92);
    transition: background-color 0.25s var(--ease-expressive), transform 0.3s var(--ease-expressive);
  }

  .gi-mobile-category-tabs-icon svg {
    width: 18px;
    height: 18px;
  }

  .gi-mobile-category-tabs-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
    transition: color 0.2s ease;
  }

  .gi-mobile-category-tabs-input:checked + .gi-mobile-category-tabs-label {
    color: var(--accent);
  }

  .gi-mobile-category-tabs-input:checked + .gi-mobile-category-tabs-label .gi-mobile-category-tabs-icon {
    background: var(--accent);
    color: var(--accent-text);
    transform: scale(1);
  }

  /* cada seção só aparece quando a aba correspondente está marcada — o
     chevron/colapso interno de cada seção continua existindo no DOM (não
     precisou mexer nisso), só que agora quem manda na visibilidade é a
     aba, não mais o clique no próprio cabeçalho da seção. */
  .control-section[data-section-id] {
    display: none;
    /* no mobile só UMA seção aparece por vez (a aba de categoria decide
       qual) — o traço/respiro de cima (pensado pra separar seções
       empilhadas no desktop) e o cabeçalho com nome+chevron não fazem
       sentido aqui: não tem nada acima pra separar, e o nome já repete o
       nome do ícone da aba logo embaixo. */
    padding-top: 0;
    border-top: none;
  }

  /* só o nome+chevron some (já repete o nome do ícone da aba logo
     embaixo) — o botão de "randomizar" da seção (quando existe) continua,
     é uma ação de verdade, não só decoração. */
  .control-section[data-section-id] .control-section-toggle {
    display: none;
  }

  .control-section[data-section-id] .control-section-randomize {
    margin-left: auto;
  }

  /* IMPORTANTE: era ".gi-mobile-category-tabs:has(...) ~ [data-section-id]"
     (combinador de irmão geral, "~") — só funciona quando o alvo vem DEPOIS
     do elemento de referência no DOM. A barra de categorias é inserida no
     fim da sidebar (depois de todas as seções, ver index.js), ou seja,
     ela SEMPRE vem depois das seções — o "~" nunca conseguia acertar
     nenhuma delas, então nenhuma categoria jamais mostrava conteúdo
     nenhum (com todo o resto escondido, o mobile inteiro parecia quebrado:
     não tinha como ajustar nada). Corrigido usando :has() no ancestral
     comum (.gi-controls) + seletor descendente — não depende de ordem. */
  .gi-controls:has(#gi-cat-format:checked) [data-section-id='format'],
  .gi-controls:has(#gi-cat-theme:checked) [data-section-id='theme'],
  .gi-controls:has(#gi-cat-reference:checked) [data-section-id='reference'],
  .gi-controls:has(#gi-cat-grid:checked) [data-section-id='grid'],
  .gi-controls:has(#gi-cat-detail:checked) [data-section-id='detail'],
  .gi-controls:has(#gi-cat-fill:checked) [data-section-id='fill'],
  .gi-controls:has(#gi-cat-gradient:checked) [data-section-id='gradient'],
  .gi-controls:has(#gi-cat-shapes:checked) [data-section-id='shapes'],
  .gi-controls:has(#gi-cat-colors:checked) [data-section-id='colors'],
  .gi-controls:has(#gi-cat-harmony:checked) [data-section-id='harmony'],
  .gi-controls:has(#gi-cat-appearance:checked) [data-section-id='appearance'],
  .gi-controls:has(#gi-cat-presets:checked) [data-section-id='presets'],
  .gi-controls:has(#gi-cat-share:checked) [data-section-id='share'],
  .gi-controls:has(#gi-cat-export:checked) [data-section-id='export'] {
    display: flex;
    /* o painel "sobe" de leve ao trocar de categoria (em vez de só
       aparecer estático) — dá o mesmo tipo de resposta de um bottom sheet
       abrindo, junto do scroll-pro-topo em JS (ver index.js) que já
       garante que o painel esteja de fato visível na tela ao trocar.
       flex:1 1 auto + min-height:0 + overflow-y:auto: .gi-controls agora é
       um "bottom sheet" fixo com altura máxima (70vh) — é a seção aberta
       em si que precisa rolar dentro desse teto (a barra de categorias,
       último item da coluna, fica sempre visível embaixo dela). */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }
}

/* ==========================================================================
   cursor customizado — ver src/ui/customCursor.js. A classe
   custom-cursor-active só entra via JS depois que o módulo confirma que
   vai desenhar o substituto (pointer:fine) — nunca direto aqui, senão um
   JS que falhasse deixava a pessoa sem cursor nenhum.
   ========================================================================== */

.custom-cursor-active,
.custom-cursor-active * {
  cursor: none !important;
}

/* input[type=range] é um controle nativo — parte dele (a bolinha/trilho)
   é desenhada em pseudo-elementos (::-webkit-slider-thumb etc.), que o
   seletor universal (*) acima NÃO alcança (pseudo-elemento não é
   elemento) — sem isso especificamente, a mãozinha do sistema aparecia
   de novo bem em cima da bolinha, mesmo com cursor:none no input em si. */
.custom-cursor-active input[type="range"]::-webkit-slider-thumb,
.custom-cursor-active input[type="range"]::-webkit-slider-runnable-track,
.custom-cursor-active input[type="range"]::-moz-range-thumb,
.custom-cursor-active input[type="range"]::-moz-range-track {
  cursor: none !important;
}

.custom-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10000;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease, transform 0.05s linear;
  will-change: transform;
}

.custom-cursor.visible {
  opacity: 1;
}

/* creme sólido + mix-blend-mode:difference — em vez de uma cor fixa (que
   sumia de vista em cima de fundo parecido), a diferença de cores com o
   que tá embaixo faz o cursor "inverter" o que ele cobre, sempre visível
   não importa a cor por baixo (mesmo truque de vários portfólios). */
.custom-cursor-shape {
  fill: var(--text);
  mix-blend-mode: difference;
  transition: fill 0.1s ease;
}

.custom-cursor.pressed {
  transform-origin: 1px 1px;
}

/* segurando o clique (arrastando um slider, por ex.) — troca de forma
   (seta vira bolinha cheia) e de cor: sai do "inverte o fundo" pra um
   vermelho sólido de verdade, confirmação bem mais clara de que o clique
   registrou do que só o scale(0.85) que já existia. */
.custom-cursor-arrow {
  display: block;
}

.custom-cursor-dot {
  display: none;
}

.custom-cursor.pressed .custom-cursor-arrow {
  display: none;
}

.custom-cursor.pressed .custom-cursor-dot {
  display: block;
}

.custom-cursor.pressed .custom-cursor-shape {
  fill: var(--accent);
  mix-blend-mode: normal;
}

/* prefers-reduced-motion: em vez de um "* { transition: none !important }"
   grosso (que também quebraria feedback útil de hover/foco), mira só nos
   dois efeitos que de fato têm "movimento" perceptível: a curva com mola
   (--ease-expressive vira uma curva neutra, sem overshoot) e a entrada de
   seção deslizando (gi-section-enter, que troca de translateY(8px) pra
   estático). Transições curtas de cor/opacidade continuam — não são o
   tipo de motion que causa desconforto. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --ease-expressive: ease;
  }

  .gi-gallery,
  [data-section-id] {
    animation: none;
  }

  .custom-cursor {
    transition: opacity 0.15s ease;
  }
}
