/* ==========================================================================
   Sabai — protótipo expresso
   Fonte da verdade: .vortex/ui-sehat-prancha.html
   Paleta Sabai: verde #02381F (principal) · amarelo #F8A01B (secundário)
           vermelho #EC3E23 (detalhe) · brancos quentes #FAF8F5/#E9E2D7
   Tipografia: Fraunces (títulos) + DM Sans (interface/corpo)
   Breakpoints: mobile <768 · tablet 768–1023 · desktop 1024+
   ========================================================================== */

:root{
  --brand-green:#02381F;
  --brand-yellow:#F8A01B;
  --brand-red:#EC3E23;
  --navy:var(--brand-green); /* nome legado: agora é o verde da marca */
  --white:#FFFFFF;
  --warm-white:#FAF8F5;
  --warm-neutral:#E9E2D7;
  --cream:#F1E6DA;
  --muted:#5E6B63;
  --accent-warm:var(--brand-yellow);
  --line:rgba(2,56,31,.10);
  --serif:'Fraunces',Georgia,'Times New Roman',serif;
  --sans:'DM Sans',-apple-system,'Segoe UI',Roboto,Arial,sans-serif;

  --ease-move:.5s ease-out;
  --header-h:96px;
  /* Posição focal específica do Camarão Thai Crispy — mesmo valor usado em
     cardapio.css (camarao-thai-crispy-melhorado.webp é o mesmo arquivo nos
     dois lugares, ver CAMARAO_THAI_CRISPY_IMG em cardapio-data.js). Ver
     comentário completo lá; resumo: foto mais larga que alta, prato bem
     centralizado, viés leve abaixo do centro pra preservar as rodelas de
     limão na base. Só este prato usa este valor — os demais continuam no
     50% 50% padrão de .card__photo. */
  --camarao-focal-position: 50% 56%;
}

@media (max-width:767px){
  :root{--header-h:72px}
}
@media (min-width:768px) and (max-width:1023px){
  :root{--header-h:84px}
}

*,*::before,*::after{box-sizing:border-box}
/* Proteção de conteúdo: impede selecionar/copiar os textos visíveis do
   site. Restaurado explicitamente em input/textarea (ver regra abaixo) para
   os campos de entrada do cliente continuarem selecionáveis normalmente —
   nunca no formulário inteiro, só no campo editável em si. Complementado
   pelo listener central de "copy" em no-copy.js. */
*,*::before,*::after{-webkit-user-select:none;user-select:none}
input,textarea{-webkit-user-select:text;user-select:text}
html{scroll-behavior:smooth}
body{
  margin:0;
  font-family:var(--sans);
  color:var(--navy);
  background:var(--warm-white);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{max-width:100%;display:block}
h1,h2,h3,p{margin:0}

/* ---------- foco visível (base) ---------- */
:focus-visible{outline:2px solid var(--navy);outline-offset:3px}

/* ---------- skip link ---------- */
.skip-link{
  position:absolute;left:16px;top:-60px;z-index:100;
  background:var(--navy);color:var(--white);
  padding:12px 18px;border-radius:8px;
  font-family:var(--sans);font-size:14px;font-weight:500;
  text-decoration:none;
  transition:top .2s ease;
}
.skip-link:focus{top:16px}

/* ==========================================================================
   CABEÇALHO
   ========================================================================== */
.site-header{
  position:fixed;
  top:0;left:0;right:0;
  z-index:60;
  height:var(--header-h);
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:16px;
  padding:0 40px;
  background:transparent;
  transition:background-color .35s ease,box-shadow .35s ease;
}
@media (max-width:1023px){
  .site-header{padding:0 20px}
  /* .btn-header some do fluxo do header no mobile/tablet: com a nav virando
     drawer fixo (fora do fluxo), manter o botão (mesmo invisível até o
     scroll) desalinharia o hambúrguer do canto direito via space-between
     (viraria 3 itens: logo/hambúrguer/botão-fantasma, empurrando o
     hambúrguer pro meio) e, rolada a página, o botão real (mesmo com
     opacity/visibility escondendo-o só visualmente) ainda ocupava espaço
     de layout e vazava ~25px pra fora da viewport em 320px. O CTA "Fazer
     pedido" já existe dentro do drawer (.menu-drawer__cta).
     Seletor `a.btn-header` (elemento+classe, especificidade 0-1-1) em vez
     de `.btn-header` (0-1-0): a regra base `.btn{display:inline-block}`
     (mais abaixo no arquivo) tem a MESMA especificidade que `.btn-header`
     sozinho e, por vir depois no arquivo, vencia a cascata mesmo dentro
     desta media query — o botão nunca chegava a sumir de verdade. Isso
     causava os 2 bugs (5 e 7) na mesma raiz. */
  a.btn-header{display:none}
}
@media (min-width:1024px){
  .site-header{gap:24px}
}
.site-header.is-scrolled{
  background:var(--cream);
  box-shadow:0 4px 24px rgba(1,32,18,.10);
}
/* Header creme: links e hambúrguer passam a verde. Links só no desktop —
   abaixo de 1024px a nav vira o drawer, que continua verde com texto claro. */
@media (min-width:1024px){
  .site-header.is-scrolled .site-header__nav a{color:var(--brand-green);text-shadow:none}
  .site-header.is-scrolled .site-header__nav a:hover{color:var(--brand-green)}
  .site-header.is-scrolled .site-header__nav a:focus-visible{outline-color:var(--brand-green)}
}
.site-header.is-scrolled .menu-toggle span{background:var(--brand-green);box-shadow:none}
.site-header.is-scrolled .menu-toggle:focus-visible,
.site-header.is-scrolled .btn-header:focus-visible{outline-color:var(--brand-green)}
/* Logo e botão ficam sempre no fluxo flex (nunca display:none) para não
   deslocar a nav (posicionada em absolute, centralizada de forma
   independente) — apenas opacity/visibility/transform escondem o
   conteúdo visualmente enquanto a hero ainda está visível. */
.site-header__logo{
  display:flex;
  align-items:center;
  line-height:0;
  flex-shrink:0;
  opacity:0;
  visibility:hidden;
  transform:translateY(-8px);
  pointer-events:none;
  transition:opacity .4s ease,transform .4s ease,visibility 0s linear .4s;
}
.site-header.is-scrolled .site-header__logo{
  opacity:1;
  visibility:visible;
  transform:translateY(0);
  pointer-events:auto;
  transition:opacity .4s ease,transform .4s ease;
}
/* Logo Sabai transparente direto sobre o header creme, sem placa. */
.site-header__logo img{
  height:52px;
  width:auto;
  -webkit-touch-callout:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-user-drag:none;
}
@media (min-width:1024px){.site-header__logo img{height:62px}}
.site-header__nav{
  display:flex;
  align-items:center;
  gap:40px;
  position:absolute;
  left:50%;
  top:50%;
  transform:translate(-50%,-50%);
}
.site-header__nav a{
  position:relative;
  font-family:var(--sans);
  font-size:15px;
  font-weight:500;
  letter-spacing:.03em;
  color:var(--white);
  text-decoration:none;
  text-shadow:0 1px 4px rgba(1,32,18,.4);
  opacity:.92;
  transition:opacity .2s ease,color .25s ease;
}
.site-header__nav a::after{
  content:'';
  position:absolute;
  left:0;
  right:0;
  bottom:-7px;
  height:2px;
  background:var(--accent-warm);
  transform:scaleX(0);
  transform-origin:center;
  transition:transform .3s ease;
}
.site-header__nav a:hover{color:var(--accent-warm);opacity:1}
.site-header__nav a:hover::after{transform:scaleX(1)}
.site-header__nav a.is-current::after{transform:scaleX(1)}
.site-header__nav a:focus-visible{
  outline:2px solid var(--white);
  outline-offset:4px;
  border-radius:2px;
}

/* ---------- Menu hambúrguer (mobile/tablet <1024px) ---------- */
.menu-toggle{
  display:none;
  flex-shrink:0;
  width:44px;height:44px;
  align-items:center;justify-content:center;
  background:transparent;border:none;padding:0;cursor:pointer;
  position:relative;
  z-index:110;
}
.menu-toggle span{
  position:absolute;left:50%;top:50%;
  width:22px;height:2px;
  background:var(--white);
  border-radius:2px;
  transform:translate(-50%,-50%);
  box-shadow:0 1px 4px rgba(1,32,18,.4);
  transition:transform .3s ease,opacity .3s ease;
}
.menu-toggle span:nth-child(1){transform:translate(-50%,calc(-50% - 7px))}
.menu-toggle span:nth-child(3){transform:translate(-50%,calc(-50% + 7px))}
.menu-toggle[aria-expanded="true"] span:nth-child(1){transform:translate(-50%,-50%) rotate(45deg)}
.menu-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
.menu-toggle[aria-expanded="true"] span:nth-child(3){transform:translate(-50%,-50%) rotate(-45deg)}
.menu-toggle:focus-visible{outline:2px solid var(--white);outline-offset:4px;border-radius:4px}
/* Com o drawer aberto, o X dedicado do painel (.menu-drawer__close) assume
   sozinho o fechamento — ele fica ancorado à borda real do painel, já o
   toggle do header (mesma posição/tamanho aproximados, por causa do
   padding do header) ficava fisicamente sobreposto a ele, interceptando
   cliques (bug real encontrado ao testar). Esconder o toggle enquanto
   aberto elimina a duplicidade/sobreposição sem remover a animação de
   abrir (hambúrguer→X só é visível durante a transição de abertura). */
.menu-toggle[aria-expanded="true"]{
  opacity:0;
  pointer-events:none;
  transition:opacity .2s ease;
}

.site-header__nav-scrim{
  display:none;
  position:fixed;inset:0;
  background:rgba(1,32,18,.5);
  opacity:0;pointer-events:none;
  transition:opacity .3s ease;
  z-index:105;
}
.site-header__nav-scrim.is-open{opacity:1;pointer-events:auto}

/* Mesmo problema de especificidade do `.btn-header` acima: `.btn` (mais
   abaixo no arquivo) empatava em especificidade com `.menu-drawer__cta`
   sozinho e vencia por ordem de cascata, fazendo um 2º botão "Fazer
   pedido" (navy sobre navy) vazar para a nav desktop (1024px+), quebrando
   em 2 linhas e desalinhando a nav já aprovada. `a.menu-drawer__cta`
   (0-1-1) resolve. */
a.menu-drawer__cta{display:none}

/* Mesmo padrão: o X do drawer só existe dentro do @media mobile, então
   fora dele cai no estilo padrão do navegador e vaza pra nav desktop. */
button.menu-drawer__close{display:none}

body.nav-open{overflow:hidden}

@media (max-width:1023px){
  .menu-toggle{display:inline-flex}
  .site-header__nav-scrim{display:block}
  .site-header__nav{
    position:fixed;
    top:0;right:0;bottom:0;
    left:auto;
    transform:translateX(100%);
    width:min(320px,86vw);
    height:100dvh;
    height:100vh;
    background:var(--navy);
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    justify-content:flex-start;
    gap:2px;
    padding:calc(var(--header-h) + 12px) 28px 32px;
    transition:transform .35s ease;
    overflow-y:auto;
    z-index:106;
    box-shadow:-12px 0 40px rgba(1,32,18,.3);
  }
  .site-header__nav.is-open{transform:translateX(0)}
  .site-header__nav a{
    width:100%;
    padding:14px 2px;
    font-size:17px;
    border-bottom:1px solid rgba(250,248,245,.14);
    text-shadow:none;
    opacity:1;
    min-height:44px;
    display:flex;
    align-items:center;
  }
  .site-header__nav a::after{display:none}
  /* X próprio do drawer, ancorado ao PAINEL (não ao header): o hambúrguer
     do header também vira X ao abrir, mas ele fica na posição do header
     (que tem padding lateral próprio, `right:0` do drawer não bate com
     o `right:20px` efetivo do toggle) — parecia "flutuar" fora do painel
     em vez de pertencer a ele. Este botão fica de verdade no canto
     superior direito do drawer, sempre alinhado com sua borda real. */
  button.menu-drawer__close{
    display:inline-flex;
    position:absolute;
    top:18px;right:20px;
    width:36px;height:36px;
    align-items:center;justify-content:center;
    background:transparent;border:none;padding:0;cursor:pointer;
    color:var(--warm-white);
  }
  .menu-drawer__close:focus-visible{outline:2px solid var(--white);outline-offset:3px;border-radius:4px}
  a.menu-drawer__cta{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    width:100%;
    margin-top:22px;
    border:none;
  }
}
/* ==========================================================================
   HERO
   ========================================================================== */
.hero{
  position:sticky;
  top:0;
  z-index:1;
  height:100vh;
  height:100svh;
  height:100dvh;
  display:flex;
  overflow:hidden;
  background:#01240F url('data:image/webp;base64,UklGRoAAAABXRUJQVlA4IHQAAAAQBgCdASogABUAPu1kqU2ppaQiMAgBMB2JZACdMxEABggH6Ae//dgAFM1TCypWr6tfD6Jv4VLRacAA/uny1tISKvydaH2f0l6eQvWt9kXYh0Gyqy6DIW4hfFVIzSqW5g3fdbR+hE1THDGvrQquPmqe+NaAAA==') 50% 45%/cover no-repeat;
  scroll-margin-top:0;
}
@media (min-width:768px){
  .hero{
    /* Camada 1 (topo): imagem real; enquanto não decodifica, fica transparente
       e a camada 2 (LQIP embutido, sem requisição de rede) fica visível por baixo. */
    background-image:url('hero-sehat-prototipo.webp'), url('data:image/webp;base64,UklGRoAAAABXRUJQVlA4IHQAAAAQBgCdASogABUAPu1kqU2ppaQiMAgBMB2JZACdMxEABggH6Ae//dgAFM1TCypWr6tfD6Jv4VLRacAA/uny1tISKvydaH2f0l6eQvWt9kXYh0Gyqy6DIW4hfFVIzSqW5g3fdbR+hE1THDGvrQquPmqe+NaAAA==');
    background-position:50% 45%;
    background-size:cover;
    background-repeat:no-repeat;
  }
}
@media (max-width:767px){
  .hero-orders-transition{
    /* Wrapper que limita o efeito "Mais pedidos sobe por cima da hero
       parada" apenas a essas duas seções. Altura = altura da hero (sticky,
       100svh) + altura de #mais-pedidos, automaticamente (soma do fluxo
       normal dos dois filhos — não precisamos calcular/forçar height). A
       .hero, sticky dentro deste wrapper, gruda em top:0 só até o wrapper
       acabar; passado isso ela é liberada normalmente, ANTES do início de
       "Sobre nós" (que fica fora do wrapper, fluxo comum). Isso evita o bug
       antigo: aqui a hero NUNCA fica presa atrás de #sobre-nos/#avaliacoes/
       rodapé, porque o limite de sticky é este wrapper, não a página. */
    position:relative;
    isolation:isolate;
  }
  .hero{
    /* Sticky restrito ao wrapper acima (não mais à página inteira — essa
       era a causa do bug "seção vazia/vídeo atrás durante scroll rápido":
       sticky-página-inteira mantinha a hero pinada atrás de TODO o resto).
       Sticky preservado em tablet/desktop via bloco acima (min-width:768px). */
    position:sticky;
    top:0;
    height:100vh;
    height:100svh;
    z-index:0;
    contain:paint;
    isolation:isolate;
    /* LQIP mobile: camada única, sempre presente desde o 1º paint até o
       poster real (.hero__poster-picture, elemento <picture> por cima) chegar. */
    background-image:url('data:image/webp;base64,UklGRnQAAABXRUJQVlA4IGgAAAAQBQCdASoYACsAPtFMoU2oJCMiOrzIAQAaCWMAyjPWBfqVCPrArqlnidaepS8wzsbAAP7xQ7KBpzJbjxSweJUXraQzmuYMF1t60Iv1Y2yQgGK5uWC4+aJ/vB9sJh/f+PRkaSj/1+GAAA==');
    background-position:50% 45%;
    background-size:cover;
    background-repeat:no-repeat;
  }
  /* Reforço defensivo contra o padrão de bug já visto neste projeto
     (sticky + siblings com transform/will-change podendo ser promovidos a
     uma camada de composição isolada, pintando por cima da seção seguinte
     mesmo com z-index CSS correto no código-fonte — visto em engines
     WebKit reais). `.orders` já é `position:relative;z-index:2;
     isolation:isolate` (stacking context próprio), o que corrige isso em
     teoria; `contain:paint` reforça na prática, garantindo que NENHUM
     filho (carrossel, card, ::before do fundo) possa pintar fora da caixa
     de `.orders` nem ser composto numa camada separada que escape da
     ordem de pintura do elemento pai. Investigação real (Playwright,
     Chromium touch-fling via CDP em várias velocidades + WebKit + rede
     lenta/cache vazio, .vortex/status.md) NÃO reproduziu o bug do card
     sozinho no estado atual — fundo, título, carrossel e botão sempre
     sobem juntos em todas as medições. Mantido como reforço preventivo,
     sem custo (não altera layout, não quebra o scroll horizontal do
     carrossel, que usa translateX apenas no eixo X). */
  .orders{contain:paint}
  /* Camada de composição própria e estável para `.orders` inteira (não só
     `contain:paint`): promove o elemento a uma layer de GPU fixa desde o
     primeiro frame, para eliminar qualquer chance de o compositor do
     Safari/iPhone recompor `.orders` em cima do vídeo da hero durante o
     scroll (bug de composição gráfica reportado no Safari/iPhone). */
  .orders{
    transform:translate3d(0,0,0);
    -webkit-backface-visibility:hidden;
    backface-visibility:hidden;
    will-change:transform;
  }
  /* Superfície opaca real, como filho literal (não ::before/background) —
     reforço extra contra o mesmo bug: garante uma camada 100% sólida
     cobrindo `.orders` desde o topo, independente de como o compositor
     trate o background do elemento pai. Mesma cor de --warm-white usada
     no fundo real de `.orders`/`.card__body`. */
  .orders__opaque-surface{
    position:absolute;
    inset:0;
    z-index:0;
    pointer-events:none;
    /* Restaura a textura de palmeiras que existia direto em .orders (ver
       regra base abaixo): esta camada é filho real (reforço anti-bug de
       composição Safari/iPhone) e por isso pinta POR CIMA do ::before de
       .orders — precisa replicar imagem+véu ela mesma, senão esconde a
       textura inteira atrás de #FAF8F5 sólido. */
    background:#FAF8F5 url('palmeiras-fundo-site.webp') 50% 50% / cover no-repeat;
    transform:translate3d(0,0,0);
    -webkit-backface-visibility:hidden;
    backface-visibility:hidden;
  }
  .orders__opaque-surface::after{
    content:'';
    position:absolute;
    inset:0;
    pointer-events:none;
    background:rgba(250,248,245,.78);
  }
  /* .orders::before (véu translúcido direto em .orders) fica redundante no
     mobile agora que .orders__opaque-surface tem seu próprio véu — evita
     duas camadas de véu concorrendo visualmente. Desativado só aqui,
     comportamento em desktop/tablet intacto. */
  .orders::before{
    background:none;
  }
}
.hero__poster-picture{
  position:absolute;inset:0;
  pointer-events:none;
}
.hero__poster-picture-img{
  display:block;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 45%;
}
.hero__video-mobile{
  display:none;
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  opacity:0;
  transition:opacity .3s ease;
  pointer-events:none;
}
@media (max-width:767px){
  .hero__video-mobile.is-active{display:block}
  .hero__video-mobile.is-active.is-ready{opacity:1}
}
/* Camada de fallback do vídeo mobile (hero-mobile-fallback-economia.webp):
   cobre o vídeo (por cima dele na ordem de pintura, mesmo stacking context
   sem z-index — nasce logo depois do <video> no HTML) enquanto ele ainda
   não estiver de fato reproduzindo — carregando, autoplay bloqueado pelo
   Safari (comum no Modo Pouca Energia) ou com erro. Some só no evento
   "playing" real (ver script.js, nunca por loadeddata/canplay/promise
   resolvida) via a classe .is-hidden, controlada por um estado hasPlayed
   explícito — depois da 1ª reprodução bem-sucedida a classe nunca é
   removida, então pausas do sistema de visibilidade da hero (fora da
   tela) não trazem a imagem de volta. `<picture>` com um único
   `<source media="(max-width:767px)">` e o `<img>` sem `src` próprio:
   fora dessa faixa (tablet/desktop) nada é requisitado. */
.hero__mobile-fallback-picture{
  display:none;
  position:absolute;inset:0;
  pointer-events:none;
}
.hero__mobile-fallback{
  display:block;
  width:100%;height:100%;
  object-fit:cover;
  pointer-events:none;
  -webkit-touch-callout:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-user-drag:none;
  /* transition:visibility com delay = a opacity termina de esmaecer (curta,
     só opacity, sem transform/will-change/filter) antes da camada sair de
     verdade do fluxo de pintura. */
  transition:opacity .3s ease, visibility 0s linear .3s;
}
.hero__mobile-fallback.is-hidden{
  opacity:0;
  visibility:hidden;
}
@media (max-width:767px){
  .hero__mobile-fallback-picture{display:block}
}
.hero__overlay{
  position:absolute;inset:0;
  background:#01240F;
  opacity:.44;
}
.hero__overlay::after{
  content:'';
  position:absolute;inset:0;
  background:radial-gradient(ellipse 64% 52% at 50% 46%,
    rgba(1,32,18,.46) 0%,
    rgba(1,32,18,.30) 50%,
    rgba(1,32,18,0) 78%);
}
/* Reforço discreto de legibilidade: degradê linear adicional, mais forte
   na base (onde tende a haver texto/UI) e se dissolvendo em direção ao
   centro, sem escurecer a foto como um todo. */
.hero__overlay::before{
  content:'';
  position:absolute;inset:0;
  background:linear-gradient(to top,
    rgba(1,32,18,.38) 0%,
    rgba(1,32,18,.12) 30%,
    rgba(1,32,18,0) 55%);
  pointer-events:none;
}
.hero__content{
  position:relative;
  z-index:2;
  width:100%;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  text-align:center;
  padding:0 24px;
}
.hero__logo{
  width:340px;
  height:auto;
  /* Sem placa: logo transparente direto sobre a foto. */
  /* Brilho creme: o verde da logo some sobre a foto escura sem ele. */
  filter:drop-shadow(0 0 2px rgba(241,230,218,.95)) drop-shadow(0 0 10px rgba(241,230,218,.55));
  opacity:0;
  animation:heroIn .32s ease-out 0s forwards;
}
.hero__tag{
  font-family:var(--sans);
  font-weight:500;
  font-size:17px;
  letter-spacing:.20em;
  text-transform:uppercase;
  color:var(--white);
  margin-top:26px;
  opacity:0;
  animation:heroIn .32s ease-out .06s forwards;
}
.hero__content .btn{
  margin-top:38px;
  opacity:0;
  animation:heroIn .32s ease-out .12s forwards;
}

@keyframes heroIn{
  from{opacity:0;transform:translateY(16px)}
  to{opacity:1;transform:translateY(0)}
}

/* Mobile: entrada praticamente imediata, sem stagger sequencial */
@media (max-width:767px){
  .hero__logo{animation:heroIn .28s ease-out 0s forwards}
  .hero__tag{animation:heroIn .28s ease-out 0s forwards}
  .hero__content .btn{animation:heroIn .28s ease-out 0s forwards}
}

/* Tablet: hero segue centralizado, logo intermediária */
@media (min-width:768px) and (max-width:1023px){
  .hero__logo{width:280px}
  .hero__tag{font-size:15px;letter-spacing:.18em}
}

/* Mobile: bloco desloca para o terço superior (área calma do prato) */
@media (max-width:767px){
  .hero__overlay::after{
    background:radial-gradient(ellipse 96% 34% at 50% 30%,
      rgba(1,32,18,.46) 0%,
      rgba(1,32,18,.30) 50%,
      rgba(1,32,18,0) 80%);
  }
  /* Centralizado verticalmente, mesma composição do tablet/desktop —
     `justify-content:flex-start;padding-top:17%` (versão anterior) deixava
     498-641px de vazio abaixo do bloco logo/subtítulo/botão em telas
     ≤767px, porque empurrava o conteúdo pro terço superior sem nada
     ocupando o espaço abaixo. Centralizar elimina o vazio e mantém a
     identidade igual à do desktop, sem alterar a imagem/crop. */
  .hero__content{
    justify-content:center;
  }
  .hero__logo{width:210px}
  .hero__tag{font-size:13px;letter-spacing:.16em;margin-top:20px}
  .hero__content .btn{margin-top:30px}
}

/* ==========================================================================
   TÍTULOS DE SEÇÃO
   ========================================================================== */
.section-title{
  position:relative;
  z-index:1;
  font-family:var(--serif);
  font-weight:400;
  font-size:44px;
  line-height:1.15;
  letter-spacing:-.01em;
  color:var(--navy);
  text-align:center;
}

@media (min-width:768px) and (max-width:1023px){
  .section-title{font-size:36px}
}
@media (max-width:767px){
  .section-title{font-size:30px}
}

/* ==========================================================================
   BOTÃO — sistema de inversão (pílula)
   ========================================================================== */
.btn{
  display:inline-block;
  font-family:var(--sans);
  font-size:15px;
  font-weight:500;
  letter-spacing:.06em;
  padding:16px 38px;
  border-radius:999px;
  border:1.5px solid transparent;
  cursor:pointer;
  text-decoration:none;
  transition:background-color .25s ease,color .25s ease,border-color .25s ease,transform .25s ease;
}

/* Botões "Ver cardápio" (Mais pedidos, Sobre nós, Avaliações — mesmo
   componente reutilizado, todos apontam pra /cardapio via data-menu-link em
   script.js): no iPhone/Safari, manter o dedo pressionado sobre um <a>
   dispara o menu de contexto/prévia nativa do link. Restrito a este
   atributo (não ao site inteiro) — o toque normal continua abrindo
   /cardapio normalmente, teclado/hover/foco não são afetados. */
[data-menu-link]{
  -webkit-touch-callout:none;
  -webkit-user-select:none;
  user-select:none;
}

.btn-onphoto{
  background:rgba(255,255,255,.14);
  color:var(--white);
  border-color:rgba(255,255,255,.4);
  backdrop-filter:blur(12px);
  -webkit-backdrop-filter:blur(12px);
  box-shadow:0 4px 20px rgba(1,32,18,.14);
  transform-origin:center;
  transition:transform .25s ease;
}
.btn-onphoto:hover{
  transform:scale(1.05);
}
.btn-onphoto:focus-visible{
  outline:2px solid var(--white);
  outline-offset:4px;
  box-shadow:0 0 0 4px rgba(1,32,18,.55);
}

.btn-primary{
  background:var(--navy);
  color:var(--white);
  border-color:var(--navy);
}
.btn-primary:hover{
  background:transparent;
  color:var(--navy);
  border-color:var(--navy);
}
.btn-primary:focus-visible{
  outline:2px solid var(--navy);
  outline-offset:4px;
}
.btn-primary:active{
  transform:translateY(1px);
}

/* .btn-header vem DEPOIS de .btn no arquivo de propósito: como .btn tem
   border:1.5px solid transparent no shorthand e ambas as regras têm a
   mesma especificidade, a ordem no arquivo decide — precisa vir depois
   para que border-color:var(--white) não seja sobrescrita. */
.btn-header{
  flex-shrink:0;
  padding:12px 28px;
  font-size:14px;
  background:var(--brand-yellow);
  color:var(--brand-green);
  border-color:var(--brand-yellow);
  opacity:0;
  visibility:hidden;
  transform:translateY(-8px);
  pointer-events:none;
  transition:opacity .4s ease,transform .4s ease,visibility 0s linear .4s,box-shadow .25s ease;
}
.btn-header:hover{
  background:var(--brand-yellow);
  color:var(--brand-green);
  border-color:var(--brand-yellow);
  box-shadow:0 8px 22px rgba(1,32,18,.2);
}
.site-header.is-scrolled .btn-header{
  opacity:1;
  visibility:visible;
  transform:translateY(0);
  pointer-events:auto;
  transition:opacity .4s ease,transform .4s ease,box-shadow .25s ease;
}
.site-header.is-scrolled .btn-header:hover{
  transform:translateY(0) scale(1.045);
}
.btn-header:focus-visible{
  outline:2px solid var(--white);
  outline-offset:4px;
  box-shadow:0 0 0 4px rgba(1,32,18,.55);
}

/* ==========================================================================
   MAIS PEDIDOS
   ========================================================================== */
.orders{
  position:relative;
  z-index:2;
  isolation:isolate;
  background:var(--warm-white) url('palmeiras-fundo-site.webp') 50% 50% / cover no-repeat;
  padding:112px 24px;
  scroll-margin-top:var(--header-h);
}
@media (max-width:1023px){
  .orders{padding:72px 16px}
}
.orders::before{
  content:'';
  position:absolute;
  inset:0;
  /* .orders já tem cor opaca direto no elemento (background shorthand
     acima); este ::before é só o véu translúcido decorativo por cima da
     imagem. z-index:0 (não negativo) — os filhos abaixo recebem
     position:relative;z-index:1 para ficar por cima desta camada. */
  z-index:0;
  pointer-events:none;
  background:rgba(250,248,245,.78);
}
/* ---------- Carrossel ---------- */
.carousel{
  position:relative;
  z-index:1;
  max-width:1464px;
  margin:56px auto 0;
  padding:0 66px;
}
@media (min-width:768px) and (max-width:1127px){
  /* Limite estendido a 1127px (não 1023px como o resto do layout): com
     card de 460px + gap 28px + padding do carrossel/orders, 2 cards
     completos só cabem a partir de ~1128px de janela. Entre 1024-1127px
     (ainda "desktop" para header/grid/about), o carrossel usa o mesmo
     tratamento do tablet (1 card central) para nunca ficar numa "zona
     morta" sem nenhum card totalmente visível — ver visibleFullCards()
     em script.js, que usa o mesmo limiar de 1128px. */
  .carousel{padding:0 54px;margin-top:40px}
}
@media (max-width:767px){
  /* Goteira real reservada para as setas (não mais overlay sobre o card):
     com só 1 card ativo visível no mobile (ver visibleFullCards() em
     script.js) a área de "peek" nas bordas é estreita demais para uma
     seta de 38px não tocar o card ativo — reservar 40px de cada lado
     garante zero interseção com foto/texto em qualquer largura ≥320px. */
  .carousel{padding:0 40px;margin-top:28px}
  /* Reforço explícito (defensivo, mesmo valor de `overflow:hidden` já
     aplicado abaixo): o "peek" lateral dos cards é efeito só HORIZONTAL
     (trilho translateX). Declarar os dois eixos por extenso documenta a
     intenção e evita que uma futura edição solte `overflow-y` sem querer
     (ex.: trocando `overflow:hidden` por `overflow-x:visible` sozinho),
     o que permitiria um card vazar verticalmente para fora de `.orders`. */
  .carousel__viewport{overflow-x:hidden;overflow-y:hidden}

  /* Altura travada do carrossel (raiz do "salto vertical" corrigida aqui):
     no empilhado mobile (.card em column, ver regra abaixo), o card ativo
     tem corpo de texto (.card__body) com altura própria, enquanto o card
     "peek" tem esse corpo colapsado a 0. Como .card__body anima sua
     PRÓPRIA altura (transition:flex-basis .3s ease — flex-basis é o eixo
     PRINCIPAL, vertical, em column), medido quadro a quadro: no clique, o
     card que está saindo encolhe 180px->0 enquanto o que está entrando
     cresce 0px->180px, em paralelo, sobre a MESMA janela de 0.3s. Com
     `.card{height:auto}` (antiga regra), a altura do trilho seguia
     align-items:stretch sobre o MAIOR card do momento — e como as duas
     curvas (uma caindo, uma subindo) se cruzam no meio, o maior valor
     entre elas DESPENCA até o cruzamento e SOBE de volta até o fim,
     formando um "V": viewport/.orders encolhem e voltam a crescer no meio
     da transição horizontal, arrastando consigo o botão "Ver cardápio
     completo" (medido: ~14px de variação de altura em pleno andamento,
     mesmo com início e fim idênticos — por isso testes antes/depois não
     detectavam). --orders-card-h é o teto exato já usado hoje pelo card
     ativo (foto 160px + corpo com o clamp máximo de título/descrição
     140px+37px + paddings 30px + borda 2px) — aplicado como altura FIXA
     (não min-height) do viewport e de todo card (ativo, peek ou clone),
     a altura do trilho deixa de depender de qual card está com o corpo
     expandido a cada instante, então não existe mais "maior card do
     momento" para o viewport perseguir — ela já é sempre a mesma. */
  :root{
    --orders-card-h:340px;
    /* Transição arredondada foto->conteúdo (mesmo princípio de cardapio.css
       .dish-card__body): só existe geometricamente aqui no empilhado
       mobile (foto em cima, texto embaixo) — no layout lado a lado de
       tablet/desktop não há "topo da foto" para o conteúdo sobrepor, por
       isso estas variáveis ficam só dentro deste media query. */
    --card-overlap-radius:14px;
    --card-overlap-offset:12px;
  }
  .carousel__viewport{
    height:var(--orders-card-h);
    min-height:var(--orders-card-h);
    max-height:var(--orders-card-h);
  }
  .carousel__track{height:100%}

  /* Arrastar com o dedo (script.js, Pointer Events) — só no mobile, onde
     o gesto é oferecido. pan-y pinch-zoom: o navegador continua tratando
     rolagem vertical e pinch-zoom nativamente; o eixo horizontal (sem
     "pan-x" na lista) fica livre para o JS mover o trilho manualmente,
     sem competir com nenhum gesto nativo de scroll horizontal. */
  .carousel__viewport{touch-action:pan-y pinch-zoom}
  /* Evita seleção acidental de texto do título/descrição durante o gesto —
     não deve competir com o arraste do carrossel. .card__photo já tem
     -webkit-user-drag:none/-webkit-touch-callout:none em todas as larguras
     (ver regra base acima). */
  .carousel__track{user-select:none}
  /* Some a transição do trilho só enquanto o dedo está arrastando (JS
     aplica/remove esta classe) — mesmo princípio de .is-snapping acima,
     mas só no transform do trilho: o dedo controla a posição 1:1, sem
     nenhuma suavização/atraso, e a transição volta assim que o gesto
     termina (commit ou snap-back), reaproveitando a mesma
     `transition:transform .55s cubic-bezier(...)` de sempre. */
  .carousel__track.is-dragging{transition:none !important}
}
.carousel__viewport{
  overflow:hidden;
  position:relative;
}
.carousel__viewport::before,
.carousel__viewport::after{
  content:'';
  position:absolute;
  top:0;bottom:0;
  width:150px;
  z-index:4;
  pointer-events:none;
  opacity:0;
  transition:opacity .3s ease;
}
@media (max-width:1127px){
  .carousel__viewport::before,
  .carousel__viewport::after{width:56px}
}
.carousel__viewport::before{
  left:0;
  background:linear-gradient(to right, rgba(250,248,245,.55) 0%, rgba(250,248,245,0) 100%);
}
.carousel__viewport::after{
  right:0;
  background:linear-gradient(to left, rgba(250,248,245,.55) 0%, rgba(250,248,245,0) 100%);
}
/* Fade só aparece quando há de fato um card cortado naquela borda
   (classes alternadas via JS em update(), a cada troca de parada). */
.carousel__viewport.has-cut-start::before{ opacity:1; }
.carousel__viewport.has-cut-end::after{ opacity:1; }

/* ---------- Notebook/desktop (>=1024px): fade lateral por TRANSPARÊNCIA ----------
   Antes, as bordas usavam ::before/::after com um degradê de
   rgba(250,248,245,.55) — uma camada bege/branca POR CIMA dos cards. Isso
   lavava os cards das pontas e escondia a textura de palmeiras do fundo da
   seção. Aqui essa camada é desligada (content:none) e substituída por uma
   máscara de transparência no próprio viewport: o card das pontas fica de
   fato translúcido e o fundo real de "Mais pedidos" aparece através dele.

   A máscara é aplicada em .carousel__viewport, e as setas são IRMÃS dele
   (vivem em .carousel, fora do viewport — medido em 1024/1280/1366/1440/
   1920px: a seta esquerda termina antes de o viewport começar), então
   continuam 100% opacas e clicáveis, nunca mascaradas.

   --carousel-mask-fade precisa terminar ANTES do primeiro card totalmente
   visível, senão os cards centrais perderiam nitidez. Folga real medida
   entre a borda do viewport e o 1º card central: 1280px=76, 1366px=119,
   1440px=156, 1920px=192, 1024px=224 (1 card central). Por isso 64px na
   faixa mais apertada e 140px só a partir de 1440px, onde há folga.
   Abaixo de 1024px nada muda: o comportamento mobile/tablet aprovado
   (incluindo o swipe) fica exatamente como está. */
@media (min-width:1024px){
  .carousel__viewport::before,
  .carousel__viewport::after{
    content:none;
  }
  .carousel__viewport{
    --carousel-mask-fade:64px;
    -webkit-mask-image:linear-gradient(
      to right,
      transparent 0,
      #000 var(--carousel-mask-fade),
      #000 calc(100% - var(--carousel-mask-fade)),
      transparent 100%
    );
    mask-image:linear-gradient(
      to right,
      transparent 0,
      #000 var(--carousel-mask-fade),
      #000 calc(100% - var(--carousel-mask-fade)),
      transparent 100%
    );
  }
}
@media (min-width:1440px){
  .carousel__viewport{--carousel-mask-fade:140px}
}
.carousel__track{
  display:flex;
  gap:28px;
  will-change:transform;
  transition:transform .55s cubic-bezier(.4,0,.2,1);
}
.carousel__arrow{
  position:absolute;
  top:50%;
  transform:translateY(-50%);
  z-index:10;
  width:48px;
  height:48px;
  border-radius:50%;
  border:1px solid var(--line);
  background:var(--white);
  color:var(--navy);
  display:flex;
  align-items:center;
  justify-content:center;
  cursor:pointer;
  box-shadow:0 6px 18px rgba(2,56,31,.14);
  transition:background-color .2s ease,color .2s ease,transform .2s ease,opacity .2s ease;
  /* No iPhone, dois toques rápidos numa das setas ampliavam a página
     (double-tap-to-zoom nativo do Safari). touch-action:manipulation
     restrito a estes 2 botões remove esse gesto específico sem tocar no
     zoom da página inteira (sem user-scalable=no/maximum-scale). Também
     evita o menu de toque prolongado/seleção no botão e o realce cinza
     padrão do Safari ao tocar, mantendo o próprio :hover/:focus-visible já
     definidos abaixo como feedback visual. */
  touch-action:manipulation;
  -webkit-touch-callout:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-tap-highlight-color:transparent;
}
/* Área de toque mínima de 44×44px sem alterar o tamanho visual da seta
   (38px em telas <768px — ver media query abaixo): expande só a área
   clicável/tocável via pseudo-elemento invisível, técnica padrão que não
   desloca nem redesenha o botão. */
.carousel__arrow::after{
  content:'';
  position:absolute;
  inset:-3px;
}
.carousel__arrow:hover{
  background:var(--navy);
  color:var(--white);
}
.carousel__arrow:focus-visible{
  outline:2px solid var(--navy);
  outline-offset:3px;
}
.carousel__arrow[disabled]{
  opacity:.35;
  cursor:default;
  pointer-events:none;
}
.carousel__arrow--prev{left:0}
.carousel__arrow--next{right:0}

@media (min-width:768px) and (max-width:1127px){
  .carousel__arrow{width:44px;height:44px}
}
@media (max-width:767px){
  /* Alinhada ao centro da FAIXA DA FOTO (altura fixa 160px, ver .card
     abaixo), não ao centro do card inteiro — como o card ativo (com
     texto) e os cards peek (só foto) têm alturas totais diferentes no
     layout empilhado do mobile, `top:50%` do container inteiro deriva
     para dentro da área de texto do card ativo. Fixar em 80px (metade de
     160px) garante que a seta fique sempre sobre a foto, nunca sobre
     h3/p, em qualquer estado. */
  .carousel__arrow{width:38px;height:38px;top:80px}
  .carousel__arrow--prev{left:2px}
  .carousel__arrow--next{right:2px}
}

.card{
  flex:0 0 460px;
  width:460px;
  height:322px;
  background:var(--white);
  border:1px solid var(--line);
  border-radius:12px;
  overflow:hidden;
  display:flex;
  align-items:stretch;
}
@media (min-width:768px) and (max-width:1127px){
  .card{
    flex-basis:min(58vw,420px);
    width:min(58vw,420px);
  }
}
@media (max-width:767px){
  /* Empilha foto em cima / texto embaixo (em vez de lado a lado): com o
     layout imagem-esquerda(42%)/texto-direita(58%) original, a coluna de
     texto ficava espremida a ~110-150px de largura em telas estreitas,
     ilegível. Empilhado, o texto usa a largura quase total do card.
     Largura do card = viewport disponível (janela − padding de .orders
     16px×2 − goteira das setas 40px×2) menos uma pequena folga de "peek"
     nas laterais, com teto de 380px perto do limite de 767px (onde o
     layout tablet assume). */
  .card{
    flex-direction:column;
    align-items:stretch;
    flex-basis:min(calc(100vw - 128px),380px);
    width:min(calc(100vw - 128px),380px);
    /* Altura fixa (não mais auto) — ver --orders-card-h acima. Idêntica
       para card ativo, peek e clone, então .card__body ganhando/perdendo
       altura (is-active) nunca mais altera a altura do CARD em si, só
       redistribui o espaço interno dele. */
    height:var(--orders-card-h);
    min-height:var(--orders-card-h);
    max-height:var(--orders-card-h);
  }
}
.card__photo{
  flex:0 0 42%;
  width:42%;
  height:100%;
  object-fit:cover;
  object-position:50% 50%;
  transition:flex-basis .3s ease,width .3s ease;
  /* Toque prolongado/arraste (Safari/iOS) e arraste no desktop — em todas
     as larguras, não só na faixa onde o swipe por dedo é oferecido (ver
     .carousel__track{user-select:none} logo abaixo, específico do gesto de
     arrastar o trilho). */
  -webkit-touch-callout:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-user-drag:none;
}
/* Camarão Thai Crispy: o arquivo camarao-sehat.png tem uma linha escura de
   ~3px colada na borda DIREITA da foto, embutida no próprio arquivo
   (confirmado pixel a pixel). Sem poder trocar/editar o arquivo, um leve
   zoom (scale) dentro do .card com overflow:hidden empurra a linha pra
   fora da área visível — variação imperceptível de enquadramento. */
img[src*="camarao-sehat"]{transform:scale(1.03)}
/* Camarão Thai Crispy (card "Mais pedidos"): arquivo trocado para
   camarao-thai-crispy-melhorado.webp (referência única em
   CAMARAO_THAI_CRISPY_IMG, cardapio-data.js — mesmo arquivo nos 3 usos do
   site). A foto antiga tinha uma linha escura embutida na borda direita,
   escondida com transform:scale(); a nova foto foi inspecionada pixel a
   pixel e não tem esse defeito — scale removido (regra obsoleta), sem
   recorte extra desnecessário. Esta página (index.html) carrega style.css,
   não cardapio.css — por isso precisa da MESMA regra de object-position
   aqui (ver comentário completo em cardapio.css). */
img[src*="camarao-thai-crispy-melhorado"]{object-position:var(--camarao-focal-position)}
.card__body{
  flex:1 1 58%;
  min-width:0;
  padding:24px 24px 24px 22px;
  display:flex;
  flex-direction:column;
  justify-content:center;
  overflow-y:auto;
  background:var(--warm-white);
  /* Revelado por GEOMETRIA (flex-basis/padding + overflow:hidden), nunca
     por opacity. Uma versão anterior usava opacity:0→1 (com e sem delay)
     para revelar o painel: em QUALQUER ponto intermediário dessa
     transição, a caixa já tinha largura real mas opacity<1 — o fundo
     branco solido do .card (var(--white)) aparecia through o painel
     semitransparente, lido pelo usuário como um "flash branco" no card
     durante toda transição (não só no wrap). Com opacity sempre em 1, o
     painel entra/sai só pela largura (flex-basis) crescendo/encolhendo,
     sempre 100% opaco — nunca há uma janela de transparência para o
     branco do card aparecer. Ver .vortex/status.md — causa raiz do bug
     de cards piscando. */
  opacity:1;
  transition:flex-basis .3s ease,padding .3s ease;
}
/* Só os 2 cards totalmente visíveis/centrais de cada posição do
   carrossel mostram nome + descrição — os cards "peek" (parcialmente
   visíveis nas bordas) mostram só a foto, para nunca correr o risco de
   cortar uma palavra no meio (ver script.js, seção 4). Para não sobrar
   um painel branco vazio nos cards peek, a foto ocupa o card inteiro e
   o painel de texto colapsa a largura zero quando o card não está ativo. */
.card:not(.is-active) .card__photo{
  flex-basis:100%;
  width:100%;
}
.card:not(.is-active) .card__body{
  flex-basis:0;
  padding-left:0;
  padding-right:0;
  overflow:hidden;
}

/* Reposicionamento silencioso do carrossel (clone → card real equivalente,
   ver script.js wrapTo/onEnd): suspende TODAS as transições do trilho e do
   conteúdo interno dos cards (foto/corpo), não só o transform do trilho.
   Sem isso, o card recém-trocado (que acabou de ganhar/perder .is-active
   no mesmo instante do salto) reanimaria sozinho suas próprias transições
   de flex-basis/opacity logo após o salto — um crescimento/fade visível
   bem no momento em que o usuário não deveria perceber nada. */
.carousel__track.is-snapping,
.carousel__track.is-snapping .card,
.carousel__track.is-snapping .card__photo,
.carousel__track.is-snapping .card__body{
  transition:none !important;
}
.card h3{
  font-family:var(--serif);
  font-weight:600;
  font-size:21px;
  line-height:1.22;
  color:var(--navy);
  flex-shrink:0;
}
.card p{
  font-family:var(--sans);
  font-size:13.5px;
  line-height:1.55;
  color:var(--muted);
  margin-top:9px;
  /* Texto completo permanece no DOM (útil para um futuro cardápio) —
     só é cortado visualmente em 4 linhas dentro do card. */
  display:-webkit-box;
  -webkit-box-orient:vertical;
  -webkit-line-clamp:4;
  line-clamp:4;
  overflow:hidden;
}
.card p em{
  font-style:normal;
  color:var(--navy);
  opacity:.7;
}

@media (max-width:767px){
  .card__photo{
    width:100%;
    height:160px;
    flex:0 0 160px;
  }
  /* Card peek (não ativo): no layout empilhado, flex-basis controla ALTURA
     (eixo principal virou vertical) — sobrescreve o "100%" da regra base
     (pensada para o eixo horizontal do desktop) para travar a foto em
     160px fixos, e a régua de texto fecha inteiramente (altura 0 +
     padding 0), sem faixa colorida residual. */
  .card:not(.is-active) .card__photo{
    flex-basis:160px;
  }
  .card:not(.is-active) .card__body{
    flex-basis:0;
    height:0;
    padding:0;
    overflow:hidden;
  }
  /* Altura travada (não mais auto): sem isso, o container ainda cresce/
     encolhe conforme o número real de linhas do título/descrição de cada
     prato, mesmo com o texto sendo cortado visualmente pelo line-clamp —
     e como .carousel__track é flex sem align-items definido (stretch por
     padrão), a altura do card ATIVO domina a altura de todo o trilho/
     viewport/.orders. min-height em h3/p reserva sempre o espaço máximo
     (2 linhas / 5 linhas do clamp) independente do texto real de cada
     prato, e a altura do body soma isso + paddings, fixa. overflow-y:
     hidden (não visible) como rede de segurança, sem cortar nada — a
     soma já cobre exatamente o clamp com uma pequena folga. */
  .card__body{
    padding:14px 16px 16px;
    height:180px;
    overflow-y:hidden;
    /* overflow-x:hidden explícito — sem ele, a regra CSS de "um eixo
       oculto promove o outro a auto" faz overflow-x computar como auto
       sozinho (overflow-y:hidden + overflow-x omitido), transformando
       .card__body num contêiner de scroll horizontal PRÓPRIO. Isso rouba
       o gesto de arrastar (script.js) sempre que o toque começa sobre o
       título/descrição: o navegador entrega só 2-3 pointermove e para o
       fluxo de toque, tratando como um scroll horizontal nativo desse
       contêiner (que nunca tem o que rolar). Fechando os dois eixos, o
       swipe do carrossel volta a funcionar também sobre o texto do card. */
    overflow-x:hidden;
    /* Transição arredondada sobre a foto: sobe --card-overlap-offset via
       margem negativa e ganha exatamente o mesmo tanto de altura (180px +
       offset), então a borda INFERIOR do bloco não se move nem 1px — só a
       borda superior avança sobre a foto. --orders-card-h (altura fixa do
       card, ver acima) continua intacto: este ajuste é só do miolo do
       card, nunca do contêiner externo, então a estabilidade vertical já
       medida quadro a quadro do carrossel não é afetada. */
    position:relative;
    margin-top:calc(-1 * var(--card-overlap-offset));
    height:calc(180px + var(--card-overlap-offset));
    padding-top:calc(14px + var(--card-overlap-offset));
    border-radius:var(--card-overlap-radius) var(--card-overlap-radius) 0 0;
  }
  .card h3{font-size:17px;min-height:calc(17px*1.22*2)}
  .card p{font-size:12.5px;-webkit-line-clamp:5;line-clamp:5;min-height:calc(12.5px*1.55*5)}
}

.orders__cta{
  position:relative;
  z-index:1;
  text-align:center;
  margin-top:48px;
}
@media (max-width:767px){
  .orders__cta{margin-top:32px}
}
/* Botão "Ver cardápio" das Avaliações: no Safari/iPhone, o 1º toque num
   link cujo :hover muda a aparência é interpretado como "simular hover"
   (mostra o estilo de :hover, mas não navega) — só o 2º toque de fato
   clica. Causa real: .btn-primary:hover (compartilhado com outros botões
   fora deste escopo, como carrinho/checkout) não tinha nenhuma guarda de
   dispositivo com hover de verdade. Em vez de editar essa regra
   compartilhada, neutraliza o :hover de volta pro estado normal só neste
   botão específico (#avaliacoes .orders__cta), e só em dispositivos sem
   hover real: como não sobra nenhuma diferença visual entre "hover" e
   normal aqui, o Safari não insere mais o toque de confirmação — o 1º
   toque já navega direto. Em mouse/trackpad (hover:hover e pointer:fine)
   nada muda, o hover de sempre continua funcionando normalmente. */
@media (hover:none), (pointer:coarse) {
  #avaliacoes .orders__cta .btn-primary:hover{
    background:var(--navy);
    color:var(--white);
    border-color:var(--navy);
  }
}
#avaliacoes .orders__cta .btn-primary{
  /* touch-action:manipulation remove o gesto de duplo-toque-para-zoom neste
     link, então o Safari não precisa esperar para decidir o que fazer com o
     toque. Não altera aparência. */
  touch-action:manipulation;
  /* -webkit-touch-callout:none (herdado de [data-menu-link]) segue impedindo
     a prévia do link no toque prolongado; nada aqui bloqueia o toque
     normal — sem pointer-events:none, sem camada por cima, sem listener. */
}
/* Garantia defensiva: se algum dia este botão ganhar um pseudo-elemento
   decorativo, ele nunca poderá receber o toque no lugar do link. Hoje
   .btn/.btn-primary não geram ::before/::after (medido: content "none"),
   então esta regra não muda nada visualmente. */
#avaliacoes .orders__cta .btn-primary::before,
#avaliacoes .orders__cta .btn-primary::after{
  pointer-events:none;
}

/* ==========================================================================
   SOBRE NÓS
   ========================================================================== */
.about{
  position:relative;
  z-index:2;
  isolation:isolate;
  /* Cor sólida direto no elemento (não só no ::before abaixo): garante um
     fundo opaco mesmo se a textura/imagem de fundo ainda não carregou ou se
     o ::before por qualquer razão não renderizar — a seção nunca fica
     transparente durante o scroll. */
  background-color:var(--brand-green);
  max-width:1180px;
  margin:0 auto;
  padding:112px 24px;
  display:grid;
  grid-template-columns:1fr;
  gap:40px;
  scroll-margin-top:var(--header-h);
}
/* .about é uma caixa centralizada (max-width:1180px), diferente de .orders
   (full-bleed). Como a hero fica sticky/pinada atrás por toda a rolagem
   restante da página, sem um fundo opaco cobrindo TODA a largura da viewport
   a foto da hero vazaria nas laterais (fora dos 1180px) mesmo com z-index
   correto. Breakout full-bleed via ::before para cobrir viewport inteira
   com o mesmo tom de fundo do body, atrás do conteúdo (z-index:-1 dentro do
   contexto de empilhamento isolado do .about, ou seja, ainda acima da hero).
   Fundo: foto de palha trançada com véu verde por cima (regras abaixo),
   para contraste com os textos claros.
   `background-image` só é aplicado quando `.about` ganha a classe
   `.bg-loaded` (via IntersectionObserver em script.js, rootMargin 200px) —
   até lá fica só o `background-color` sólido, evitando buscar essa imagem
   de fundo (mais distante da 1ª tela) no load inicial da página. */
.about::before{
  content:'';
  position:absolute;
  top:0;
  bottom:0;
  left:50%;
  width:100vw;
  transform:translateX(-50%);
  /* Cor sólida já garantida direto em .about (acima) — aqui fica só a
     camada decorativa de imagem/textura, POR CIMA dessa cor. z-index:0 (não
     negativo): o conteúdo (.about__text/.about__mosaic, logo abaixo) recebe
     position:relative;z-index:1 para ficar por cima desta camada.
     Tamanho natural (sem background-size:cover) + repetição: o arquivo
     original tem 1536×1024px, e esta camada é full-bleed (100vw) na altura
     inteira da seção — em celulares altos (layout empilhado em 1 coluna) e
     em desktops muito largos, `cover` forçava essa imagem a ser ampliada
     bem além da resolução original, pixelizando a textura. Como é uma
     textura de papel/fibra uniforme e sem padrão direcional, repetir no
     tamanho nativo preenche a área inteira sem emenda perceptível e sem
     nunca ampliar o arquivo. */
  background-position:center;
  background-repeat:repeat;
  z-index:0;
  pointer-events:none;
}
/* Fundo de palha trançada: versão vertical no celular/tablet (seção
   empilhada, mais alta que larga) e horizontal a partir de 1024px (texto +
   mosaico lado a lado). Foto, não textura repetível — por isso cover, sem
   repeat. O véu verde por cima garante contraste do texto claro. */
.about.bg-loaded::before{
  background-image:
    linear-gradient(rgba(2,56,31,.62),rgba(2,56,31,.62)),
    url("sabai-sobre-nos-trancado-natural-mobile.webp");
  background-size:cover;
  background-repeat:no-repeat;
}
@media (min-width:1024px){
  .about.bg-loaded::before{
    background-image:
      linear-gradient(rgba(2,56,31,.62),rgba(2,56,31,.62)),
      url("sabai-sobre-nos-trancado-natural-desktop.webp");
  }
}
/* Chegada direta (âncora do cabeçalho na carga inicial, restauração nativa
   de scroll do navegador no reload no meio da página, retorno via bfcache
   do cardápio) — ver script.js, seção 2z. Enquanto <body> tiver esta
   classe (removida pouco depois de 'load'/'pageshow'), qualquer elemento
   com transição de revelação (cards, avaliações, "Sobre nós") aparece no
   estado final imediatamente, sem a transição normal de scroll — pedido
   explícito do cliente: o estado padrão é visível, a animação é só um
   "enhancement" durante scroll real, nunca motivo para esconder algo que
   já deveria estar na tela. */
body.reveal-instant .about-step,
body.reveal-instant .card.reveal-init,
body.reveal-instant .review-card.reveal-init{
  transition:none !important;
}
/* Estado inicial de animação: sequência de 8 passos atrelada ao scroll
   (ver script.js, seção 2b). Cada elemento marcado com [data-about-step]
   recebe .about-step via JS e alterna .is-visible conforme o progresso
   de scroll cruza seu limiar — reversível nos dois sentidos. */
.about-step{
  opacity:0;
  transform:translateY(28px);
  transition:opacity .9s ease-out,transform .9s ease-out;
}
.about-step--fade-only{
  transform:none;
}
.about-step.is-visible{
  opacity:1;
  transform:translateY(0);
}
/* Imagens do mosaico: fade+translateY puro, sem scale (evita efeito de
   "aparecer e depois crescer"). Transição única cobrindo todas as
   propriedades juntas. */
.about-step--img{
  transform:translateY(20px);
  filter:blur(3px);
}
/* Especificidade elevada (.about__card.about-step--img, 0-2-0) de propósito:
   sem isso, a regra ".about__card" (0-1-0, declarada mais abaixo no arquivo)
   venceria a cascata e sobrescreveria esta transition inteira (shorthand),
   deixando opacity/filter sem transição e o transform em .4s em vez de 1.2s. */
.about__card.about-step--img{
  transition:opacity 1.2s cubic-bezier(0.16,1,0.3,1),transform 1.2s cubic-bezier(0.16,1,0.3,1),filter 1.2s cubic-bezier(0.16,1,0.3,1);
}
.about-step--img.is-visible{
  transform:translateY(0);
  filter:blur(0);
}
/* Rede de segurança PURA CSS (não depende de script.js ter rodado a
   tempo): no celular, o cliente pediu conteúdo visível por padrão. A
   lógica em script.js (isMobileNoAnim) já evita adicionar .reveal-init/
   .about-step nesse viewport, mas se por qualquer motivo (JS ainda não
   executado nesse instante, erro de execução, corrida de tempo com CSS/
   fontes carregando de forma assíncrona) uma dessas classes acabar
   presente com opacity:0 no mobile, esta regra garante que o texto/
   imagem nunca fique oculto durante o scroll — sobrepõe qualquer opacity/
   visibility/filter/transform vindo das regras acima. Custo desprezível,
   nunca esconde nada que já deveria estar visível. */
@media (max-width:767px){
  .about-step,
  .card.reveal-init,
  .review-card.reveal-init{
    opacity:1 !important;
    visibility:visible !important;
    filter:none !important;
    transform:none !important;
  }
}
.about__text{
  position:relative;
  z-index:1;
  align-self:center;
}
/* Especificidade elevada (.about__text .about__eyebrow, 0-2-0) de propósito:
   o eyebrow também é um <p> dentro de .about__text, então fica sujeito à
   regra ".about__text p" (0-1-1) definida mais abaixo — sem isso, aquela
   regra vence e sobrescreve font-size/color mesmo vindo depois no arquivo. */
.about__text .about__eyebrow{
  font-family:var(--sans);
  font-size:12px;
  font-weight:500;
  letter-spacing:.09em;
  text-transform:uppercase;
  color:var(--accent-warm);
  margin:0;
}
.about__text h2{
  font-family:var(--serif);
  font-weight:400;
  font-size:44px;
  line-height:1.15;
  letter-spacing:-.01em;
  color:var(--warm-white);
  margin-top:6px;
}
.about__text p{
  font-size:17px;
  line-height:1.75;
  color:rgba(250,248,245,.85);
  margin-top:22px;
  max-width:44ch;
}
.about__cta{
  margin-top:32px;
}
/* Botão "Ver Cardápio" sobre o novo fundo navy de .about: mesmo padrão
   fundo creme (mesmo tom do header) / texto verde, em vez
   do .btn-primary padrão (navy sobre navy ficaria ilegível aqui). Escopo
   restrito a .about__cta para não afetar o outro uso de .btn-primary
   (seção "Mais pedidos", sobre fundo claro). */
.about__cta.btn-primary{
  background:var(--cream);
  color:var(--navy);
  border-color:var(--cream);
}
.about__cta.btn-primary:hover{
  background:transparent;
  color:var(--cream);
  border-color:var(--cream);
}
.about__cta.btn-primary:focus-visible{
  outline:2px solid var(--cream);
}

/* ---------- Mosaico de imagens ---------- */
.about__mosaic{
  position:relative;
  z-index:1;
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:20px;
}
.about__col{
  display:flex;
  flex-direction:column;
  gap:20px;
}
.about__col--b{
  margin-top:-48px;
  transition:transform .2s linear;
  will-change:transform;
}
.about__card{
  width:100%;
  height:260px;
  object-fit:cover;
  border-radius:16px;
  box-shadow:0 8px 18px rgba(2,56,31,.10);
  display:block;
  transition:transform .4s ease,box-shadow .4s ease;
  -webkit-touch-callout:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-user-drag:none;
}
.about__card:hover{
  transform:scale(1.03);
  box-shadow:0 12px 26px rgba(2,56,31,.18);
}
.about__card.about-step--img.is-visible:hover{
  transform:translateY(0) scale(1.03);
  box-shadow:0 12px 26px rgba(2,56,31,.18);
}

@media (min-width:1024px){
  .about{
    grid-template-columns:1fr 1fr;
    gap:72px;
    align-items:center;
    padding:112px 24px;
  }
  .about__card{height:300px}
}
@media (min-width:768px) and (max-width:1023px){
  .about{padding:88px 24px}
  .about__text h2{font-size:36px}
}
@media (max-width:767px){
  .about{padding:64px 20px}
  .about__text h2{font-size:30px}
  .about__text p{font-size:16px}
  .about__col--b{margin-top:-24px}
}

/* ==========================================================================
   AVALIAÇÕES
   ========================================================================== */
.reviews{
  position:relative;
  z-index:2;
  isolation:isolate;
  background-color:#F5EDE4;
  padding:112px 24px;
  scroll-margin-top:var(--header-h);
}
:root{--leaf-drop:max(240px,24vw)}
/* Topo do rodapé transparente na altura exata em que a camada de folhas de
   .reviews desce sobre ele (só na home — style.css não carrega no cardápio). */
body:has(.reviews.bg-loaded) .site-footer{
  background-color:transparent;
  background-image:linear-gradient(to bottom,transparent calc(var(--leaf-drop) - 2px),#F1E6DA calc(var(--leaf-drop) - 2px));
}
/* `background-image` só é aplicado quando `.reviews` ganha a classe
   `.bg-loaded` (IntersectionObserver, rootMargin 200px) — mesma técnica de
   carregamento adiado usada em `.about::before` acima, já que esta seção
   também fica distante da 1ª tela. */
/* Folhas: camada que cobre a seção inteira e ainda desce --leaf-drop para
   dentro do rodapé (que deixa essa faixa do topo transparente, regra
   abaixo). cover + âncora na base: nunca sobra faixa vazia em cima, e as
   pontas das folhas terminam no rodapé. O degradê no fim funde o creme da
   foto no creme do rodapé. */
.reviews::before{
  content:"";
  position:absolute;
  top:0;left:0;right:0;
  bottom:calc(-1 * var(--leaf-drop));
  z-index:-1;
  pointer-events:none;
  background-repeat:no-repeat;
  background-size:100% 100%,cover;
  background-position:0 0,right bottom;
}
.reviews.bg-loaded::before{
  background-image:
    linear-gradient(to bottom,rgba(241,230,218,0) calc(100% - 90px),#F1E6DA 100%),
    url("fundo-avaliacoes-cores-palmeiras.webp");
}
@media (min-width:768px) and (max-width:1023px){
  .reviews{padding:80px 24px}
}
@media (max-width:767px){
  .reviews{padding:64px 18px}
  .reviews__title{font-size:28px}
}
.reviews__eyebrow{
  font-family:var(--sans);
  font-size:12px;
  font-weight:500;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--brand-green);
  text-align:center;
}
.reviews__title{
  font-family:var(--serif);
  font-weight:400;
  font-size:38px;
  line-height:1.2;
  letter-spacing:-.01em;
  color:var(--navy);
  text-align:center;
  margin-top:10px;
}
.reviews__grid{
  max-width:1180px;
  margin:56px auto 0;
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:28px;
}
.review-card{
  padding:36px 32px;
  border:1px solid rgba(2,56,31,.12);
  border-radius:16px;
  background:var(--white);
  box-shadow:0 8px 18px rgba(2,56,31,.08);
  display:flex;
  flex-direction:column;
}
.review-card__quote{
  font-family:var(--serif);
  font-size:40px;
  line-height:1;
  color:var(--accent-warm);
  opacity:.85;
}
.review-card__stars{
  font-size:15px;
  letter-spacing:.12em;
  color:var(--accent-warm);
  margin-top:10px;
}
.review-card__text{
  font-family:var(--sans);
  font-size:15.5px;
  line-height:1.65;
  color:var(--muted);
  margin-top:18px;
  flex-grow:1;
}
.review-card__name{
  font-family:var(--sans);
  font-size:15px;
  font-weight:700;
  color:var(--navy);
  margin-top:24px;
}
.review-card__source{
  font-family:var(--sans);
  font-size:12.5px;
  color:rgba(2,56,31,.55);
  margin-top:3px;
}
/* Entrada única (não bidirecional) via IntersectionObserver, mesmo padrão
   dos cards de "Mais pedidos" — classe de estado inicial só é aplicada
   via JS, então sem JS o conteúdo permanece visível por padrão. */
.review-card.reveal-init{
  opacity:0;
  transform:translateY(16px);
  transition:opacity .5s ease,transform .5s ease;
}
.review-card.is-visible{
  opacity:1;
  transform:translateY(0);
}

@media (max-width:1023px){
  .reviews__grid{grid-template-columns:1fr;margin-top:40px;gap:20px}
}
@media (max-width:767px){
  .review-card{padding:26px 22px}
}

/* Defesa em profundidade (mobile): o JS (script.js) já evita aplicar o
   estado inicial oculto no celular, mas esta regra garante que, mesmo que
   alguma dessas classes acabe presente, NUNCA sobra filter:blur nem
   transição longa (>400ms) num elemento em largura <=767px — pedido
   explícito do cliente após o relatório citar blur/1.2s no mobile. */
@media (max-width:767px){
  .about-step,
  .about-step--img,
  .about__card.about-step--img,
  .card.reveal-init,
  .review-card.reveal-init{
    opacity:1;
    transform:none;
    filter:none;
    transition:opacity .3s ease-out,transform .3s ease-out;
  }
  .about-step.is-visible,
  .about-step--img.is-visible,
  .card.is-visible,
  .review-card.is-visible{
    opacity:1;
    transform:none;
    filter:none;
  }
}

/* RODAPÉ — extraído para footer.css (componente compartilhado com cardapio.html) */

/* ==========================================================================
   MOVIMENTO — prefers-reduced-motion
   ========================================================================== */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  .hero__logo,.hero__tag,.hero__content .btn{
    animation:none !important;
    opacity:1 !important;
    transform:none !important;
  }
  .card.reveal-init,.about-step,.about-step--img,.review-card.reveal-init{
    transition:opacity .01ms !important;
    transform:none !important;
    opacity:1 !important;
  }
  .btn,.card,.about__card,.site-header__logo,.btn-header,.site-header__nav a::after{
    transition-duration:.01ms !important;
  }
  .about__col--b{
    transform:none !important;
  }
}
