/* ============================================================
   The cinematic layer.

   The page alternates between two worlds, and that alternation IS the design:

     ACTS   — full-bleed photography on charcoal, always dark whatever the
              viewer's theme, type at poster scale. These are the film.
     SHEETS — the light document sections (menu, visit). These are the shop.

   A butcher's site should feel like heat and then like paper. Nothing here
   fights the base layer: acts paint their own fixed ground and never read a
   theme token that could flip underneath them.
   ============================================================ */

/* ---------- the dark world ---------- */
.act {
  --act-ink:  #FBF7F3;
  --act-dim:  #C9BDB3;
  --act-bg:   #0B0908;
  position: relative;
  background: var(--act-bg);
  color: var(--act-ink);
  overflow: clip;
}
.act--tall { min-height: 100svh; }

/* Grain. One inline SVG turbulence, tiled — no image request, and it is the
   difference between "a photo on a page" and "a frame". */
.act::after {
  content: "";
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  opacity: .16; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");
}

/* ---------- cold open ---------- */
.opener {
  position: relative;
  min-height: 100svh;
  display: grid; grid-template-rows: auto 1fr auto;
  gap: var(--s-4);
  padding: var(--s-4) 0 var(--s-5);
  isolation: isolate;
}
.opener__media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.opener__media picture { display: block; width: 100%; height: 100%; }
.opener__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.opener__media img {
  animation: openerIn 1.4s cubic-bezier(.2,.7,.2,1) both,
             breathe 30s ease-in-out 1.4s infinite alternate;
}
@keyframes openerIn { from { opacity: 0; transform: scale(1.14); } to { opacity: 1; transform: scale(1.06); } }
@keyframes breathe  { from { transform: scale(1.06) translate3d(0,0,0); }
                      to   { transform: scale(1.15) translate3d(-1.4%,-1.6%,0); } }
.opener__veil {
  position: absolute; inset: 0; z-index: 1;
  background:
    radial-gradient(120% 85% at 50% 8%, rgb(11 9 8 / 12%) 0%, rgb(11 9 8 / 72%) 62%, rgb(11 9 8 / 94%) 100%),
    linear-gradient(to top, rgb(11 9 8 / 96%) 0%, rgb(11 9 8 / 30%) 46%, rgb(11 9 8 / 55%) 100%);
}
/* Scoped to the CONTENT children only. A bare `.opener > *` also matched the
   media and the veil and reset their position:absolute to relative, which
   dropped a 1730px image into flow and pushed the headline off-screen. */
.opener > .shell { position: relative; z-index: 2; }

.opener__top { display: flex; align-items: center; gap: var(--s-3); }
.opener__mark {
  width: 3.4rem; aspect-ratio: 1; flex: none; background: var(--act-ink);
  -webkit-mask: url("../img/badge.svg") center / contain no-repeat;
          mask: url("../img/badge.svg") center / contain no-repeat;
}
.opener__wordmark { line-height: 1.1; }
.opener__wordmark b {
  display: block; font-family: var(--display); font-weight: 800;
  font-size: var(--step-0); letter-spacing: .01em;
}
.opener__wordmark span {
  display: block; font-size: var(--step--1); letter-spacing: .16em;
  text-transform: uppercase; color: var(--act-dim);
}

.opener__body { align-self: center; display: flex; flex-direction: column; gap: var(--s-4); }
.opener h1 {
  font-size: clamp(2.7rem, 10.5vw, 7.4rem);
  line-height: .92; letter-spacing: -0.035em; max-width: 15ch;
}
/* Line-by-line mask reveal. Each line rides up from behind its own edge —
   the one flourish the page spends real motion on. */
.lineup { display: block; overflow: hidden; }
.lineup > span {
  display: block;
  animation: lineup 1.05s cubic-bezier(.16,.84,.28,1) both;
  animation-delay: calc(var(--i, 0) * 105ms + 250ms);
}
@keyframes lineup { from { transform: translate3d(0,105%,0); } to { transform: none; } }

.opener__lede { color: var(--act-dim); font-size: var(--step-1); max-width: 32ch; }
.opener__cta { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }

.scrollcue {
  display: inline-flex; align-items: center; gap: .6rem;
  font-size: var(--step--1); letter-spacing: .16em; text-transform: uppercase;
  color: var(--act-dim);
}
.scrollcue::after {
  content: ""; width: 1px; height: 2.4rem; background: currentColor;
  transform-origin: top; animation: cue 2.1s ease-in-out infinite;
}
@keyframes cue { 0%,100% { transform: scaleY(.25); opacity: .35 } 45% { transform: scaleY(1); opacity: 1 } }

/* ---------- the pinned stage ---------- */
.stage { position: relative; }
.stage__pin {
  position: sticky; top: 0;
  height: 100svh;
  display: grid;
  overflow: clip;
}
/* isolation is load-bearing: the chassis assigns each figure a z-index in the
   80s to order the crossfade, and without a stacking context here those values
   compete with the wash (2) and the captions (3) and win — which paints the
   photographs over the text. */
.stage__frames { grid-area: 1 / 1; position: relative; z-index: 1; isolation: isolate; }
.stage__frames > figure {
  position: absolute; inset: 0; margin: 0;
  opacity: 0; will-change: opacity, transform;
}
.stage__frames picture { display: block; width: 100%; height: 100%; }
.stage__frames img { width: 100%; height: 100%; display: block; }
.stage__frames img { object-fit: cover; }
.stage__wash {
  grid-area: 1 / 1; z-index: 2; pointer-events: none;
  background:
    linear-gradient(to top, rgb(11 9 8 / 97%) 0%, rgb(11 9 8 / 88%) 22%,
                            rgb(11 9 8 / 52%) 52%, rgb(11 9 8 / 40%) 78%, rgb(11 9 8 / 62%) 100%),
    linear-gradient(to right, rgb(11 9 8 / 88%) 0%, rgb(11 9 8 / 55%) 42%, rgb(11 9 8 / 6%) 100%);
}
.stage__caps { grid-area: 1 / 1; z-index: 3; align-self: end; padding-bottom: var(--s-6); }
.stage__capgrid { display: grid; }
.stage__capgrid > [data-cap] {
  grid-area: 1 / 1;                 /* all four captions occupy ONE cell */
  display: flex; flex-direction: column; gap: var(--s-2);
  will-change: opacity, transform;
}
.stage__caps h3, .stage__caps p, .stage__kicker { text-shadow: 0 1px 24px rgb(11 9 8 / 65%); }
.stage__caps h3 {
  font-size: clamp(1.9rem, 6.4vw, 3.6rem); line-height: 1.0; letter-spacing: -.028em;
  max-width: 16ch;
}
.stage__caps p { color: var(--act-dim); max-width: 34ch; }
.stage__kicker {
  font-size: var(--step--1); letter-spacing: .18em; text-transform: uppercase;
  color: var(--accent);
}
/* progress rail — reads the --p the chassis writes */
.stage__rail {
  grid-area: 1 / 1; z-index: 4; align-self: end; justify-self: stretch;
  height: 2px; background: rgb(255 255 255 / 14%);
}
.stage__rail::before {
  content: ""; display: block; height: 100%; background: var(--accent);
  transform-origin: left; transform: scaleX(var(--p, 0));
}

/* ---------- numbers band ---------- */
.band {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  padding-block: var(--s-6);
}
.band__cell { display: flex; flex-direction: column; gap: .2rem; }
.band__n {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.6rem, 8vw, 4.6rem); line-height: .95; letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
}
.band__l { font-size: var(--step--1); letter-spacing: .14em; text-transform: uppercase; color: var(--act-dim); }
.band__cell--accent .band__n { color: var(--accent); }

/* ---------- reveal primitive ---------- */
[data-reveal] {
  opacity: 0; transform: translate3d(0, 18px, 0);
  transition: opacity .75s cubic-bezier(.2,.7,.2,1), transform .75s cubic-bezier(.2,.7,.2,1);
  transition-delay: calc(var(--d, 0) * 80ms);
}
[data-reveal][data-revealed] { opacity: 1; transform: none; }

/* ---------- quote wall ---------- */
.quotes { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.quote {
  display: flex; flex-direction: column; gap: var(--s-3);
  padding: var(--s-4); border-left: 2px solid var(--accent);
  background: rgb(255 255 255 / 3%);
}
.quote blockquote { margin: 0; font-size: var(--step-1); line-height: 1.35; }
.quote cite {
  font-style: normal; font-size: var(--step--1); letter-spacing: .1em;
  text-transform: uppercase; color: var(--act-dim);
}

/* ---------- packs, in the dark ---------- */
.act .packs { --surface: rgb(255 255 255 / 4%); }
.act .pack {
  background: rgb(255 255 255 / 4%); border-color: rgb(255 255 255 / 10%);
  box-shadow: none; color: var(--act-ink);
}
.act .pack__price { color: var(--accent); }
.act .pack__includes { color: var(--act-dim); }
.act .section__head p { color: var(--act-dim); }
.act .eyebrow { color: var(--act-dim); }

/* ---------- the light sheet ---------- */
.sheet {
  background: var(--surface);
  color: var(--ink);
  border-top: 1px solid var(--rule);
  box-shadow: 0 -30px 60px -40px rgb(0 0 0 / 55%);
}
.sheet .menu-item { border-color: var(--rule); }

/* ---------- sticky order bar, lifted ---------- */
.orderbar {
  transform: translate3d(0, 120%, 0);
  transition: transform .45s cubic-bezier(.2,.8,.2,1);
}
.orderbar[data-lifted] { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .orderbar { transform: none; transition: none; }
  .opener__media img { animation: none; transform: scale(1.06); }
  .lineup > span { animation: none; }
  .scrollcue::after { animation: none; transform: scaleY(.7); }
}

/* ---------- desktop composition ---------- */
/* ---------- portrait composition ----------
   On a phone the stage is 430 wide and ~900 tall, so a landscape frame fills
   the middle and leaves dead darkness above the caption. Pull the caption up
   into the frame, lift the mid-tones so the photograph is still legible as a
   photograph, and favour the lower half of each frame where the subject is. */
@media (max-width: 46rem) {
  /* Caption sits ON the darkened lower third rather than floating mid-frame,
     which leaves the photograph a clean upper two-thirds to be a photograph in. */
  .stage__caps { align-self: end; padding-bottom: 11svh; padding-top: 0; }
  .stage__frames img { object-position: 50% 62%; }
  .stage__wash {
    background:
      linear-gradient(to top, rgb(11 9 8 / 95%) 0%, rgb(11 9 8 / 90%) 26%,
                              rgb(11 9 8 / 34%) 55%, rgb(11 9 8 / 12%) 78%,
                              rgb(11 9 8 / 42%) 100%);
  }
  .stage__caps h3 { max-width: 13ch; }

  /* The order bar's job on a phone is the ACTION. "Cerrado ahora · abre mañana
     a las 7:30 a.m." wrapped to three lines and doubled the bar's height for
     information the opener, the menu and the visit section all already carry. */
  .orderbar .state { display: none; }
  .orderbar .btn { flex: 1 1 auto; }
}

@media (min-width: 60rem) {
  .stage__caps { padding-bottom: var(--s-7); }
  .opener { padding-bottom: var(--s-6); }
}
