/* Orbitron (SIL Open Font License) hospedada aqui mesmo -- font-src do CSP
   é 'self' data:, então uma tag <link> pra fonts.googleapis.com nunca
   carregaria. Um arquivo variável só (peso 400 a 900 no mesmo .woff2) cobre
   qualquer peso que a gente use, sem precisar de um arquivo por peso.
   Fonte de título/display oficial da marca (ver guia de estilo da Andressa:
   "Títulos/Display: Orbitron" -- o lettering do logo em si é um desenho
   próprio "inspirado na Orbitron", não a fonte pronta; Orbitron é quem
   efetivamente entra no app pra tudo que não é o lettering do logo).
   Aplicada em h1/h2/h3 (ver regra logo abaixo) -- Inter continua sendo a
   fonte de texto/UI (body), como o guia também especifica. */
@font-face {
  font-family: 'Orbitron';
  src: url('fonts/orbitron-variable.woff2') format('woff2-variations'), url('fonts/orbitron-variable.woff2') format('woff2');
  font-weight: 400 900;
  font-display: swap;
}

:root {
  color-scheme: dark;
  /* paleta "Quiet Reef": carvao frio + creme quente + salvia clareada como
     acento, substitui a "Hermès Reef" (terracota + dourado quentes). Cinco
     cores de referencia do dono (#1E2326 carvao, #4D5A55 salvia, #B9B0C9
     lavanda, #E7E2D8 creme, #C8A77A dourado) mapeadas via 3 propostas
     revisadas com o especialista-ux; esta e a Proposta B ("salvia como
     acento, dourado em 2o plano"), escolhida pelo dono depois de ver as 3
     lado a lado numa maquete da tela Meu Reef. As cores de especies de
     coral (--cr-acropora etc.) continuam existindo pra distinguir series
     num grafico com varios parametros ao mesmo tempo — funcao de
     contraste mutuo, nao de marca — e por isso nao mudam com o redesign. */
  --cr-bg: #1E2326;
  --cr-surface-a: #2E3136;
  --cr-surface-b: #3A3C43;
  --cr-acropora: #00e5ff;
  --cr-zoanthus: #00ff87;
  --cr-torch: #ffd600;
  --cr-blastomussa: #ff4081;
  --cr-ruby: #ff1744;
  --cr-scolymia: #ff6600;
  --cr-ricordea: #b200ff;

  /* ─── identidade de aquário (ícone/cor em Configuração) ─────────────────
     2026-08-28: a primeira versão do seletor de aparência usava as cores de
     espécie de coral acima por conveniência; a Andressa pediu a paleta de
     marca "Quiet Reef" no lugar (mesmos 5 tons já usados no resto do app,
     mais Ocean Blue e Coral Orange, novos e exclusivos desta tela) --
     mesma ideia das --cr-*: literal, nao muda entre tema claro/escuro,
     porque e uma escolha de identidade da pessoa, nao uma cor de estado. */
  --aq-sage: #4D5A55;
  --aq-lavender: #B9B0C9;
  --aq-ocean: #5D7FA6;
  --aq-gold: #C8A77A;
  --aq-pearl: #E7E2D8;
  --aq-deep: #1E2326;
  --aq-orange: #E06A2C;

  /* ─── neutros de texto/fundo/borda (Quiet Reef) ─────────────────────────
     Rampa de 11 tons interpolando creme (#E7E2D8, extremo claro) ate carvao
     (#1E2326, extremo escuro) — substitui o cinza-azulado frio do Tailwind
     (slate) por um neutro quente coerente com a paleta nova. Usada pelos
     overrides de .text-slate-* / .bg-slate-* mais abaixo (perto do bloco
     .text-cyan-*). --cr-border-rgb e a lavanda (#B9B0C9), usada como tinta
     de borda/divisor em baixa opacidade — unico papel da lavanda nesta
     proposta (nao e um acento por aquario, so o tom dos contornos). */
  --cr-neutral-50-rgb: 227, 222, 212;
  --cr-neutral-100-rgb: 221, 216, 207;
  --cr-neutral-200-rgb: 207, 203, 195;
  --cr-neutral-300-rgb: 187, 184, 177;
  --cr-neutral-400-rgb: 147, 146, 141;
  --cr-neutral-500-rgb: 114, 115, 113;
  --cr-neutral-600-rgb: 86, 88, 88;
  --cr-neutral-700-rgb: 66, 69, 70;
  --cr-neutral-800-rgb: 50, 54, 56;
  --cr-neutral-900-rgb: 38, 43, 45;
  --cr-neutral-950-rgb: 32, 37, 40;
  --cr-border-rgb: 185, 176, 201; /* #B9B0C9 — lavanda, so' bordas/divisores */
  /* Tom fixo, NUNCA troca com o modo dia/noite — usado só pelo texto que
     senta em cima de um botão de acento (as 47 ocorrências de
     text-slate-950 no app inteiro são texto escuro sobre botão/selo
     colorido, nunca corpo de texto genérico). Se usasse
     --cr-neutral-950-rgb direto, esse texto inverteria pra creme no modo
     dia e ficaria ilegível sobre um botão de acento claro. */
  --cr-ink-fixed-rgb: 30, 35, 38; /* #1E2326 — carvão */

  /* ─── tema por aquario (Context Theming) ───────────────────────────────
     A cor de status (saudavel/atencao/critico = verde/amarelo/vermelho)
     NUNCA muda — ela precisa significar a mesma coisa em qualquer aquario.
     O que muda por aquario e a cor de marca: botoes primarios, nav ativo,
     brilhos e o aro do avatar. Aquario "primario" (o primeiro da lista) usa
     Salvia clareada + Dourado; qualquer aquario alem do primeiro
     (quarentena, muda, fish only...) usa Dourado + Salvia com os papeis
     invertidos — so pra dar pra diferenciar num relance qual aquario esta
     ativo. O JS alterna isso trocando body[data-aquarium-theme]; sem o
     atributo, cai no tema primario abaixo.

     Salvia pura (#4D5A55) e proxima demais da luminancia do proprio fundo
     pra funcionar como cor de botao/nav ativo (contraste insuficiente) —
     por isso o tom usado aqui como accent primario e uma versao clareada
     (mistura com creme), nao o hex puro. Atencao: nao reaproveite a salvia
     (pura ou clareada) como cor de "saudavel" so por parecer conveniente —
     status e semantico e fica com o verde/vermelho fixos (ver bloco
     .text-emerald-* / .text-rose-* mais abaixo), independente deste tema. */
  --theme-accent-100-rgb: 232, 234, 233; /* salvia clareada, tom 100 */
  --theme-accent-200-rgb: 209, 213, 211; /* salvia clareada, tom 200 */
  --theme-accent-300-rgb: 180, 188, 184; /* salvia clareada, tom 300 */
  --theme-accent-400-rgb: 149, 160, 154; /* salvia clareada, tom 400 */
  --theme-accent-rgb: 126, 139, 132; /* #7E8B84 — Salvia clareada */
  --theme-accent2-rgb: 200, 167, 122; /* #C8A77A — Dourado */

  /* Altura real de #app-top-header, medida por ResizeObserver em
     app.js (syncHeaderHeight) assim que o cabecalho fixo existe no DOM.
     O fallback (7rem) so' vale no primeiro paint, antes do JS rodar --
     generoso o bastante (cobre chip de aquario + ticker juntos) pra nunca
     deixar conteudo por baixo do cabecalho nesse instante inicial. */
  --header-height: 7rem;

  /* Paleta de cores pro ícone de perfil (PDF "ÍCONES DE PERFIL" da
     Andressa, 2026-08-29) -- combinação livre ícone+cor pro avatar de quem
     não quer subir foto. Cinco tons repetem os hex da marca acima (Quiet
     Reef), só que expostos aqui como variável de uso livre -- as de cima
     estão presas a um papel específico (fundo, borda, acento por aquário)
     e mexer nelas pra reaproveitar quebraria esses outros sistemas. Ocean
     Blue é a única cor realmente nova desta paleta -- Coral Orange saiu a
     pedido da Andressa (2026-08-29). Decorativa/identidade, não status --
     nunca usar em alerta/indicador. */
  --profile-color-sage-reef: #4D5A55;
  --profile-color-coral-lavender: #B9B0C9;
  --profile-color-ocean-blue: #5D7FA6;
  --profile-color-reef-gold: #C8A77A;
  --profile-color-pearl: #E7E2D8;
  --profile-color-deep-reef: #1E2326;

  /* ─── Design System (Fase 2, 2026-09-01) ────────────────────────────────
     Papel FIXO por cor, não decorativo -- pedido da Andressa: Coral Orange
     só em ação principal/alerta importante (nunca destrutivo), Ocean Blue
     em dado de água/informativo, Sage em saudável/estável, Gold em
     destaque premium, Lavender só como acento raro de identidade. Os hex
     dela (Deep Reef/Sage Reef/Reef Gold/etc.) são a mesma direção da
     paleta "Quiet Reef" já em produção, só com valores levemente
     diferentes -- reaproveita os tons que já estão no ar (--aq-*/--cr-*
     acima) em vez de criar uma segunda versão quase igual; só Coral Orange
     e Ocean Blue já eram idênticos. --ds-danger reaproveita o mesmo
     vermelho já mapeado em .bg-rose-500 abaixo (crítico é semântico, não
     muda com este sistema). */
  --ds-bg-base: rgb(var(--cr-neutral-950-rgb));
  --ds-surface: var(--cr-surface-a);
  --ds-surface-hover: var(--cr-surface-b);
  --ds-border: rgba(var(--cr-neutral-700-rgb), 0.35);
  --ds-border-strong: rgba(var(--cr-neutral-500-rgb), 0.5);
  --ds-text-primary: rgb(var(--cr-neutral-50-rgb));
  --ds-text-secondary: rgb(var(--cr-neutral-300-rgb));
  --ds-text-tertiary: rgb(var(--cr-neutral-500-rgb));
  --ds-action: var(--aq-orange);
  --ds-action-hover: #EA7E45;
  --ds-action-press: #C65A22;
  --ds-action-ink: #1B0F08;
  --ds-info: var(--aq-ocean);
  --ds-info-wash: rgba(93, 127, 166, 0.14);
  --ds-stable: var(--aq-sage);
  --ds-stable-text: rgb(var(--theme-accent-300-rgb));
  --ds-stable-wash: rgba(77, 90, 85, 0.16);
  --ds-premium: var(--aq-gold);
  --ds-premium-wash: rgba(200, 167, 122, 0.16);
  --ds-lavender: var(--aq-lavender);
  --ds-danger: #b8483a;
  --ds-danger-wash: rgba(184, 72, 58, 0.14);

  --ds-r-sm: 8px;
  --ds-r-md: 12px;
  --ds-r-lg: 20px;
  --ds-r-full: 9999px;

  --ds-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --ds-shadow-md: 0 8px 24px -8px rgba(0, 0, 0, 0.5);
  --ds-shadow-lg: 0 24px 56px -16px rgba(0, 0, 0, 0.65);

  --ds-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ds-dur: 180ms;
}

body[data-aquarium-theme="secondary"] {
  --theme-accent-100-rgb: 245, 239, 231; /* dourado, tom 100 */
  --theme-accent-200-rgb: 235, 223, 207; /* dourado, tom 200 */
  --theme-accent-300-rgb: 223, 204, 178; /* dourado, tom 300 */
  --theme-accent-400-rgb: 210, 183, 146; /* dourado, tom 400 */
  --theme-accent-rgb: 200, 167, 122; /* #C8A77A — Dourado */
  --theme-accent2-rgb: 126, 139, 132; /* #7E8B84 — Salvia clareada */
}

/* ─── Modo Dia ───────────────────────────────────────────────────────────
   O JS grava data-color-mode="day" no <html> (localStorage, ver
   applyColorMode em app.js) — no <html>, não no <body>: --cr-bg e as
   variáveis --cr-neutral-* / --theme-accent-* são declaradas no :root (o
   próprio <html>), e uma custom property herda como valor JÁ RESOLVIDO no
   ancestral onde foi calculada — se o atributo ficasse só no <body>, o
   :root nunca veria o override e --cr-bg continuaria congelado no tom da
   noite. A rampa simplesmente inverte (950↔50, 900↔100, 800↔200, 700↔300,
   600↔400, 500 fica no meio) — mesma paleta "Quiet Reef", só o outro lado
   dela; --cr-bg/--cr-surface-a/-b são literais (não derivam de var()),
   então precisam de valor próprio aqui também.

   O tom de acento (sálvia/dourado) precisa de rampa de texto separada: no
   modo noite, --theme-accent-100..400 são tons CLAROS (texto de acento
   sobre fundo escuro); no modo dia o mesmo texto precisa ser ESCURO pra
   continuar legível sobre um fundo creme. */
html[data-color-mode="day"] {
  color-scheme: light;
  --cr-bg: #E3DED4;
  --cr-surface-a: #EEEAE1;
  --cr-surface-b: #E6E0D4;
  --cr-neutral-50-rgb: 32, 37, 40;
  --cr-neutral-100-rgb: 38, 43, 45;
  --cr-neutral-200-rgb: 50, 54, 56;
  --cr-neutral-300-rgb: 66, 69, 70;
  --cr-neutral-400-rgb: 86, 88, 88;
  --cr-neutral-500-rgb: 114, 115, 113;
  --cr-neutral-600-rgb: 147, 146, 141;
  --cr-neutral-700-rgb: 187, 184, 177;
  --cr-neutral-800-rgb: 207, 203, 195;
  --cr-neutral-900-rgb: 221, 216, 207;
  --cr-neutral-950-rgb: 227, 222, 212;

  --theme-accent-100-rgb: 61, 72, 66; /* #3D4842 */
  --theme-accent-200-rgb: 70, 82, 75; /* #46524B */
  --theme-accent-300-rgb: 82, 95, 83; /* #525F53 */
  --theme-accent-400-rgb: 100, 112, 98; /* #647062 */
  --theme-accent-rgb: 90, 107, 94; /* #5A6B5E — Sálvia, aprofundada pro fundo claro */
  --theme-accent2-rgb: 140, 108, 63; /* #8C6C3F — Dourado escurecido */
}

html[data-color-mode="day"] body[data-aquarium-theme="secondary"] {
  --theme-accent-100-rgb: 92, 69, 34; /* #5C4522 */
  --theme-accent-200-rgb: 110, 83, 41; /* #6E5329 */
  --theme-accent-300-rgb: 131, 99, 51; /* #836333 */
  --theme-accent-400-rgb: 156, 121, 64; /* #9C7940 */
  --theme-accent-rgb: 166, 129, 63; /* #A6813F — Dourado aprofundado */
  --theme-accent2-rgb: 90, 107, 94; /* #5A6B5E — Sálvia escurecida */
}

/* #cycling-card e #feed-mode-card ainda usam gradiente proprio embutido
   (ambar/ciano sobre slate-900 puro do Tailwind, nao os tokens --cr-surface-*)
   -- fundo fixo escuro em qualquer tema, de proposito, pra continuar lendo
   como "alerta"/"modo ativo" mesmo de dia. O texto .text-slate-* de dentro,
   porem, segue os tokens --cr-neutral-* normais e inverteria pra escuro no
   modo dia, sumindo contra esse fundo que nunca clareia. Prende a rampa
   nos valores da noite so' dentro desses dois cards.

   #auth-gate entrou na mesma lista por um motivo um pouco diferente: o
   logo oficial (app/brand/nest-reef-logo.png) tem o wordmark "NEST REEF"
   num tom claro fixo, desenhado pra sentar sobre fundo escuro -- no modo
   dia, o resto da tela de entrada (que usa os mesmos tokens .bg-slate-* /
   .text-slate-* do app inteiro) clareava certinho, e o logo ficava quase
   ilegível por cima. Como essa tela é anterior ao login (o modo dia/noite
   é preferência de quem já está usando o app), prender ela sempre no
   visual da noite resolve sem precisar de uma segunda versão do logo. */
#cycling-card,
#feed-mode-card,
#auth-gate {
  --cr-neutral-50-rgb: 227, 222, 212;
  --cr-neutral-100-rgb: 221, 216, 207;
  --cr-neutral-200-rgb: 207, 203, 195;
  --cr-neutral-300-rgb: 187, 184, 177;
  --cr-neutral-400-rgb: 147, 146, 141;
  --cr-neutral-500-rgb: 114, 115, 113;
  --cr-neutral-600-rgb: 86, 88, 88;
  --cr-neutral-700-rgb: 66, 69, 70;
  --cr-neutral-800-rgb: 50, 54, 56;
  --cr-neutral-900-rgb: 38, 43, 45;
  --cr-neutral-950-rgb: 32, 37, 40;
}

body {
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background-color: var(--cr-bg);
  background-image:
    radial-gradient(circle at 15% 0%, rgba(var(--theme-accent-rgb), 0.08), transparent 38%),
    radial-gradient(circle at 85% 15%, rgba(var(--theme-accent2-rgb), 0.06), transparent 42%);
  background-attachment: fixed;
  transition: background-image 0.3s ease;
}

/* ─── fundo fotográfico por área (pedido da Andressa, 2026-09-14: fotos de
   coral reais no lugar do glow genérico, uma foto diferente por área do
   app, sempre atrás do cabeçalho/conteúdo inteiros, não só uma faixa no
   topo — ver referencias-visuais/inspiracao-externa/ pro lote original).
   `position: fixed; inset: 0` cobre o viewport inteiro por trás de
   cabeçalho + conteúdo como UMA superfície só (evita a costura que uma
   faixa só-no-topo criava entre cabeçalho e card); sem desfoque -- o
   degradê escuro por cima já garante legibilidade sem precisar borrar a
   foto. `body` continua só com o glow genérico como base -- toda tela
   real do app abaixo já tem seu próprio `::before` com foto própria. */
/* Ficha do aquário (view-aquarium-profile) entrou neste grupo em
   2026-09-16 -- é uma sub-tela de Meu Reef (aberta pela lista de lá),
   mesma foto de fundo da tela mãe, mesmo motivo do grupo do Lab logo
   abaixo (pedido da Andressa: "todas as abas do app devem ter o mesmo
   padrao"). */
#view-tests,
#view-aquarium-profile {
  position: relative;
}
#view-tests::before,
#view-aquarium-profile::before {
  content: '';
  position: fixed;
  inset: 0;
  background-size: cover;
  background-position: 30% 30%;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(180deg, rgba(10, 14, 18, 0.5) 0%, rgba(10, 14, 18, 0.72) 45%, rgba(10, 14, 18, 0.9) 100%),
    url('/backgrounds/meu-reef.jpg');
}
#view-tests > *,
#view-aquarium-profile > * {
  position: relative;
  z-index: 1;
}
/* Login (#auth-gate) é a tela inteira, não uma "faixa" no topo -- fundo
   cobre tudo, com o mesmo degradê escuro (mais forte no centro, onde fica
   o formulário). */
#auth-gate {
  background-image:
    linear-gradient(180deg, rgba(8, 14, 24, 0.55) 0%, rgba(8, 14, 24, 0.82) 100%),
    url('/backgrounds/login.jpg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* qualquer elemento cuja cor de marca dependa do tema do aquario ativo
   anima suavemente ao trocar de aquario, em vez de trocar a cor no corte. */
button.bg-cyan-500,
.reef-bottom-nav button[aria-current="page"],
#header-brand-icon,
[class*="cyan"] {
  transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
}

/* titulos de card, numeros e parametros pesados — aspecto de painel de dados
   tecnologico. Texto de postagem continua no peso normal, de proposito.
   Orbitron (ver @font-face acima) é a fonte de titulo/display oficial da
   marca; Bold (700) porque é um dos dois pesos que o guia de estilo lista
   pra titulo (o outro é Medium/500) e mantem o peso visual forte que os
   titulos ja tinham. Tracking positivo (em vez do -0.01em de antes) porque
   Orbitron pede espaçamento amplo pra nao parecer apertada -- o guia de
   estilo lista "espaçamento amplo (tracking)" como um dos tracos da marca. */
h1, h2, h3 {
  font-family: 'Orbitron', Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 700;
  letter-spacing: 0.03em;
}

/* cartoes com leve profundidade e brilho na borda no hover, pra reforcar
   a identidade "Quiet Reef" sem reescrever cada tela. */
#app-shell section,
#app-shell article {
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
#app-shell section:hover,
#app-shell article:hover {
  border-color: rgba(var(--theme-accent-rgb), 0.35);
}

.reef-bottom-nav button[aria-current="page"] {
  box-shadow: inset 0 0 0 1px rgba(var(--theme-accent-rgb), 0.4);
}

/* ─── cartões: "placa fosca", não painel de vidro/neon ────────────────────
   Pedido explícito (2026-08-26): menos card-card-card, menos brilho, mais
   minimalismo nas bordas — vibe Apple/PAX (objeto quieto, não gadget).
   Sai o gradiente metálico + blur + linha de luz; entra uma superfície
   sólida (--cr-surface-a) com uma borda-fio quase invisível. Cobre feed/
   galeria/fauna e qualquer cartão "grande" (perfil, ajustes, manutenção,
   calculadoras...) que use o padrão rounded-3xl + border-slate-800, sem
   precisar editar tela por tela. */
#community-feed > article,
.rounded-3xl.border-slate-800:not(.bg-gradient-to-br) {
  background-color: var(--cr-surface-a);
  border-color: rgba(var(--cr-border-rgb), 0.16) !important;
}

/* Sem moldura de card em nenhuma tela (pedido da Andressa, 2026-09-14:
   "quero todo o app no mesmo padrão... sem card, mantenha só os
   necessários de conteúdo") -- reaproveita o MESMO gancho de seletor da
   regra "placa fosca" acima (`rounded-3xl border-slate-800`, já usado em
   todo cartão "grande" do app) em vez de duplicar; `.rounded-2xl.border-
   slate-800` é o outro padrão de card (menor) do app, entrando aqui pela
   primeira vez. Fundo e borda ficam totalmente transparentes -- seguro
   porque campo de formulário de verdade (input/textarea/select) usa
   `border-slate-700`, não `border-slate-800`, então nenhum campo onde a
   pessoa precisa ver o contorno pra saber que dá pra tocar/digitar é
   afetado por esta regra; só os blocos "de texto" (informativos, sem
   input dentro) perdem a moldura.

   EXCLUSÃO: `:not([role="dialog"] *)` -- modais/diálogos (registrar
   medição, adicionar equipamento, qualquer folha por cima da tela)
   reaproveitam essas mesmas classes pro próprio painel, e precisam
   continuar opacos -- um modal transparente mostrando a foto atrás
   ficaria ilegível; "fluido" vale pra navegar entre telas, não pra
   elemento flutuante por cima de uma. */
.rounded-3xl.border-slate-800:not(.bg-gradient-to-br):not([role="dialog"] *),
.rounded-2xl.border-slate-800:not([role="dialog"] *) {
  background-color: transparent !important;
  border-color: transparent !important;
}

/* botão de ação primária: toque tátil no clique (encolhe), sem acender —
   um clique físico, não um painel de energia. */
button:active {
  transform: scale(0.97);
}
button.bg-cyan-500 {
  transition: transform 0.12s ease, opacity 0.12s ease, background-color 0.3s ease;
}

#app-top-header h1 {
  transition: opacity 0.15s ease;
  /* Orbitron ja vem do h1, h2, h3 acima -- aqui so' o ajuste fino que o
     nome da marca merece por cima do titulo generico: peso Medium (mais
     proximo do traço fino do wordmark real do que o Bold usado no resto
     dos titulos) e tracking bem mais largo (o wordmark oficial tem
     bastante respiro entre as letras). */
  font-weight: 500;
  letter-spacing: 0.2em;
}

/* barra de rolagem discreta, no lugar da padrao do sistema */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: rgba(60, 60, 60, 0.8);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::selection {
  background: rgba(var(--theme-accent-rgb), 0.35);
  color: #ffffff;
}

/* barra de navegacao flutuante, tipo HUD: vidro fosco sobre o feed, sem
   encostar nas bordas da tela. O feed rola por baixo dela — por isso o
   .app-shell abaixo ganha padding-bottom generoso, maior que a barra +
   margem de seguranca, senao o ultimo card do feed ficaria escondido atras
   do vidro. */
.reef-bottom-nav {
  left: 0.75rem;
  right: 0.75rem;
  bottom: calc(0.75rem + env(safe-area-inset-bottom));
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 1.5rem;
  background: rgba(5, 12, 23, 0.55);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  box-shadow: 0 8px 32px -8px rgba(0, 0, 0, 0.6);
}

/* Cabecalho fixo (pedido da Andressa, 2026-08-24): banda cheia (left/right:0,
   sem as margens flutuantes do .reef-bottom-nav) porque o cabecalho sempre
   se leu como parte do topo da pagina, nao um HUD solto por cima do
   conteudo. O alinhamento com o conteudo abaixo (mesmo max-w-6xl, mesmo
   padding lateral de .app-shell) vem do wrapper "mx-auto max-w-6xl" dentro
   do header no index.html -- o proprio <header> so' fornece a faixa de cor
   full-bleed e o padding vertical (com folga extra pro notch/status bar). */
#app-top-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 45;
  padding: calc(0.75rem + env(safe-area-inset-top)) 1rem 0.6rem;
  background: rgb(var(--cr-neutral-950-rgb) / 0.92);
  backdrop-filter: blur(15px);
  -webkit-backdrop-filter: blur(15px);
  border-bottom: 1px solid rgba(var(--cr-border-rgb), 0.16);
}

/* Cabeçalho fixo removido de vez (pedido da Andressa, 2026-09-14: "retire
   todo o cabeçalho e coloque o título da página embaixo do ícone da barra
   de baixo -- não precisa mais do logo nessas abas"). O nome/ícone da
   marca não precisa mais aparecer em toda tela -- quem abriu o app já
   sabe que app é esse. `!important` porque a regra `#app-top-header {
   display: grid; ... }` mais abaixo neste arquivo tem a mesma
   especificidade (só id) e viria depois na cascata. */
#app-top-header {
  display: none !important;
}
/* Título de cada aba embaixo do próprio ícone, na barra de navegação --
   único lugar que resta pra dizer em qual aba a pessoa está, sem
   cabeçalho no topo. `content` via `::after` em vez de marcação nova no
   HTML. */
.reef-bottom-nav .grid button {
  flex-direction: column;
  gap: 0.2rem;
}
.reef-bottom-nav .grid button::after {
  font-size: 0.62rem;
  font-weight: 600;
  line-height: 1;
}
#nav-profile::after {
  content: 'Perfil';
}
#nav-tests::after {
  content: 'Meu Reef';
}
#nav-community::after {
  content: 'Feed';
}
#nav-settings::after {
  content: 'Lab';
}
.reef-bottom-nav button {
  min-height: 3.5rem !important;
}
/* Sem cabeçalho, o respiro que `.app-primary-view`/`.app-shell` reservavam
   pra não ficar embaixo dele (`calc(var(--header-height) + 0.75rem)`, ou
   os 3rem fixos que Meu Reef/Lab herdavam) deixa de fazer sentido -- fica
   só `env(safe-area-inset-top)`, a margem de segurança real do aparelho
   (notch/status bar; 0 quando não existe). `!important` porque várias
   telas têm override próprio por id, mais específico que a classe. */
.app-primary-view,
.app-shell {
  padding-top: env(safe-area-inset-top) !important;
}

/* Rede de seguranca: telas "fixed inset-0" que cobrem a viewport inteira
   (Estudos, visualizador de foto) tem sua propria barra superior, diferente
   dos demais dialogs (que sao folhas com items-end/sm:p-6, nunca coladas na
   borda real da tela). Sem esta classe, essa barra comecava exatamente em
   top:0 -- com viewport-fit=cover, a barra do sistema (notch/status bar de
   um PWA instalado) fica sobreposta a ela, e o botao de voltar/fechar
   entra embaixo da area tocavel do sistema, virando um botao que existe
   na tela mas nao responde a toque. Foi o que aconteceu com a seta de
   voltar de Estudos. Adicione esta classe em qualquer nova tela feita
   assim (fixed inset-0, barra colada no topo). O 1.25rem (em vez do 0.75rem
   do resto do padding vertical) e' folga extra pedida depois do primeiro
   ajuste -- o botao ainda parecia perto demais da borda mesmo ja fora da
   area do notch. */
.fullbleed-dialog-topbar {
  padding-top: calc(1.25rem + env(safe-area-inset-top));
}

/* Empurra o resto de #app-shell pra fora de baixo do cabecalho fixo, que
   deixou de ocupar espaco no fluxo normal. --header-height e' a altura real
   medida (ver :root e syncHeaderHeight em app.js) -- nao um numero fixo,
   porque o cabecalho muda de altura conforme chip de aquario/ticker
   aparecem ou somem.
   NAO aumentar este respiro (tentativa revertida em 2026-09-12): #app-shell
   fica no fluxo normal o tempo todo, mesmo quando a tela ativa e' uma
   .app-primary-view IRMA dele (Perfil/Feed/Configuração) -- aumentar o
   padding-top aqui infla uma "caixa fantasma" vazia antes de QUALQUER
   tela, nao so' Meu Reef/Lab. A margem de seguranca extra pedida pela
   Andressa foi pra .app-shell .app-primary-view abaixo, que so' afeta
   quem e' filha de #app-shell de verdade. */
.app-shell {
  padding-top: calc(var(--header-height) + 0.75rem);
}

/* Abas do Feed (Início/Buscar/Chat, #feed-tabs no index.html): ocupam o
   MESMO lugar do .reef-bottom-nav (0.75rem da base) -- o menu principal
   (Meu Reef/Feed/Lab) fica escondido enquanto o Feed está ativo (ver
   "body.is-feed-view" mais abaixo), então não há mais duas barras
   flutuantes empilhadas, só uma. Antes disto mudar, as abas ficavam
   grudadas 5.625rem acima do menu principal, que continuava visível por
   baixo -- gerava duas barras e mais poluição visual do que uma rede
   social de verdade costuma ter. */
.feed-tabs-dock {
  position: fixed;
  left: 0.75rem;
  right: 0.75rem;
  bottom: calc(0.75rem + env(safe-area-inset-bottom));
  z-index: 40;
}

/* Botão flutuante "+" de nova publicação -- mesmo raciocinio de
   position:fixed do .feed-tabs-dock acima (ver comentário no index.html
   sobre containing block/transform). Fica acima do dock de abas, não em
   cima dele. */
.feed-compose-fab {
  position: fixed;
  right: 1rem;
  bottom: calc(5.25rem + env(safe-area-inset-bottom));
  z-index: 40;
  background: rgb(var(--theme-accent-rgb));
  box-shadow: 0 10px 24px -6px rgba(var(--theme-accent-rgb), 0.55);
}
.feed-compose-fab.hidden {
  display: none;
}
.feed-compose-fab:active {
  transform: scale(0.93);
}
/* Sobe temporariamente pra cima do lembrete de instalar o app
   (#install-reminder), que também mora no canto inferior direito e é largo
   o bastante (calc(100% - 2rem)) pra tapar o botão em vez de só disputar
   espaço com ele -- ver o MutationObserver que alterna esta classe em
   app.js, perto de collapsePostComposer/expandPostComposer. */
.feed-compose-fab.feed-compose-fab--above-reminder {
  bottom: calc(12.5rem + env(safe-area-inset-bottom));
}

/* Só uma barra flutuante por vez agora (ver comentário acima) -- mesmo
   respiro do padrão de .app-primary-view, não precisa do dobro que
   duas barras empilhadas exigiam antes. */
#view-community {
  padding-bottom: calc(7.5rem + env(safe-area-inset-bottom));
}

/* ─── Modo imersivo do Feed ───────────────────────────────────────────────
   Ativado por app.js (navigateToView) só quando a Comunidade é a view
   ativa, trocando "body.classList.toggle('is-feed-view', ...)". Esconde o
   cromo global que faz sentido em Meu Reef/Lab (identidade do app, atalho
   de perfil, seletor de aquário, guia de IA) mas só atrapalha a imersão de
   uma tela que quer se parecer com uma rede social de verdade, não mais
   uma aba do app de aquarismo. Nada disso é removido de fato -- some só
   enquanto o Feed está aberto, e volta sozinho ao sair (a troca de classe
   acontece a cada navegação, não é um estado que gruda). O botão de
   convite do cabeçalho não está mais nessa lista: saiu do app inteiro a
   pedido do André (2026-08-25), não só do Feed.

   Seletor por id (ver convenção no topo deste arquivo) garante que a
   escondida vence qualquer classe utilitária que .js já tenha aplicado
   nesses elementos (ex.: o "inline-flex" que renderAquariumSelector põe no
   seletor de aquário quando há mais de um aquário). */
body.is-feed-view #app-top-header h1,
body.is-feed-view #header-brand-icon,
body.is-feed-view #global-aquarium-switcher,
body.is-feed-view .reef-bottom-nav {
  display: none;
}

/* ─── remapeamento de cor "Quiet Reef": ciano vira a cor de marca do tema
   ativo (Salvia clareada no aquario primario, Dourado a partir do segundo
   aquario — ver variaveis --theme-accent-* no :root), rose/esmeralda/ambar
   ficam retonados pra um vermelho tijolo, verde musgo e dourado que
   combinam com a paleta nova sem perder a leitura imediata de
   critico/saudavel/atencao — cobre os componentes ja construidos (botoes,
   badges, aneis de saude, abas) sem precisar trocar classe por classe em
   cada tela. Rose, esmeralda e ambar ficam fixos: sao cores de status e
   precisam significar sempre a mesma coisa, independente do aquario
   selecionado ou do tom de marca do momento. */
.text-cyan-100 { color: rgb(var(--theme-accent-100-rgb)) !important; }
.text-cyan-200 { color: rgb(var(--theme-accent-200-rgb)) !important; }
.text-cyan-300 { color: rgb(var(--theme-accent-300-rgb)) !important; }
.text-cyan-300\/80 { color: rgba(var(--theme-accent-300-rgb), 0.8) !important; }
.text-cyan-400 { color: rgb(var(--theme-accent-400-rgb)) !important; }
.bg-cyan-400 { background-color: rgb(var(--theme-accent-400-rgb)) !important; }
.bg-cyan-500 { background-color: rgb(var(--theme-accent-rgb)) !important; }
.bg-cyan-500\/10 { background-color: rgba(var(--theme-accent-rgb), 0.1) !important; }
.bg-cyan-500\/20 { background-color: rgba(var(--theme-accent-rgb), 0.2) !important; }
.bg-cyan-500\/5 { background-color: rgba(var(--theme-accent-rgb), 0.05) !important; }
.bg-cyan-500\/15 { background-color: rgba(var(--theme-accent-rgb), 0.15) !important; }
.border-cyan-400 { border-color: rgb(var(--theme-accent-400-rgb)) !important; }
.border-cyan-500 { border-color: rgb(var(--theme-accent-rgb)) !important; }
.border-cyan-500\/30 { border-color: rgba(var(--theme-accent-rgb), 0.3) !important; }
.border-cyan-500\/25 { border-color: rgba(var(--theme-accent-rgb), 0.25) !important; }
.border-cyan-500\/40 { border-color: rgba(var(--theme-accent-rgb), 0.4) !important; }
.border-cyan-500\/20 { border-color: rgba(var(--theme-accent-rgb), 0.2) !important; }
.border-cyan-500\/50 { border-color: rgba(var(--theme-accent-rgb), 0.5) !important; }
.accent-cyan-500 { accent-color: rgb(var(--theme-accent-rgb)) !important; }

.text-rose-100 { color: #f5ddd6 !important; }
.text-rose-200 { color: #eab6a8 !important; }
.text-rose-300 { color: #e0917d !important; }
.text-rose-300\/80 { color: rgba(224, 145, 125, 0.8) !important; }
.text-rose-400 { color: #d16e52 !important; }
.bg-rose-500 { background-color: #b8483a !important; }
.bg-rose-500\/10 { background-color: rgba(184, 72, 58, 0.1) !important; }
.bg-rose-500\/20 { background-color: rgba(184, 72, 58, 0.2) !important; }
.bg-rose-500\/5 { background-color: rgba(184, 72, 58, 0.05) !important; }
.border-rose-500 { border-color: #b8483a !important; }
.border-rose-500\/30 { border-color: rgba(184, 72, 58, 0.3) !important; }
.border-rose-500\/40 { border-color: rgba(184, 72, 58, 0.4) !important; }
.border-rose-500\/20 { border-color: rgba(184, 72, 58, 0.2) !important; }

.text-emerald-100 { color: #dcebd2 !important; }
.text-emerald-200 { color: #badcac !important; }
.text-emerald-300 { color: #9cbe8a !important; }
.text-emerald-300\/80 { color: rgba(156, 190, 138, 0.8) !important; }
.text-emerald-400 { color: #82a66e !important; }
.bg-emerald-300 { background-color: #9cbe8a !important; }
.bg-emerald-400 { background-color: #82a66e !important; }
.bg-emerald-500 { background-color: #6b8f5a !important; }
.bg-emerald-500\/10 { background-color: rgba(107, 143, 90, 0.1) !important; }
.bg-emerald-500\/20 { background-color: rgba(107, 143, 90, 0.2) !important; }
.bg-emerald-500\/5 { background-color: rgba(107, 143, 90, 0.05) !important; }
.border-emerald-400\/30 { border-color: rgba(130, 166, 110, 0.3) !important; }
.border-emerald-500 { border-color: #6b8f5a !important; }
.border-emerald-500\/30 { border-color: rgba(107, 143, 90, 0.3) !important; }
.border-emerald-500\/40 { border-color: rgba(107, 143, 90, 0.4) !important; }
.border-emerald-500\/20 { border-color: rgba(107, 143, 90, 0.2) !important; }

.text-amber-100 { color: #fbefd9 !important; }
.text-amber-200 { color: #f5dbae !important; }
.text-amber-300 { color: #e3b15c !important; }
.text-amber-400 { color: #d69f42 !important; }
.bg-amber-500\/10 { background-color: rgba(201, 138, 43, 0.1) !important; }
.bg-amber-500\/20 { background-color: rgba(201, 138, 43, 0.2) !important; }
.bg-amber-500\/5 { background-color: rgba(201, 138, 43, 0.05) !important; }
.bg-amber-600\/10 { background-color: rgba(201, 138, 43, 0.1) !important; }
.border-amber-500\/20 { border-color: rgba(201, 138, 43, 0.2) !important; }
.border-amber-500\/40 { border-color: rgba(201, 138, 43, 0.4) !important; }
.border-amber-500\/30 { border-color: rgba(201, 138, 43, 0.3) !important; }
.shadow-amber-400\/50 { --tw-shadow-color: rgba(227, 177, 92, 0.5) !important; --tw-shadow: var(--tw-shadow-colored) !important; }

/* ─── remapeamento de neutros "Quiet Reef": o cinza-azulado frio do
   Tailwind (slate) usado em texto/fundo/borda em todo o app vira um
   neutro quente (rampa creme -> carvao, --cr-neutral-*-rgb no :root),
   coerente com a paleta nova. Borda ganha uma leitura propria: em vez do
   slate original, usa a lavanda (--cr-border-rgb) em opacidade baixa, o
   unico papel dela nesta proposta. So cobre as combinacoes
   classe+opacidade realmente usadas em app/index.html e app/app.js (grep,
   nao suposicao) — mesma tecnica do bloco de cyan/rose/emerald/amber
   acima. Sombra (shadow-slate-*), gradiente (from/to-slate-*) e
   placeholder ficam de fora de proposito: sao efeitos de opacidade quase
   preta, a diferenca entre frio/quente ali e imperceptivel, e cobrir
   tambem aumentaria o raio de mudanca sem ganho visual real. */
.text-slate-50 { color: rgb(var(--cr-neutral-50-rgb)) !important; }
.text-slate-100 { color: rgb(var(--cr-neutral-100-rgb)) !important; }
.text-slate-200 { color: rgb(var(--cr-neutral-200-rgb)) !important; }
.text-slate-300 { color: rgb(var(--cr-neutral-300-rgb)) !important; }
.text-slate-300\/90 { color: rgba(var(--cr-neutral-300-rgb), 0.9) !important; }
.text-slate-400 { color: rgb(var(--cr-neutral-400-rgb)) !important; }
.text-slate-500 { color: rgb(var(--cr-neutral-500-rgb)) !important; }
.text-slate-600 { color: rgb(var(--cr-neutral-600-rgb)) !important; }
.text-slate-700 { color: rgb(var(--cr-neutral-700-rgb)) !important; }
.text-slate-800 { color: rgb(var(--cr-neutral-800-rgb)) !important; }
.text-slate-950 { color: rgb(var(--cr-ink-fixed-rgb)) !important; }

.bg-slate-600\/80 { background-color: rgba(var(--cr-neutral-600-rgb), 0.8) !important; }
.bg-slate-700 { background-color: rgb(var(--cr-neutral-700-rgb)) !important; }
.bg-slate-800 { background-color: rgb(var(--cr-neutral-800-rgb)) !important; }
.bg-slate-800\/40 { background-color: rgba(var(--cr-neutral-800-rgb), 0.4) !important; }
.bg-slate-800\/60 { background-color: rgba(var(--cr-neutral-800-rgb), 0.6) !important; }
.bg-slate-800\/70 { background-color: rgba(var(--cr-neutral-800-rgb), 0.7) !important; }
.bg-slate-800\/80 { background-color: rgba(var(--cr-neutral-800-rgb), 0.8) !important; }
.bg-slate-900 { background-color: rgb(var(--cr-neutral-900-rgb)) !important; }
.bg-slate-900\/50 { background-color: rgba(var(--cr-neutral-900-rgb), 0.5) !important; }
/* Cards de conteúdo mais transparentes (pedido da Andressa, 2026-09-07:
   "o card da bio está muito branco/opaco, aumente a transparência...
   aplique o mesmo para todos os cards") -- bg-slate-900/60 é usado só por
   painéis de conteúdo (formulários, caixas de resultado, abas, a bio),
   nunca por modal/overlay que precise ficar sólido pra legibilidade (esses
   usam outros valores, ex. bg-slate-950/95 nos diálogos fixed inset-0),
   então reduzir aqui não compromete nada por cima do que precisa ficar
   opaco. */
.bg-slate-900\/60 { background-color: rgba(var(--cr-neutral-900-rgb), 0.32) !important; }
/* Variante :focus da mesma classe (usada no card da bio, que só aparece
   enquanto a pessoa escreve -- ver #public-bio em index.html) precisa do
   mesmo valor, senão herda o 0.6 padrão do Tailwind sem passar pelo
   remapeamento acima. */
.focus\:bg-slate-900\/60:focus { background-color: rgba(var(--cr-neutral-900-rgb), 0.32) !important; }
.bg-slate-900\/70 { background-color: rgba(var(--cr-neutral-900-rgb), 0.7) !important; }
.bg-slate-900\/80 { background-color: rgba(var(--cr-neutral-900-rgb), 0.8) !important; }
.bg-slate-900\/90 { background-color: rgba(var(--cr-neutral-900-rgb), 0.9) !important; }
.bg-slate-900\/95 { background-color: rgba(var(--cr-neutral-900-rgb), 0.95) !important; }
.bg-slate-950 { background-color: rgb(var(--cr-neutral-950-rgb)) !important; }
.bg-slate-950\/50 { background-color: rgba(var(--cr-neutral-950-rgb), 0.5) !important; }
.bg-slate-950\/60 { background-color: rgba(var(--cr-neutral-950-rgb), 0.6) !important; }
.bg-slate-950\/70 { background-color: rgba(var(--cr-neutral-950-rgb), 0.7) !important; }
/* Só pros cards de conteúdo que usavam bg-slate-950/70 (pedido da Andressa,
   2026-09-07, mesmo motivo do bg-slate-900/60 acima) -- os 4 usos de
   bg-slate-950/70 que são modal/overlay (onboarding, seletor de data,
   seletor de opção, upload de foto) continuam em /70 de propósito, pra não
   perder legibilidade por cima do conteúdo que cobrem. */
.bg-slate-950\/38 { background-color: rgba(var(--cr-neutral-950-rgb), 0.38) !important; }
.bg-slate-950\/80 { background-color: rgba(var(--cr-neutral-950-rgb), 0.8) !important; }
.bg-slate-950\/85 { background-color: rgba(var(--cr-neutral-950-rgb), 0.85) !important; }
.bg-slate-950\/88 { background-color: rgba(var(--cr-neutral-950-rgb), 0.88) !important; }
.bg-slate-950\/90 { background-color: rgba(var(--cr-neutral-950-rgb), 0.9) !important; }
.bg-slate-950\/92 { background-color: rgba(var(--cr-neutral-950-rgb), 0.92) !important; }
.bg-slate-950\/95 { background-color: rgba(var(--cr-neutral-950-rgb), 0.95) !important; }

.border-slate-600 { border-color: rgba(var(--cr-border-rgb), 0.55) !important; }
.border-slate-700 { border-color: rgba(var(--cr-border-rgb), 0.45) !important; }
.border-slate-700\/70 { border-color: rgba(var(--cr-border-rgb), 0.32) !important; }
.border-slate-800 { border-color: rgba(var(--cr-border-rgb), 0.35) !important; }
.border-slate-800\/50 { border-color: rgba(var(--cr-border-rgb), 0.18) !important; }
.border-slate-800\/60 { border-color: rgba(var(--cr-border-rgb), 0.21) !important; }
.border-slate-800\/70 { border-color: rgba(var(--cr-border-rgb), 0.25) !important; }
.border-slate-800\/80 { border-color: rgba(var(--cr-border-rgb), 0.28) !important; }

* {
  box-sizing: border-box;
}

button,
input,
select,
textarea {
  max-width: 100%;
}

#app-shell,
#auth-gate {
  overflow-x: hidden;
}

#app-shell section,
#app-shell article,
#auth-gate article,
#auth-gate section,
#pump-grid > article,
#community-feed > article {
  min-width: 0;
}

/* viewport-fit=cover estende a pagina por baixo do notch/ilha dinamica e da
   barra de status — o respiro pro cabecalho agora fica na regra
   #app-top-header / .app-shell logo acima (cabecalho fixo, ver comentario
   la'). Aqui so' sobra o respiro equivalente pro auth-gate, que nao tem
   cabecalho fixo proprio. */
#auth-gate {
  padding-top: calc(1.5rem + env(safe-area-inset-top));
  padding-bottom: calc(1.5rem + env(safe-area-inset-bottom));
}

/* Correção 2026-09-09 (pedido da Andressa -- card de seleção de aquário
   escondido atrás do cabeçalho em Meu Reef): o comentário antigo aqui dizia
   que TODA .app-primary-view é irmã de #app-shell, nunca filha -- só é
   verdade pra Perfil/Feed/Configuração; Meu Reef e as telas do Lab são
   filhas de #app-shell de verdade (herdam o padding-top dele, ver regra
   .app-shell acima). As duas famílias conviviam com sorte: quem é filha
   ficava certa só por acidente de DOM, quem é irmã (Perfil/Configuração)
   nunca teve respiro nenhum pro cabeçalho fixo -- só o Feed tinha rede de
   segurança própria (body.is-feed-view escondendo o cromo do cabeçalho).
   Dar o padding-top AQUI, em toda .app-primary-view, resolve as duas de
   uma vez só -- e a regra seguinte cancela a duplicação pra quem já ganha
   o respiro do .app-shell ancestral, pra não empurrar o conteúdo duas
   vezes pra baixo. */
.app-primary-view {
  min-height: calc(100dvh - 10rem - env(safe-area-inset-bottom));
  padding-top: calc(var(--header-height) + 0.75rem);
  padding-bottom: calc(7.5rem + env(safe-area-inset-bottom));
}

/* Filha real de #app-shell (Meu Reef, telas do Lab): o ancestral já reserva
   --header-height + 0.75rem antes de qualquer view aparecer -- aqui só
   falta o respiro extra, não a altura do cabeçalho de novo.
   0.75rem -> 3rem (2026-09-12, pedido da Andressa: nome do aquário em Meu
   Reef ainda aparecia coberto pelo cabeçalho mesmo depois da remedição em
   pageshow/visibilitychange -- ver syncHeaderHeight em app.js) -- margem de
   segurança maior contra imprecisão de medida (ex.: env(safe-area-inset-top)
   recalculado pelo iOS num instante em que o listener ainda não rodou de
   novo). Só aqui, não em .app-shell/.app-primary-view acima -- aquelas
   regras valem pra TODA tela, inclusive Perfil/Feed/Configuração, que nunca
   tiveram esse bug relatado; #app-shell fica no fluxo o tempo todo mesmo
   quando a tela ativa é uma dessas irmãs, então aumentar o respiro lá
   infla uma "caixa fantasma" vazia antes de qualquer tela, não só Meu
   Reef/Lab (foi exatamente esse o efeito colateral encontrado e revertido
   nesta mesma correção). */
.app-shell .app-primary-view {
  padding-top: 3rem;
}

/* Botão voltar discreto: só a seta, sem pílula/borda -- o próprio "‹" já
   indica a ação, texto ("Voltar") é redundante. Existe só ESTE componente
   de "voltar" em cada janela do app (nunca mais de um por tela): o JS em
   navigateToView já garante isso escondendo TODOS os `[data-view-back]`
   juntos na tela inicial (não tem pra onde voltar ali) -- nenhuma tela
   deveria ganhar um segundo botão "voltar" próprio por fora deste, mesmo
   que pareça mais específico (ex.: "Voltar pro X"); ver Feed, que tinha um
   assim e foi removido por ser exatamente essa duplicata. min-width/height
   mantêm a área de toque de ~2.25rem mesmo com o visual mínimo. */
/* position: sticky (correção 2026-08-28, pedido da Andressa: a seta
   precisa continuar acessível mesmo depois de rolar a tela pra baixo, não
   só no topo).
   `top: 0.5rem` (correção 2026-08-29, substituiu
   `calc(var(--header-height) + 0.5rem)`): a area de conteudo (onde este
   botao mora) ja COMEÇA logo abaixo do cabeçalho fixo, por causa do
   padding-top de .app-shell -- somar --header-height de novo aqui contava
   o cabeçalho duas vezes e empurrava o botão pra BAIXO do próprio título
   da tela. Bug real, não só capricho visual: a seta ficava desenhada em
   cima do "Lab"/"Parâmetros"/etc., tornando os dois difíceis de tocar
   certo.
   OBS pra próxima pessoa que mexer aqui: o "position: sticky" em si não
   está realmente grudando durante a rolagem de verdade -- #app-shell só
   declara `overflow-x: hidden`, mas isso faz o CSS computar
   `overflow-y: auto` sozinho (regra do próprio spec quando só um eixo vira
   não-visible), o que transforma #app-shell num "container de rolagem"
   aos olhos do position:sticky mesmo sem jamais precisar da própria barra
   -- e a seta acaba só rolando junto com a página em vez de grudar. Tirar
   `overflow-x: hidden` resolveria, mas esse hidden pode estar segurando
   algum vazamento horizontal que não foi possível confirmar nesta rodada;
   arrumar direito exige ou testar a remoção com calma em todas as telas,
   ou trocar este botão pra `position: fixed` (like o cabeçalho/nav já
   fazem) com o alinhamento horizontal replicado à parte. Por ora, `top:
   0.5rem` já resolve a sobreposição real (o caso mais comum: usuário abre
   a tela e nem rolou ainda) -- só não resolve o "continuar visível bem
   longe do topo depois de rolar bastante", que já estava quebrado antes
   desta correção também. */
.view-back-button {
  display: inline-flex;
  position: sticky;
  top: 0.5rem;
  z-index: 20;
  min-height: 2.25rem;
  min-width: 2.25rem;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.75rem;
  border-radius: 9999px;
  background: rgba(var(--cr-neutral-950-rgb), 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: rgba(var(--cr-neutral-300-rgb), 0.85);
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1;
  transition: background-color 160ms ease, color 160ms ease;
}
/* o JS marca .hidden quando nao ha pra onde voltar (ex.: na tela inicial) —
   mas o display: inline-flex acima tinha a mesma especificidade do .hidden
   do Tailwind e, carregando depois, sempre ganhava. O botao nunca ficava
   escondido de verdade, so a classe mudava. */
.view-back-button.hidden {
  display: none;
}

.view-back-button:hover,
.view-back-button:focus-visible {
  background: rgba(var(--cr-neutral-700-rgb), 0.55);
  color: rgb(var(--cr-neutral-50-rgb));
  outline: none;
}

/* Botoes-pilula pequenos (chips de periodo do grafico, seletor de aquario no
   cabecalho, "Ver previsao") usavam so px-2.5 py-1 text-xs -- ~24px de
   altura real no celular, abaixo do minimo confortavel de toque (mesmo
   padrao de ~36px ja usado em .view-back-button acima). Cresce a area de
   toque sem esticar o texto: flex + min-height, o padding da propria
   pilula continua controlando a largura. */
[role="group"][aria-label="Período do gráfico"] button,
#global-aquarium-switcher,
#reef-pulse-forecast-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.25rem;
}

.reef-bottom-nav button {
  min-width: 0;
  min-height: 2.75rem;
  border-radius: 0.75rem;
  padding: 0.45rem 0.35rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.15;
  text-align: center;
  overflow-wrap: anywhere;
  transition: background-color 160ms ease, color 160ms ease;
}

.reef-bottom-nav button:hover,
.reef-bottom-nav button:focus-visible {
  background: rgba(30, 41, 59, 0.9);
  outline: none;
}

/* barra de rolagem sempre visivel na conversa do Guia (nao so ao tocar,
   como o indicador nativo do celular) — pedido de quem testou: sem ela nao
   dava pra perceber que tinha mais mensagem acima/abaixo pra rolar. */
#guide-chat-history {
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--theme-accent-rgb), 0.55) transparent;
}
#guide-chat-history::-webkit-scrollbar {
  width: 5px;
}
#guide-chat-history::-webkit-scrollbar-thumb {
  background: rgba(var(--theme-accent-rgb), 0.55);
  border-radius: 999px;
  border: none;
}

#history-chart {
  width: 100% !important;
  height: 320px !important;
}
.slider-track {
  height: 10px;
  border-radius: 9999px;
}

.mini-ticker {
  min-width: 280px;
  max-width: 100%;
}

#app-top-header {
  display: grid;
  gap: 0.65rem;
}

#app-top-actions {
  min-width: 0;
}

#app-top-actions button {
  flex: 0 1 auto;
}

#quick-ticker {
  width: 100%;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
}

/*
 * O `.hidden` do Tailwind é seletor de classe; a regra acima é por id, e esta
 * folha carrega depois de tailwind.css. Por especificidade o id vencia, então
 * o `classList.add('hidden')` do JS não escondia nada: a faixa ficava desenhada
 * e vazia no alto da tela, empurrando o conteúdo para baixo.
 *
 * Ao estilizar por id aqui, repita isto sempre que o JS também esconder o
 * elemento.
 */
#quick-ticker.hidden {
  display: none;
}

#quick-ticker > span {
  min-width: 0;
}

#quick-ticker .text-slate-500 {
  display: none;
}

#dashboard-primary-actions {
  width: 100%;
  min-width: 0;
}

#dashboard-primary-actions > button {
  min-width: 0;
}

#dashboard-status-grid > div {
  min-width: 0;
}

#dashboard-status-grid p {
  overflow-wrap: anywhere;
}

/* auto-grid: cada card tem no minimo 150px, e se sobrar um numero impar de
   parametros (ex.: 5 em vez de 4) o(s) ultimo(s) da linha incompleta se
   expande(m) pra preencher o espaco, em vez de deixar um buraco alinhado a
   esquerda. CSS Grid com `repeat(auto-fit, minmax(...))` resolve isso so
   quando todos os cards cabem numa unica linha — com mais de uma linha (o
   caso comum no celular, onde so 2 cards cabem por linha), a coluna vazia da
   ultima linha incompleta nao colapsa. Flexbox com `flex-wrap` resolve para
   qualquer numero de linhas: cada linha redistribui o espaço so entre os
   itens que ela realmente tem. */
#pump-grid {
  display: flex;
  flex-wrap: wrap;
  /* explicito de proposito: cada linha do "auto-grid" estica os cards pra
     altura do maior vizinho (ja e o padrao do flexbox sem isto, mas deixar
     escrito evita que uma mudanca futura em align-items quebre o painel
     simetrico sem ninguem perceber por que). */
  align-items: stretch;
}

#pump-grid > article {
  flex: 1 1 150px;
  /* cabecalho, valor e selo ficam centralizados verticalmente dentro do
     card — como o card estica pra altura do maior vizinho da linha, um
     card com pouco conteudo nao fica com o texto "colado" no topo e um
     vazio embaixo; o conteudo ocupa o meio, como um painel de controle. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}

#community-feed button[data-action='open-author-profile'] {
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
}

#community-feed button[data-action='expand-photo'] {
  padding: 0;
  cursor: zoom-in;
}

#community-size-limit-enabled {
  cursor: pointer;
}

#community-media-ready-state {
  box-shadow: inset 0 0 0 1px rgba(16, 185, 129, 0.1);
}

#community-feed video,
#public-profile-posts video,
#community-media-ready-preview video {
  width: 100%;
  max-width: 100%;
  border-radius: 1rem;
  background: var(--cr-bg);
}

/* So a imagem ampliada do lightbox precisa disto — ela tem pan/zoom por
   JS (pointerdown em lightboxViewport) que depende do gesto de uma unica
   dedo nao virar scroll da pagina por baixo. Miniatura de feed nao tem esse
   JS: aplicar a mesma regra nelas bloqueava o scroll de quem tocava a foto
   pra rolar a tela, sem ganhar zoom nenhum em troca. */
#photo-lightbox-image {
  touch-action: pinch-zoom;
}

.community-post-kind {
  display: inline-flex;
  align-items: center;
  border-radius: 9999px;
  padding: 0.2rem 0.65rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.community-post-kind-text {
  border: 1px solid rgba(148, 163, 184, 0.35);
  background: rgba(15, 23, 42, 0.7);
  color: #cbd5e1;
}

.community-post-kind-image {
  border: 1px solid rgba(16, 185, 129, 0.45);
  background: rgba(16, 185, 129, 0.12);
  color: #a7f3d0;
}

.community-post-kind-video {
  border: 1px solid rgba(34, 211, 238, 0.45);
  background: rgba(34, 211, 238, 0.12);
  color: #a5f3fc;
}

#public-profile-posts > article img {
  max-width: 100%;
  height: auto;
}

#view-public-profile article,
#view-photo-lightbox article {
  min-width: 0;
}

@media (max-width: 640px) {
  .mini-ticker {
    width: 100%;
    justify-content: center;
  }

  #app-top-header {
    gap: 0.55rem;
  }

  /* Wordmark maiúsculo + tracking largo ocupa mais largura por caractere
     que o título antigo (peso 900, sem tracking) — precisa de um tamanho
     bem menor pra não brigar com os ícones do cabeçalho num telefone. */
  #app-top-header h1 {
    font-size: 1.15rem;
    line-height: 1.2;
  }

  #app-top-actions {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.5rem;
    width: 100%;
  }

  #app-top-actions > button {
    width: 100%;
    justify-self: stretch;
    text-align: center;
    min-height: 2.4rem;
  }

  #quick-ticker {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.4rem;
    border-radius: 1rem;
    padding: 0.55rem;
  }

  #quick-ticker > span {
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  #app-shell {
    padding-left: 0.75rem;
    padding-right: 0.75rem;
  }

  /* Mesma reducao de respiro lateral que #app-shell ganha aqui em telas
     estreitas -- o header fixo esta fora da arvore do #app-shell agora,
     entao precisa da propria regra pra continuar alinhado com o conteudo. */
  #app-top-header {
    padding-left: 0.75rem;
    padding-right: 0.75rem;
  }

  #view-lab > article,
  #view-tests > article,
  #view-profile > article,
  #view-community > article,
  #view-settings > article {
    padding: 1rem;
  }

  #dashboard-primary-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%;
    gap: 0.5rem;
  }

  #dashboard-primary-actions > button {
    width: 100%;
    min-height: 2.5rem;
    white-space: normal;
    line-height: 1.2;
    text-align: center;
  }

  #dashboard-status-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.6rem;
  }

  #dashboard-status-grid > div {
    padding: 0.75rem;
  }

  #view-community .mt-4.flex.flex-wrap.items-center.gap-3,
  #view-settings .flex.flex-wrap.items-center.gap-3,
  #view-profile .mt-4.flex.flex-wrap.items-center.gap-2,
  #view-profile .mt-5.flex.flex-wrap.items-center.gap-3 {
    display: grid;
    grid-template-columns: 1fr;
    width: 100%;
  }

  #view-community .mt-4.flex.flex-wrap.items-center.gap-3 > button,
  #view-settings .flex.flex-wrap.items-center.gap-3 > button,
  #view-profile .mt-4.flex.flex-wrap.items-center.gap-2 > button,
  #view-profile .mt-5.flex.flex-wrap.items-center.gap-3 > button {
    width: 100%;
    min-height: 2.5rem;
    text-align: center;
  }

  #view-community .flex.flex-wrap.gap-2.text-xs.text-slate-300,
  #view-profile .mt-3.flex.flex-wrap.items-center.gap-2,
  #view-settings .mt-4.grid.gap-4 {
    min-width: 0;
  }

  #view-community textarea,
  #view-profile textarea,
  #view-settings textarea,
  #view-settings input,
  #view-profile input,
  #view-community input {
    font-size: 16px;
  }

  #view-community img,
  #view-profile img {
    max-width: 100%;
    height: auto;
  }

  #view-public-profile {
    padding-left: 0;
    padding-right: 0;
  }

  #view-public-profile article {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  #public-profile-posts > article img {
    max-height: 230px;
    object-fit: cover;
  }

  /* gap subiu de 0.2rem pra 0.5rem (correção 2026-08-29, pedido da
     Andressa): com 0.2rem (3.2px) os botões ficavam praticamente colados,
     risco real de toque errado -- a barra tem espaço de sobra mesmo no
     celular mais estreito (iPhone SE, 320px) pra essa folga. */
  .reef-bottom-nav .mx-auto {
    gap: 0.5rem;
    padding-left: 0.25rem;
    padding-right: 0.25rem;
  }

  .reef-bottom-nav button {
    min-height: 3.25rem;
    padding: 0.35rem 0.15rem;
    font-size: 0.62rem;
    white-space: normal;
  }
}

@media (max-width: 420px) {
  #app-top-header h1 {
    font-size: 1rem;
  }

  #quick-ticker {
    grid-template-columns: 1fr;
  }

  #dashboard-primary-actions {
    grid-template-columns: 1fr;
  }

  #dashboard-status-grid {
    grid-template-columns: 1fr;
  }
}

@media (min-width: 641px) and (max-width: 1024px) {
  #dashboard-primary-actions > button,
  #view-community .mt-4.flex.flex-wrap.items-center.gap-3 > button,
  #view-settings .flex.flex-wrap.items-center.gap-3 > button {
    min-height: 2.4rem;
  }

  #dashboard-primary-actions {
    gap: 0.5rem;
  }

  #dashboard-status-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  #quick-ticker {
    justify-content: flex-start;
  }
}

@media (min-width: 640px) {
  .app-primary-view {
    padding-bottom: 6rem;
  }
}

/* ─── Feed edge-to-edge (imersivo, estilo TikTok/Instagram) ──────────────
   Posts com foto/video preenchem a largura do cartao "Feed da comunidade"
   sem a moldura lateral que os outros cards do app usam — a legenda e o
   autor moram dentro de um degrade escuro sobre a propria imagem, em vez de
   ocupar uma faixa de texto separada abaixo dela. */
/* cancela exatamente o padding do cartao "Feed da comunidade" (article),
   que muda de 1rem para 1.25rem no breakpoint sm — ver a mesma mudanca em
   "#view-community > article" dentro de @media (max-width: 640px) abaixo.
   Sem acompanhar o mesmo valor, a midia vazava alguns pixels pra fora da
   tela (ou ficava com uma faixa sobrando) dependendo da largura. */
#community-feed {
  margin-left: -1rem;
  margin-right: -1rem;
}

@media (min-width: 641px) {
  #community-feed {
    margin-left: -1.25rem;
    margin-right: -1.25rem;
  }
}

.feed-post-media {
  position: relative;
}

.feed-post-media img,
.feed-post-media video {
  display: block;
  aspect-ratio: 4 / 5;
  width: 100%;
  object-fit: cover;
}

.feed-post-scrim-top,
.feed-post-scrim-bottom {
  position: absolute;
  left: 0;
  right: 0;
  pointer-events: none;
}

.feed-post-scrim-top {
  top: 0;
  padding: 0.75rem;
  display: flex;
  justify-content: flex-end;
  background: linear-gradient(to bottom, rgba(2, 6, 16, 0.55), transparent);
}

.feed-post-scrim-top > * {
  pointer-events: auto;
}

.feed-post-scrim-bottom {
  bottom: 0;
  padding: 2.5rem 1rem 0.85rem;
  background: linear-gradient(to top, rgba(2, 6, 16, 0.88) 0%, rgba(2, 6, 16, 0.55) 55%, transparent 100%);
}

.feed-post-scrim-bottom > * {
  pointer-events: auto;
}

/* brilho explosivo de "curtir com dois toques", centralizado sobre a foto —
   some sozinho, nunca intercepta toque (pointer-events: none). */
.feed-like-pulse {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
}

.feed-like-pulse svg {
  width: 5.5rem;
  height: 5.5rem;
  filter: drop-shadow(0 0 1.25rem rgba(255, 64, 129, 0.85));
  color: var(--cr-blastomussa);
}

.feed-like-pulse.is-active {
  animation: reef-like-pulse 700ms ease-out;
}

@keyframes reef-like-pulse {
  0% {
    opacity: 0;
    transform: scale(0.35);
  }
  25% {
    opacity: 1;
    transform: scale(1.15);
  }
  60% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(1);
  }
}

/* rolagem com encaixe leve entre posts — "proximity", nao "mandatory", pra
   nao brigar com a rolagem livre do resto do app (so os posts declaram
   scroll-snap-align, entao nenhuma outra tela e afetada). */
html {
  scroll-snap-type: y proximity;
}

.feed-post {
  scroll-snap-align: start;
}

/* posts so-texto ganham tratamento tipografico "thread" (Threads/X): avatar
   e texto lado a lado, fonte maior, sem moldura de midia. */
.feed-post-thread p {
  font-size: 0.95rem;
  line-height: 1.55;
}

/* ─── "Meu Cantinho do Reef": textura de fundo do card principal do Perfil ──
   Sem foto real embutida (CSP so libera img-src 'self' data: blob:, uma URL
   externa nao carregaria mesmo). A textura e 100% CSS: raios de luz vindos
   de cima (como sol filtrando na agua) + manchas organicas nas cores da
   marca simulando coral/rocha ao fundo, tudo nas cores ja usadas no resto
   do app. O ultimo layer (linear-gradient solido a 60% de opacidade) e o
   "overlay escuro" pedido — mantem o texto branco 100% legivel por cima. */
/* Painel de tom FIXO (não segue o modo dia/noite) — tratado como uma
   "foto" da marca, com texto claro por cima (ver regra de
   #profile-hero h2/p logo abaixo), então a base e o overlay ficam sempre
   escuros nos dois modos. Cores atualizadas pra paleta "Quiet Reef"
   (sálvia/dourado) — antes usava hex literal de uma paleta anterior
   (terracota/dourado quentes da "Hermès Reef"). */
#profile-hero {
  position: relative;
  background-image:
    linear-gradient(rgba(var(--cr-ink-fixed-rgb), 0.6), rgba(var(--cr-ink-fixed-rgb), 0.6)),
    radial-gradient(ellipse 65% 45% at 18% -5%, rgba(var(--theme-accent-rgb), 0.22), transparent 60%),
    radial-gradient(ellipse 55% 40% at 85% 0%, rgba(var(--theme-accent2-rgb), 0.18), transparent 55%),
    radial-gradient(circle at 12% 95%, rgba(107, 143, 90, 0.16), transparent 42%),
    radial-gradient(circle at 88% 100%, rgba(var(--theme-accent2-rgb), 0.16), transparent 38%),
    radial-gradient(circle at 50% 105%, rgba(var(--cr-ink-fixed-rgb), 0.95), transparent 55%),
    linear-gradient(180deg, #262b2e 0%, #1e2326 55%, #14171a 100%);
  background-repeat: no-repeat;
  background-size: cover;
}

/* Mesmo raciocinio do bloco #cycling-card/#feed-mode-card mais abaixo: fundo
   fixo escuro nos dois modos, mas o texto .text-slate-* de dentro segue os
   tokens --cr-neutral-* normais e inverteria pra escuro no modo dia. Prende
   a rampa nos valores da noite so' dentro do card. */
#profile-hero {
  --cr-neutral-50-rgb: 227, 222, 212;
  --cr-neutral-100-rgb: 221, 216, 207;
  --cr-neutral-200-rgb: 207, 203, 195;
  --cr-neutral-300-rgb: 187, 184, 177;
  --cr-neutral-400-rgb: 147, 146, 141;
  --cr-neutral-500-rgb: 114, 115, 113;
  --cr-neutral-600-rgb: 86, 88, 88;
  --cr-neutral-700-rgb: 66, 69, 70;
  --cr-neutral-800-rgb: 50, 54, 56;
  --cr-neutral-900-rgb: 38, 43, 45;
  --cr-neutral-950-rgb: 32, 37, 40;
}

#profile-hero h2,
#profile-hero > div > p.text-slate-400 {
  color: #ece8e0 !important;
}

/* Foto de fundo do Perfil cobre a TELA inteira, não só o card-herói
   (pedido da Andressa: "quero que tenha uma imagem em todo o fundo, não
   apenas no card") -- mesma técnica das outras áreas (fixed + degradê,
   sem desfoque). #profile-hero (abaixo) perde o próprio gradiente opaco
   e os brilhos de cor da marca -- a foto já dá contraste e cor sozinha, e
   uma segunda camada de tom só no card criava uma costura visível bem na
   borda de cima dele. */
#view-profile {
  position: relative;
}
#view-profile::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(180deg, rgba(10, 14, 18, 0.5) 0%, rgba(10, 14, 18, 0.72) 45%, rgba(10, 14, 18, 0.9) 100%),
    url('/backgrounds/perfil.jpg');
  background-size: cover;
  background-position: 15% 40%;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 0;
}
#view-profile > * {
  position: relative;
  z-index: 1;
}
#profile-hero {
  background-image: none;
}
#profile-hero::before {
  content: none;
}

/* Mesma técnica, uma foto própria por área -- Lab e as sub-telas dele
   (Parâmetros/Manutenção/Inventário/Alimentação/Reef Explorer) contam como
   UMA área só (mesma navegação; trocar de foto a cada sub-tela ficaria
   estranho). Reef Explorer (view-lab-explore) e Ficha do aquário
   (view-aquarium-profile, no grupo do Meu Reef mais abaixo) entraram aqui
   em 2026-09-16 -- tinham nascido sem essa foto de fundo (pedido da
   Andressa: "todas as abas do app devem ter o mesmo padrao"), ficando
   visualmente "mais planas" que as telas irmãs da mesma seção. */
#view-lab,
#view-lab-parameters,
#view-lab-maintenance,
#view-lab-inventory,
#view-lab-feeding,
#view-lab-explore,
#view-lab-readings {
  position: relative;
}
#view-lab::before,
#view-lab-parameters::before,
#view-lab-maintenance::before,
#view-lab-inventory::before,
#view-lab-feeding::before,
#view-lab-explore::before,
#view-lab-readings::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(180deg, rgba(10, 14, 18, 0.5) 0%, rgba(10, 14, 18, 0.72) 45%, rgba(10, 14, 18, 0.9) 100%),
    url('/backgrounds/lab.jpg');
  background-size: cover;
  background-position: 10% 55%;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 0;
}
#view-lab > *,
#view-lab-parameters > *,
#view-lab-maintenance > *,
#view-lab-inventory > *,
#view-lab-feeding > *,
#view-lab-explore > *,
#view-lab-readings > * {
  position: relative;
  z-index: 1;
}
#view-community {
  position: relative;
}
#view-community::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(180deg, rgba(10, 14, 18, 0.5) 0%, rgba(10, 14, 18, 0.72) 45%, rgba(10, 14, 18, 0.9) 100%),
    url('/backgrounds/feed.jpg');
  background-size: cover;
  background-position: 85% 60%;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 0;
}
/* `:not(.feed-tabs-dock):not(.feed-compose-fab)` -- sem essa exclusão, o
   `> *` abaixo trocaria o `position: fixed` das abas Início/Buscar/Chat e
   do botão "+" flutuante (filhos diretos de #view-community) por
   `position: relative`, fazendo os dois "subirem" pro meio da tela em
   vez de ficarem fixos embaixo. */
#view-community > *:not(.feed-tabs-dock):not(.feed-compose-fab) {
  position: relative;
  z-index: 1;
}
#view-settings {
  position: relative;
}
#view-settings::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(180deg, rgba(10, 14, 18, 0.5) 0%, rgba(10, 14, 18, 0.72) 45%, rgba(10, 14, 18, 0.9) 100%),
    url('/backgrounds/configuracao.jpg');
  background-size: cover;
  background-position: 10% 65%;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 0;
}
#view-settings > * {
  position: relative;
  z-index: 1;
}

/* ─── o trilho deslizante dos antigos Ajustes (Aquario/Controlador/Conta)
   foi removido junto com as abas na reorganizacao Meu Reef/Feed/Lab — a
   tela "Configuração" que restou (so o antigo painel Aquario) e' uma
   pagina so, sem abas. O trilho de Manutenção/Inventário em Meu Reef
   (.water-panels-viewport/.water-panels-track/.water-panel-page/
   .water-tab-segment) foi removido do mesmo jeito na correcao de
   2026-08-24, quando os dois viraram destinos separados dentro de Lab >
   Água (ver view-lab-maintenance/view-lab-inventory em index.html) — sem
   tab nenhuma sobrando, nao fazia sentido manter o CSS do trilho. */

/* ─── mesmo padrao de abas deslizantes que existiu em Agua e em Ajustes
   (ambos removidos, ver comentario acima) — classes duplicadas de proposito
   (nunca compartilhadas entre as telas que tiveram esse padrao) pelo mesmo
   motivo: um ajuste numa tela nao arrisca quebrar as outras. Hoje o Feed
   (Início / Buscar / Chat) e' o unico lugar do app que ainda usa esse
   mecanismo. */

.feed-panels-viewport {
  overflow-x: hidden;
  touch-action: pan-y;
}

.feed-panels-track {
  display: flex;
  width: 300%;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}

.feed-panels-track.is-dragging {
  transition: none;
}

.feed-panel-page {
  width: calc(100% / 3);
  min-width: 0;
  flex-shrink: 0;
  /* Desvio deliberado do padrao de Agua/Ajustes: la os 3 paineis tem altura
     parecida (formularios), aqui nao -- o feed cronologico pode ter dezenas
     de posts enquanto Buscar/Chat tem pouca coisa. Sem isto, align-items:
     stretch (o padrao do flex) esticaria os paineis curtos ate a altura do
     mais alto, deixando uma faixa vazia enorme embaixo deles. */
  align-self: flex-start;
  /* Justamente por causa da diferenca de altura acima: o painel "Inicio"
     (bem mais alto que os outros dois) fica ao lado de um painel curto, e o
     translateX do track cai numa posicao fracionaria de pixel (largura do
     track nem sempre divide certinho pelo numero de paineis). Sem clipar
     cada pagina na propria borda, essa fracao de pixel deixa escapar um fio
     visivel do conteudo do painel vizinho por baixo da pagina ativa -- em
     algum lugar aleatorio da tela, sem nenhuma relacao com o que a pessoa
     esta vendo. Reproduzido e confirmado com overflow:hidden aqui. */
  overflow: hidden;
}

.feed-tab-segment {
  background: transparent;
  color: #94a3b8;
}

.feed-tab-segment.is-active {
  background: rgba(var(--theme-accent-rgb), 0.14);
  color: #ffffff;
  box-shadow: inset 0 -2px 0 0 rgb(var(--theme-accent-rgb));
}

/* ─── Biblioteca de ebooks ────────────────────────────────────────────────
   "Capa" do ebook na galeria: numero do volume sobre o icone de livro
   (svgIcon('book'), ver app/src/icons.js), como lombada de estante -- sem
   foto de estoque nem gradiente de marketing. */
.ebook-cover {
  display: flex;
  width: 3rem;
  height: 3.75rem;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  border-radius: 0.65rem;
  background: rgba(var(--cr-neutral-300-rgb), 0.1);
  color: rgb(var(--theme-accent-rgb));
  font-size: 0.7rem;
  font-weight: 700;
}

/* Espaco reservado para imagem dentro de uma pagina do leitor: nunca uma
   imagem inventada -- uma referencia visual central (icone + legenda do que
   deveria estar ali), ate a foto de verdade entrar no lugar. */
.ebook-image-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  border: 1px dashed rgba(var(--cr-neutral-300-rgb), 0.3);
  border-radius: 1rem;
  padding: 1.75rem 1.25rem;
  text-align: center;
  color: rgb(var(--cr-neutral-400-rgb));
}

.ebook-image-placeholder-label {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.ebook-image-placeholder-caption {
  max-width: 32rem;
  font-size: 0.75rem;
  line-height: 1.4;
  color: rgb(var(--cr-neutral-500-rgb));
}

/* Ilustração vetorial original (ver bloco.ilustracao em ebooks-data.js e
   renderEbookBlock em app.js) — desenhada nas cores do próprio logo da Nest
   Reef, não uma foto de banco de imagem. O SVG já pinta o próprio fundo
   (carvão), então só cabe aqui a moldura arredondada + a legenda embaixo. */
.ebook-illustration {
  overflow: hidden;
  border-radius: 1rem;
  border: 1px solid rgba(var(--cr-neutral-300-rgb), 0.14);
}

.ebook-illustration svg {
  display: block;
  width: 100%;
  height: auto;
}

.ebook-illustration .ebook-image-placeholder-caption {
  padding: 0 0.25rem;
}

/* ═══ Tour guiado (onboarding) ═══════════════════════════════════════════
   Balão de passo a passo — ver o comentário no topo de
   app/src/onboarding-tour.js pra como a posição é calculada e como isto
   sobrevive a uma troca de identidade visual. Resumo pra quem for mexer
   aqui: o balão e o botão primário já usam classes Tailwind remapeadas
   (.bg-cyan-500, .border-slate-700 etc., ver bloco de remapeamento acima) —
   só o ANEL DE DESTAQUE ao redor do elemento apontado usa
   --theme-accent-rgb direto, porque não existe classe Tailwind equivalente
   pra "borda + brilho em volta de um retângulo medido em JS". Trocando essa
   variável (já é feito em qualquer redesign, ver :root), o anel muda
   junto — não precisa editar nada aqui. */

/* Medido/posicionado via JS a cada passo (ver destacarPassoDoTour em
   app.js) — a transição aqui é só o que anima suavemente ENTRE um passo e
   outro depois da primeira medição; a troca de tela em si já tem seu
   próprio fade (showView). */
.onboarding-tour-highlight {
  position: fixed;
  border-radius: 20px;
  border: 2px solid rgba(var(--theme-accent-rgb), 0.9);
  box-shadow:
    0 0 0 4px rgba(var(--theme-accent-rgb), 0.22),
    0 0 24px 2px rgba(var(--theme-accent-rgb), 0.35);
  pointer-events: none;
  transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
}

.onboarding-tour-balloon {
  position: fixed;
  width: min(320px, calc(100vw - 24px));
  pointer-events: auto;
  transition: top 0.25s ease, left 0.25s ease;
}

/* Losango de 45° com duas bordas apagadas — a técnica clássica de "seta de
   balão" via CSS puro, sem imagem. Qual das duas bordas apaga (e de que
   lado o losango fica colado) depende de pra que lado o balão abriu
   (data-posicao, escrito por app.js a partir do retorno de
   calcularPosicaoBalao). A posição ao longo da borda (arrowOffset, o outro
   eixo) é aplicada por app.js via style.left/style.top inline, pra sempre
   apontar pro centro do elemento mesmo quando o balão foi empurrado pra
   caber na tela. */
.onboarding-tour-balloon-arrow {
  position: absolute;
}

.onboarding-tour-balloon[data-posicao='bottom'] .onboarding-tour-balloon-arrow {
  top: -7px;
  border-right: none;
  border-bottom: none;
}

.onboarding-tour-balloon[data-posicao='top'] .onboarding-tour-balloon-arrow {
  bottom: -7px;
  border-left: none;
  border-top: none;
}

.onboarding-tour-balloon[data-posicao='right'] .onboarding-tour-balloon-arrow {
  left: -7px;
  border-top: none;
  border-right: none;
}

.onboarding-tour-balloon[data-posicao='left'] .onboarding-tour-balloon-arrow {
  right: -7px;
  border-bottom: none;
  border-left: none;
}

/* Quem pediu menos movimento no sistema não deveria ver o balão/anel
   deslizando entre passos — mesmo critério já usado em haptics/sons
   (ver hapticsEnabled/soundEnabled em app.js). */
@media (prefers-reduced-motion: reduce) {
  .onboarding-tour-highlight,
  .onboarding-tour-balloon,
  #onboarding-tour-overlay {
    transition: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   Design System Nest Reef (Fase 2, 2026-09-01)
   Fundação de componentes -- classes prefixadas ".ds-" de propósito, pra
   nunca colidir com uma classe Tailwind gerada e deixar claro, olhando o
   HTML, o que já foi migrado pro sistema novo e o que ainda é markup
   antigo. Nada aqui foi aplicado nas telas existentes ainda (só nos 6
   campos "Tipo/Tipo de Reef/Sistema" que motivaram este PR) -- migrar
   tela por tela é trabalho das Fases 3-5, ver plano de implementação na
   auditoria.
   ═══════════════════════════════════════════════════════════════════════ */

.ds-btn {
  font-size: 0.92rem;
  font-weight: 600;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0.72em 1.3em;
  border-radius: var(--ds-r-md);
  transition: background var(--ds-dur) var(--ds-ease), transform 90ms var(--ds-ease), border-color var(--ds-dur) var(--ds-ease);
}
.ds-btn:active { transform: scale(0.97); }
.ds-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.ds-btn-primary { background: var(--ds-action); color: var(--ds-action-ink); }
.ds-btn-primary:hover { background: var(--ds-action-hover); }
.ds-btn-primary:active { background: var(--ds-action-press); }
.ds-btn-secondary { background: var(--ds-surface); color: var(--ds-text-primary); border: 1px solid var(--ds-border-strong); }
.ds-btn-secondary:hover { background: var(--ds-surface-hover); }
.ds-btn-destructive { background: transparent; color: var(--ds-danger); border: 1px solid rgba(184, 72, 58, 0.45); }
.ds-btn-destructive:hover { background: var(--ds-danger-wash); }
.ds-btn-icon { width: 44px; height: 44px; padding: 0; border-radius: var(--ds-r-full); background: var(--ds-surface); color: var(--ds-text-secondary); border: 1px solid var(--ds-border); }
.ds-btn-icon:hover { background: var(--ds-surface-hover); color: var(--ds-text-primary); }
.ds-btn:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ds-bg-base), 0 0 0 4px var(--ds-action); }

.ds-field {
  background: var(--cr-bg);
  border: 1px solid var(--ds-border-strong);
  border-radius: var(--ds-r-md);
  padding: 0.75em 0.9em;
  color: var(--ds-text-primary);
  font-size: 0.92rem;
  width: 100%;
  transition: border-color var(--ds-dur) var(--ds-ease);
}
.ds-field::placeholder { color: var(--ds-text-tertiary); }
.ds-field:focus-visible { outline: none; border-color: var(--ds-action); box-shadow: 0 0 0 3px rgba(224, 106, 44, 0.22); }

/* botão-gatilho de um campo "select" custom (mesmo padrão de
   app/src/date-picker.js: input nativo escondido guarda o valor real,
   este botão é só a UI visível) -- ver #select-picker-overlay no
   index.html e app/src/custom-select.js pra a lógica. */
.ds-select-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  text-align: left;
  cursor: pointer;
}
.ds-select-trigger svg { flex-shrink: 0; color: var(--ds-text-tertiary); transition: transform var(--ds-dur) var(--ds-ease); }
.ds-select-trigger[aria-expanded="true"] svg { transform: rotate(180deg); }

.ds-segmented { display: inline-flex; padding: 4px; background: var(--cr-bg); border: 1px solid var(--ds-border); border-radius: var(--ds-r-md); gap: 2px; }
.ds-segmented button { font-size: 0.85rem; font-weight: 500; color: var(--ds-text-secondary); background: transparent; border: none; padding: 0.55em 1.1em; border-radius: calc(var(--ds-r-md) - 4px); cursor: pointer; transition: background var(--ds-dur) var(--ds-ease), color var(--ds-dur) var(--ds-ease); }
.ds-segmented button[aria-selected="true"] { background: var(--ds-surface-hover); color: var(--ds-text-primary); box-shadow: var(--ds-shadow-sm); }

.ds-chip { font-size: 0.82rem; color: var(--ds-text-secondary); background: var(--ds-surface); border: 1px solid var(--ds-border); border-radius: var(--ds-r-full); padding: 0.45em 1em; cursor: pointer; transition: all var(--ds-dur) var(--ds-ease); }
.ds-chip[aria-selected="true"] { color: var(--ds-action-ink); background: var(--ds-action); border-color: var(--ds-action); }

/* Card "limpo" (pedido da Andressa, 2026-09-02, referência: layout do
   projeto "Futuristic Technology Website" no Behance): profundidade vem do
   contraste de fundo (--ds-surface sobre --ds-bg-base), não de borda
   pesada nem sombra grande -- a borda fica só o suficiente pra separar do
   fundo em telas OLED/sem contraste de cor, a sombra é sutil (sh-sm), e o
   padding cresce pra dar mais respiro. */
.ds-card { background: var(--ds-surface); border: 1px solid var(--ds-border); border-radius: var(--ds-r-lg); padding: 1.5rem; box-shadow: var(--ds-shadow-sm); }
.ds-card-flat .ds-row { display: flex; justify-content: space-between; padding: 0.7rem 0; border-top: 1px solid var(--ds-border); font-size: 0.88rem; }
.ds-card-flat .ds-row:first-child { border-top: none; }

.ds-badge { display: inline-flex; align-items: center; gap: 0.4em; font-size: 0.78rem; font-weight: 600; padding: 0.3em 0.7em; border-radius: var(--ds-r-sm); }
.ds-badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; }
.ds-badge-ok { color: var(--ds-stable-text); background: var(--ds-stable-wash); }
.ds-badge-ok::before { background: var(--ds-stable-text); }
.ds-badge-atencao { color: rgb(var(--theme-accent2-rgb)); background: var(--ds-premium-wash); }
.ds-badge-atencao::before { background: rgb(var(--theme-accent2-rgb)); }
.ds-badge-critico { color: var(--ds-danger); background: var(--ds-danger-wash); }
.ds-badge-critico::before { background: var(--ds-danger); }

.ds-alert { display: flex; gap: 0.85rem; padding: 1rem 1.1rem; border-radius: var(--ds-r-md); border: 1px solid; }
.ds-alert svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 0.1rem; }
.ds-alert .ds-alert-title { font-size: 0.9rem; font-weight: 600; color: var(--ds-text-primary); }
.ds-alert .ds-alert-desc { font-size: 0.85rem; color: var(--ds-text-secondary); margin-top: 0.15rem; }
.ds-alert-action { background: var(--ds-info-wash); border-color: rgba(93, 127, 166, 0.4); }
.ds-alert-action svg { color: var(--ds-info); }
.ds-alert-success { background: var(--ds-stable-wash); border-color: rgba(77, 90, 85, 0.45); }
.ds-alert-success svg { color: var(--ds-stable-text); }
.ds-alert-critical { background: var(--ds-danger-wash); border-color: rgba(184, 72, 58, 0.45); }
.ds-alert-critical svg { color: var(--ds-danger); }

/* toast -- substitui gradualmente os 4 setMensagemX quase-duplicados
   achados na auditoria (não migrado ainda; foundation só). */
.ds-toast {
  position: fixed;
  left: 1rem;
  right: 1rem;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 5.5rem);
  max-width: 22rem;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-strong);
  border-radius: var(--ds-r-md);
  padding: 0.8rem 1rem;
  box-shadow: var(--ds-shadow-md);
  z-index: 80;
  animation: ds-toast-in 0.3s var(--ds-ease);
}
.ds-toast.ds-toast-leaving { animation: ds-toast-out 0.22s var(--ds-ease) forwards; }
.ds-toast svg { width: 18px; height: 18px; flex-shrink: 0; }
.ds-toast span { font-size: 0.86rem; color: var(--ds-text-primary); }
.ds-toast-success svg { color: var(--ds-stable-text); }
.ds-toast-error svg { color: var(--ds-danger); }
@keyframes ds-toast-in { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes ds-toast-out { from { transform: none; opacity: 1; } to { transform: translateY(10px); opacity: 0; } }

/* skeleton de loading -- substitui o texto "Carregando..." (foundation
   só; aplicar tela por tela nas próximas fases). */
.ds-skel { background: linear-gradient(90deg, var(--ds-surface) 0%, var(--ds-surface-hover) 50%, var(--ds-surface) 100%); background-size: 200% 100%; animation: ds-shimmer 1.6s ease-in-out infinite; border-radius: var(--ds-r-sm); }
@keyframes ds-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

.ds-empty-state { text-align: center; padding: 2.4rem 1.5rem; border: 1px dashed var(--ds-border-strong); border-radius: var(--ds-r-lg); }
.ds-empty-state svg { width: 34px; height: 34px; color: var(--ds-text-tertiary); margin: 0 auto; }
.ds-empty-state h4 { font-size: 0.96rem; margin-top: 0.9rem; color: var(--ds-text-primary); }
.ds-empty-state p { font-size: 0.85rem; color: var(--ds-text-secondary); margin-top: 0.35rem; max-width: 32ch; margin-inline: auto; }

@media (prefers-reduced-motion: reduce) {
  .ds-toast, .ds-skel { animation: none !important; }
}

/* ─── #select-picker-overlay (dropdown custom, ver app/src/custom-select.js) ─
   Mesma técnica do #date-picker-overlay: opção "selecionada" em destaque
   neutro (fundo elevado + sombra, igual .ds-segmented[aria-selected]) em
   vez de preenchimento colorido sólido -- pedido explícito da Andressa ao
   ver o azul nativo do <select> no Tipo de Reef: "a cor escolhida seja
   transparente com sombra ou use outro modelo que já esteja em uso no
   app". */
#select-picker-overlay {
  z-index: 76;
}
#select-picker-list {
  max-height: min(60vh, 360px);
  overflow-y: auto;
}
.ds-select-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  text-align: left;
  padding: 0.7em 0.9em;
  border-radius: var(--ds-r-sm);
  font-size: 0.92rem;
  color: var(--ds-text-secondary);
  background: transparent;
  border: none;
  cursor: pointer;
  transition: background var(--ds-dur) var(--ds-ease), color var(--ds-dur) var(--ds-ease);
}
.ds-select-option:hover { background: rgba(var(--cr-neutral-500-rgb), 0.12); }
.ds-select-option[aria-selected="true"] {
  background: var(--ds-surface-hover);
  color: var(--ds-text-primary);
  font-weight: 600;
  box-shadow: var(--ds-shadow-sm);
}
.ds-select-option svg { width: 16px; height: 16px; opacity: 0; color: var(--ds-text-primary); }
.ds-select-option[aria-selected="true"] svg { opacity: 1; }
/* Descrição por opção (pedido da Andressa, 2026-09-16): mora logo abaixo
   do botão da opção a que pertence, não numa faixa única no fim da lista
   -- ver construirListaDeOpcoesHtml em app/src/custom-select.js.

   Em toque (a maioria dos celulares/tablets, sem hover de verdade) fica
   SEMPRE visível -- mais simples de ler e, principalmente, evita um bug
   real: uma versão anterior escondia por padrão e revelava ao arrastar o
   dedo, mas isso mudava a altura da lista NO MEIO do mesmo toque que ia
   selecionar, e em celular de verdade o botão "fugia" de baixo do dedo
   quando a lista se reorganizava, fazendo o toque não selecionar nada.
   Só em telas com mouse de verdade (`hover: hover` E `pointer: fine` --
   um notebook com trackpad, não um tablet com caneta) é que esconde por
   padrão e bindCustomSelects em app.js revela a certa ao passar o mouse
   (`.is-previewing`) -- ali hover e clique são dois eventos discretos e
   separados, sem risco de mudar o alvo no meio do próprio clique. */
.ds-select-option-description {
  padding: 0 0.9em 0.6em;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--ds-text-tertiary);
}
@media (hover: hover) and (pointer: fine) {
  .ds-select-option-description { display: none; }
  .ds-select-option-description.is-previewing { display: block; }
}

/* ─── Splash de abertura (pedido da Andressa, 2026-09-04) ─────────────────
   Primeira coisa que aparece quando o app carrega, depois da splash nativa
   do sistema (essa a gente não controla) -- ícone em destaque, "NEST REEF"
   tipografado (não a versão raster pequena do logo, pra ficar nítido em
   qualquer tamanho de tela), tagline embaixo, e a bolinha dourada do
   emblema faz uma volta completa. Não bloqueia toque (pointer-events:none
   o tempo inteiro) -- quem for mais rápido que a animação já toca direto
   no que tem por baixo, e a suíte de testes automatizada (que faz login
   logo após o load) nunca precisa esperar por isso. O sumiço em si é CSS
   puro (@keyframes com forwards) -- só a decisão de mostrar ou não (uma
   vez por sessão do app, ver splash-init.js) precisa de JS, porque isso
   depende de sessionStorage. Posição/tamanho da bolinha (89.9%, 30.6%,
   ~13% de diâmetro) veio de medir o pixel de verdade em
   app/brand/nest-reef-icon.png (script Python, centróide do maior grupo
   de pixels dourados), não é chute visual. */
html[data-skip-splash] .app-splash {
  display: none;
}
.app-splash {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.1rem;
  background: var(--cr-bg);
  pointer-events: none;
  animation: app-splash-dismiss 3.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
/* some assim que a 3ª palavra termina de aparecer (ver .app-splash-word-3
   mais abaixo -- os dois estão amarrados no mesmo instante, ~2,65s, de
   propósito, pra não sumir nem antes nem depois da frase terminar).
   Tudo ficou ~35% mais lento (pedido da Andressa, 2026-09-04). */
@keyframes app-splash-dismiss {
  0%, 80% { opacity: 1; }
  100% { opacity: 0; visibility: hidden; }
}

.app-splash-icon-wrap {
  position: relative;
  width: 34vw;
  max-width: 160px;
  aspect-ratio: 1;
  opacity: 0;
  scale: 0.92;
  /* origem perto da base do emblema -- o balanço (mais abaixo) usa a
     mesma origem pra parecer que o conjunto balança "preso" embaixo,
     como uma planta/coral balançando com a correnteza, não girando em
     torno do próprio meio. */
  transform-origin: 50% 84%;
  animation:
    app-splash-icon-in 0.6s cubic-bezier(0.2, 0.8, 0.3, 1) 0.2s forwards,
    app-splash-coral-sway 3.4s ease-in-out 0.9s infinite;
}
@keyframes app-splash-icon-in {
  to { opacity: 1; scale: 1; }
}
/* Balanço leve "como uma onda" (pedido da Andressa, 2026-09-04) -- não dá
   pra balançar só o coral isolado sem recortá-lo do PNG (mesma técnica de
   recorte que causou o problema do sol logo abaixo; arriscado demais
   repetir num desenho grande e orgânico logo depois de ter dado problema
   num círculo pequeno). Em vez disso o emblema inteiro balança bem sutil
   (poucos graus, ida assimétrica pra parecer corrente, não pêndulo
   simétrico) -- lê como o coral reagindo à água mesmo sem isolar só ele.
   Usa a propriedade `rotate` separada (não `transform: rotate()`) de
   propósito: assim ela some independente da animação de entrada acima,
   que já ocupa `scale`/`opacity` -- as duas convivem sem uma sobrescrever
   a outra (suporte amplo o bastante nos navegadores móveis relevantes
   pro app). Continua girando (infinite) até a splash sumir. */
@keyframes app-splash-coral-sway {
  0%, 100% { rotate: 0deg; }
  30% { rotate: 2.4deg; }
  65% { rotate: -1.3deg; }
}
.app-splash-icon { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; z-index: 0; }

/* cobre a bolinha dourada original (estática, presa na imagem) com um
   círculo na cor de fundo -- a cópia animada logo abaixo nasce exatamente
   no mesmo lugar, então a troca é imperceptível: sem isso apareceriam
   duas bolinhas (a fixa da imagem + a que orbita). Correção 2026-09-04
   (pedido da Andressa: "quando o sol sai do lugar fica uma sombra dele
   amarela"): a cobertura cresceu de 15% pra 19% de diâmetro (margem de
   segurança bem maior que o ponto original, ~13%) e ganhou `z-index`
   explícito -- não confia só na ordem do DOM pra ficar por cima da
   imagem e por baixo do ponto animado. */
.app-splash-sun-cover {
  position: absolute;
  left: 89.9%;
  top: 30.6%;
  width: 19%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--cr-bg);
  z-index: 1;
}
.app-splash-sun-pivot {
  position: absolute;
  inset: 0;
  z-index: 2;
  will-change: transform;
  animation: app-splash-sun-orbit 2s cubic-bezier(0.5, 0, 0.5, 1) 0.65s 1 both;
}
@keyframes app-splash-sun-orbit {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.app-splash-sun-dot {
  position: absolute;
  left: 89.9%;
  top: 30.6%;
  width: 13%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, #F0D6A4, #C8A77A 62%, #A3814F 100%);
  box-shadow: 0 0 10px rgba(200, 167, 122, 0.45);
}

/* Bolhas entre o coral e o sol (pedido da Andressa, 2026-09-04) --
   4 pontinhos dourados na imagem original (89.9%/30.6% é o sol; estes
   quatro ficam entre 59-74% x, 36-61% y, medidos do mesmo jeito). Mesma
   técnica de cobertura+cópia do sol, com a margem generosa que já
   corrigiu o problema de lá (cobertura ~35-40% maior que o ponto
   original em cada uma). Diferente do sol, a cópia NÃO repete a cor
   dourada -- vira uma bolha de verdade (fundo quase transparente, brilho
   sutil no canto, contorno claro fino) que sobe e desaparece em loop,
   cada uma com atraso/duração levemente diferente pra não subirem todas
   junto (bolha de verdade nunca sobe em fileira sincronizada). */
.app-splash-bubble-cover {
  position: absolute;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--cr-bg);
  z-index: 1;
}
.app-splash-bubble-cover-1 { left: 69.64%; top: 47.04%; width: 11%; }
.app-splash-bubble-cover-2 { left: 62.41%; top: 36.31%; width: 6.5%; }
.app-splash-bubble-cover-3 { left: 59.82%; top: 50.85%; width: 5.5%; }
.app-splash-bubble-cover-4 { left: 73.82%; top: 60.72%; width: 4.5%; }

.app-splash-bubble {
  position: absolute;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.08) 55%, rgba(255, 255, 255, 0.02) 78%);
  border: 1px solid rgba(255, 255, 255, 0.35);
  opacity: 0;
  z-index: 2;
  animation-name: app-splash-bubble-rise;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}
/* sobem (margin-top negativo, não translateY -- translateY em % seria
   relativo ao próprio tamanho da bolha, bem pequeno demais pra dar a
   sensação de subida; margin desloca em vw, igual ao tamanho do ícone,
   então a distância percorrida acompanha o ícone em qualquer tela). */
@keyframes app-splash-bubble-rise {
  0% { margin-top: 0; opacity: 0; }
  18% { opacity: 0.85; }
  78% { opacity: 0.3; }
  100% { margin-top: -2.4vw; opacity: 0; }
}
.app-splash-bubble-1 { left: 69.64%; top: 47.04%; width: 8.03%; animation-delay: 1.1s; animation-duration: 3.1s; }
.app-splash-bubble-2 { left: 62.41%; top: 36.31%; width: 4.78%; animation-delay: 1.6s; animation-duration: 2.5s; }
.app-splash-bubble-3 { left: 59.82%; top: 50.85%; width: 3.92%; animation-delay: 0.9s; animation-duration: 3.4s; }
.app-splash-bubble-4 { left: 73.82%; top: 60.72%; width: 3.01%; animation-delay: 1.35s; animation-duration: 2.8s; }

.app-splash-wordmark {
  font-family: 'Orbitron', ui-sans-serif, system-ui, sans-serif;
  font-size: clamp(1.35rem, 6vw, 1.9rem);
  font-weight: 600;
  letter-spacing: 0.32em;
  padding-left: 0.32em; /* compensa o letter-spacing puxando o texto pra direita, senão fica desalinhado do ícone acima */
  color: rgb(var(--cr-neutral-50-rgb));
  opacity: 0;
  animation: app-splash-fade-up 0.6s ease-out 0.45s forwards;
}
@keyframes app-splash-fade-up {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* "Discover. Share. Connect." em 3 momentos -- uma palavra de cada vez,
   da esquerda pra direita, não digitação contínua (pedido da Andressa,
   2026-09-04, correção do primeiro rascunho que digitava letra a letra).
   Cada palavra é um <span> próprio com seu atraso -- a anterior continua
   visível quando a próxima aparece, então a frase vai se completando.
   A 3ª palavra termina de aparecer por volta de 2,65s, o mesmo instante em
   que a splash inteira começa a sumir (app-splash-dismiss) e a volta da
   bolinha termina (app-splash-sun-orbit) -- os três amarrados de
   propósito. */
.app-splash-tagline {
  font-size: 0.78rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(var(--cr-neutral-300-rgb), 0.8);
}
.app-splash-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(4px);
  animation: app-splash-word-in 0.5s ease-out forwards;
}
.app-splash-word-1 { animation-delay: 0.8s; }
.app-splash-word-2 { animation-delay: 1.5s; }
.app-splash-word-3 { animation-delay: 2.15s; }
@keyframes app-splash-word-in {
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .app-splash-icon-wrap, .app-splash-wordmark, .app-splash-word {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .app-splash-sun-pivot { animation: none; }
  .app-splash-bubble { animation: none; opacity: 0; }
}
