/* ==================================================================
   TRATTORIA SORRENTO — Photo grid
   ------------------------------------------------------------------
   The layout of the original trattoriasorrento.com home page, dressed in
   this site's type and colour: photos and solid tiles side by side with
   thin even gaps. Photos carry no text; words live in the tiles.

   Loaded after style.css on Home, About, Gallery and Contact only.

     .mosaic           the grid: 6 columns on desktop, 2 on tablets, 1 on phones
     .span-2/3/4/6     how many of the 6 desktop columns a block takes
     .rows-2           two rows tall (desktop)
     .m-photo          a photo block; --h sets its minimum height as a
                       fraction of the page width, --pos its focal point,
                       --ratio its shape on phones
     .m-tile           a solid tile for words: --navy or --linen
     .m-stack          a short photo above a tile, inside a .span-4
     .m-hero           the logo photo at the top of the home page

   Motion (bottom of this file) retunes the site's own reveal vocabulary
   for tiles: rows assemble left to right, photos unveil from navy and
   settle, words follow their tile, photos drift with depth.
   ================================================================== */

.mosaic {
  --gap: clamp(8px, 1.1vw, 16px);
  /* Page width, capped at the shell: block heights scale with it and stop
     growing once the shell stops growing. */
  --unit: min(100vw, var(--shell));
  /* One long, soft ease-out for everything that arrives. */
  --m-ease: cubic-bezier(0.22, 1, 0.36, 1);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gap);
  width: min(100% - 2rem, var(--shell));
  margin-inline: auto;
  padding-block: var(--gap) clamp(3.5rem, 7vw, 6rem);
}

/* Keyboard focus and in-page jumps stop clear of the sticky header. */
html { scroll-padding-top: 6rem; }

/* ------------------------------------------------------------------
   Photos
   ------------------------------------------------------------------ */
.m-photo {
  position: relative;
  overflow: hidden;
  margin: 0;
  min-width: 0;
  min-height: calc(var(--unit) * var(--h, 0.22));
  background: var(--band);
  isolation: isolate;
}

/* The picture fills its block whatever the block's height. site.js wraps a
   revealing photo's contents in .reveal-frame, which fills the block too. */
/* JPEG XL photos sit in a <picture> (WebP fallback); it lays out as if it
   weren't there. */
.mosaic picture { display: contents; }

.m-photo > img,
.m-photo picture > img,
.m-hero__media picture > img,
.m-photo > .reveal-frame,
.m-photo > .reveal-frame > img,
.m-hero__media,
.m-hero__media > img,
.m-hero__media > .reveal-frame,
.m-hero__media > .reveal-frame > img {
  position: absolute;
  inset: 0;
}
/* style.css positions .reveal-frame relatively with a percentage height,
   which collapses inside a block sized by min-height. */
.js .m-photo[data-anim] > .reveal-frame,
.js .m-hero__media[data-anim] > .reveal-frame {
  position: absolute;
  height: auto;
}

.m-photo img:not(.m-hero__logo) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
}

/* A navy wash over a photo, so white type on it holds its contrast. */
.m-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(10, 26, 49, 0.42);
  pointer-events: none;
}

/* ------------------------------------------------------------------
   Tiles
   ------------------------------------------------------------------ */
.m-tile {
  --pad: clamp(1.75rem, 3.4vw, 3.25rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  min-width: 0;
  padding: var(--pad);
  overflow-wrap: break-word;
}
/* A tile whose content reads from the top rather than the middle. */
.m-tile--top { justify-content: flex-start; }
.m-tile > :last-child { margin-bottom: 0; }

/* Navy tiles also carry .section-band in the markup, so the site's own
   rules for buttons, facts, the marquee and dish cards on navy apply. */
.m-tile--navy {
  background: var(--band);
  color: var(--band-ink);
}
.m-tile--linen {
  background: var(--canvas-alt);
  color: var(--ink);
}

.m-tile p,
.m-tile .section-intro { max-width: 52ch; }
.m-tile--linen p,
.m-tile--linen .section-intro { color: var(--ink-soft); }

.mosaic .section-title { font-size: clamp(2rem, 3.1vw, 3.1rem); }
.mosaic .page-title { font-size: clamp(2.6rem, 5vw, 4.75rem); margin-bottom: 0.3em; }

.m-eyebrow {
  display: block;
  margin: 0 0 1.1rem;
  font-family: var(--font-body);
  font-size: 0.72rem;
  line-height: 1.4;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.m-tile--navy .m-eyebrow,
.m-hero .m-eyebrow { color: var(--band-soft); }

/* A short lemon bar under a page title (section titles draw their own). */
.m-rule {
  display: block;
  width: 3.2rem;
  height: 2px;
  margin: 0.2rem 0 1.5rem;
  background: var(--limone);
}

.m-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.75rem;
}

/* ------------------------------------------------------------------
   Netlify pieces inside tiles: marquee, facts, dishes, reviews
   ------------------------------------------------------------------ */
/* The dish marquee runs edge to edge across its tile. */
.m-tile .marquee {
  align-self: stretch;
  margin: 0.6rem calc(-1 * var(--pad)) 0.4rem;
  padding-block: 0.95rem;
}
.m-tile .marquee-group span { font-size: clamp(1.35rem, 2vw, 1.85rem); padding-inline: 1.1rem; }

/* Three lanes of dish names fill the middle of their tile, drifting in
   alternating directions at their own pace; the middle lane is the
   largest and brightest. */
.m-tile .marquee--lanes {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  gap: 0.4rem;
  min-height: 11rem;
  padding-block: 0.9rem;
}
.marquee--lanes .marquee-lane { overflow: hidden; }
.marquee--lanes .marquee-lane .marquee-group span {
  font-size: clamp(1.3rem, 1.75vw, 1.6rem);
  color: var(--band-soft);
  padding-inline: 0.85rem;
}
.marquee--lanes .marquee-lane:nth-child(2) .marquee-group span {
  font-size: clamp(1.9rem, 2.6vw, 2.4rem);
  color: var(--band-ink);
}
.marquee--lanes .marquee-lane:nth-child(1) .marquee-track { animation-duration: calc(var(--lane-dur, 40s) * 1.15); }
.marquee--lanes .marquee-lane:nth-child(2) .marquee-track { animation-duration: calc(var(--lane-dur, 32s) * 1.3); animation-direction: reverse; }
.marquee--lanes .marquee-lane:nth-child(3) .marquee-track { animation-duration: calc(var(--lane-dur, 46s) * 1.3); }

.m-tile .facts {
  align-self: stretch;
  margin-top: 0.4rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border: 0;
}
.m-tile .facts div {
  padding: 1.25rem 1rem 1.1rem 0;
  border-right: 0;
  border-bottom: 1px solid var(--band-rule);
}
.m-tile .facts div:nth-last-child(-n + 2) { border-bottom: 0; }
.m-tile .facts dd { font-size: clamp(1.55rem, 2.2vw, 2.15rem); }

.m-tile .featured-grid {
  align-self: stretch;
  margin-top: clamp(1.5rem, 2.5vw, 2.25rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
}
.m-tile .dish-card { padding: clamp(1.4rem, 2.2vw, 1.9rem) clamp(1rem, 1.6vw, 1.4rem); }
.m-tile .dish-card:first-child { padding-left: 0; }
.m-tile .dish-index { margin-bottom: 0.9rem; }
.m-tile .dish-card h3 { font-size: clamp(1.4rem, 1.9vw, 1.8rem); }
.m-tile .dish-card .menu-desc { margin-bottom: 1rem; }

.m-tile.review { justify-content: flex-start; gap: 1rem; }
/* .m-tile--linen p would otherwise grey them out. */
.m-tile .review__stars { color: var(--limone); }
.m-tile.review blockquote { font-size: 1.02rem; }

/* Small text links get a 44px tap area from an invisible pseudo-element,
   so the link itself (and its underline) keeps its size. */
.mosaic .text-link,
.mosaic .review figcaption a { position: relative; }
.mosaic .text-link::after,
.mosaic .review figcaption a::after {
  content: "";
  position: absolute;
  inset: -12px -6px;
}

/* A fact tile: a small label over one large line (About). */
.m-fact { justify-content: flex-end; }
.m-fact .m-eyebrow { margin-bottom: 0.6rem; }
.m-fact .m-fact__value {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(2rem, 3vw, 2.9rem);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--band-ink);
}

/* A guest quote tile (About). */
.m-quote { justify-content: space-between; gap: 1.25rem; }
.m-quote blockquote {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(1.3rem, 1.9vw, 1.65rem);
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.m-quote cite {
  font-style: normal;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Contact: call, text and follow in one tile; hours beside it; the map. */
/* A little snugger than other tiles, so on a laptop the whole first two
   rows (title, call, hours, map) sit above the fold. */
.m-contact,
.m-hours { --pad: clamp(1.5rem, 2.7vw, 2.5rem); }
.m-contact { justify-content: space-between; gap: 1.5rem; }
.m-contact .m-eyebrow { margin-bottom: 0.35rem; }
.m-contact__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(1.45rem, 2.2vw, 1.95rem);
  letter-spacing: -0.025em;
  text-decoration: none;
  color: var(--ink);
}
.m-contact__link:hover { color: var(--price); }
.m-contact .social-row { min-height: 44px; margin-top: 0; color: var(--ink); }

.m-hours__title { font-size: clamp(1.8rem, 2.4vw, 2.3rem); margin-bottom: 0; }
.m-hours .hours-list { align-self: stretch; margin-top: 1rem; font-variant-numeric: tabular-nums; }
.m-hours .hours-list li { padding-block: 0.55rem; }
.m-hours .hours-list li span:last-child { white-space: nowrap; }
.m-hours .note { margin-top: 1rem; }
.m-hours .note a { display: inline-block; padding-block: 0.75rem; margin-block: -0.75rem; }

.m-map { min-height: 320px; border: 0; background: var(--band); }

/* Words that sit on the page itself between rows: a section heading and
   a closing button. */
.m-head { padding: clamp(2.5rem, 5vw, 4.5rem) 0 clamp(0.5rem, 1.5vw, 1.25rem); }
.m-head .section-intro { margin-top: 0.25rem; }
.m-foot { padding-top: clamp(1rem, 2vw, 1.75rem); }

/* ------------------------------------------------------------------
   Gallery: one continuous mosaic; every photo opens large on click
   ------------------------------------------------------------------ */
.gallery-grid.mosaic { columns: auto; column-gap: var(--gap); }

/* The framed-print look of the old masonry gives way to plain blocks. */
.mosaic .gallery-item {
  margin: 0;
  padding: 0;
  border: 0;
  box-shadow: none;
  background: var(--band);
}
.mosaic .gallery-item::before,
.mosaic .gallery-item::after { content: none; }

.mosaic .gallery-item button {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: transparent;
}
.mosaic .gallery-item img {
  height: 100%;
  object-fit: cover;
  transition:
    transform 0.9s var(--m-ease),
    scale 1.7s var(--m-ease) var(--anim-delay, 0ms);
}
.js .mosaic .gallery-item[data-anim]:not(.is-visible) img { scale: 1.08; }
/* The block clips its contents, so the focus ring sits inside the photo. */
.mosaic .gallery-item button:focus-visible { outline-offset: -6px; }

/* Captions sit on the photo, over a soft navy fade. Clicks pass through
   to the photo's button. */
.mosaic .gallery-item figcaption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 2.75rem 1.1rem 0.95rem;
  background: linear-gradient(to top, rgba(6, 18, 35, 0.82), rgba(6, 18, 35, 0));
  color: var(--band-ink);
  font-size: 1.08rem;
  pointer-events: none;
  transition: translate 0.6s var(--m-ease);
}
.mosaic .gallery-item .gallery-num { color: var(--limone); }
/* The reveal rule (Motion, below) owns translate on the frame's children,
   so the hover lift has to match its reach and name translate itself. */
.js .mosaic .gallery-item > .reveal-frame > figcaption {
  transition: translate 0.6s var(--m-ease), scale 1.7s var(--m-ease) var(--anim-delay);
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .js .mosaic .gallery-item.is-visible:hover > .reveal-frame > figcaption { translate: 0 -4px; }
}

/* Chapter tiles open each group of photos. */
.m-chapter { justify-content: flex-end; gap: 0.4rem; }
.m-chapter .gallery-title { font-size: clamp(1.6rem, 2.3vw, 2.2rem); }
.m-chapter .gallery-count { display: block; margin-top: 0.4rem; }

@media (min-width: 1025px) {
  /* Fixed-height rows so the mosaic locks together like the original. */
  .gallery-grid.mosaic { grid-auto-rows: minmax(calc(var(--unit) * 0.2), auto); }
  .gallery-grid .m-photo { min-height: 0; }
  .gallery-grid .m-chapter--lead { grid-column: 5 / 7; grid-row: 1; }
}

/* ------------------------------------------------------------------
   A short photo stacked above a tile, inside one wide block
   ------------------------------------------------------------------ */
.m-stack {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: var(--gap);
  min-width: 0;
}
.m-photo--short { --h: 0.13; }

/* ------------------------------------------------------------------
   The logo photo (home, first row)
   ------------------------------------------------------------------ */
.m-hero {
  display: grid;
  place-items: center;
  min-height: calc(var(--unit) * 0.3);
  text-align: center;
  color: var(--band-ink);
}
.m-hero__media { overflow: hidden; }
.m-hero__content {
  position: relative;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: clamp(2rem, 4vw, 3rem) clamp(1.25rem, 3vw, 2.5rem);
}
.m-hero__logo {
  width: min(78%, 400px);
  height: auto;
  filter: drop-shadow(0 2px 14px rgba(0, 0, 0, 0.35));
}
.m-hero .m-eyebrow {
  margin: 1rem 0 0;
  color: var(--band-ink);
  text-shadow: 0 1px 2px rgba(6, 18, 35, 0.7), 0 0 14px rgba(6, 18, 35, 0.55);
}
.m-hero .m-actions { justify-content: center; margin-top: 1.25rem; }

/* ------------------------------------------------------------------
   Utility
   ------------------------------------------------------------------ */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------------
   Phones (one column): every block full width, photos by shape
   ------------------------------------------------------------------ */
@media (max-width: 640px) {
  .m-photo {
    min-height: 0;
    aspect-ratio: var(--ratio, 4 / 3);
  }
  .m-photo--short { aspect-ratio: 16 / 9; }
  .m-hero { aspect-ratio: auto; min-height: 340px; }
  .m-stack { display: contents; }
  .m-tile .dish-card { padding-inline: 0; border-right: 0; }
}

/* ------------------------------------------------------------------
   Tablets (two columns): photos pair up, tiles run the full width
   ------------------------------------------------------------------ */
@media (min-width: 641px) and (max-width: 1024px) {
  .mosaic {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-flow: row dense;
  }
  .m-photo { min-height: calc(var(--unit) * 0.36); }
  .m-photo--short { min-height: calc(var(--unit) * 0.3); }
  .m-tile,
  .m-hero,
  .mosaic > .span-6 { grid-column: 1 / -1; }
  .m-stack { display: contents; }
  /* The gallery keeps its order (a chapter heading always comes before its
     photos); the last photo of each chapter takes the row on its own. */
  .gallery-grid.mosaic { grid-auto-flow: row; }
  .gallery-grid .m-wide-tablet { grid-column: 1 / -1; }
}

/* ------------------------------------------------------------------
   Desktop (six columns)
   ------------------------------------------------------------------ */
@media (min-width: 1025px) {
  .mosaic { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .span-2 { grid-column: span 2; }
  .span-3 { grid-column: span 3; }
  .span-4 { grid-column: span 4; }
  .span-6 { grid-column: 1 / -1; }
  .rows-2 { grid-row: span 2; }
}

/* ==================================================================
   MOTION
   ================================================================== */
/* Timing. Each block waits for its place in the row (--tile-delay, set
   by staggerMosaic in site.js). */
.js .mosaic [data-anim] { --anim-delay: var(--tile-delay, 0ms); }

/* One piece per block. A photo or a tile is in place from the start,
   under a navy panel carrying the site's lemon sprig; the panel sweeps up
   and away with the lemon edge at its foot. Nothing inside a block moves
   on its own, so a row reads as a few clean sweeps. (The photo stays
   painted underneath, so it counts as the page's main content at once.) */
.js .mosaic [data-anim="clip"] > .reveal-frame,
.js .mosaic [data-anim="clip"] > .reveal-frame > * {
  translate: none;
  transition: scale 1.7s var(--m-ease) var(--anim-delay);
}
.js .mosaic [data-anim="clip"] > .reveal-frame { transition: scale 0.9s var(--m-ease); }
.js .mosaic [data-anim="clip"] > .reveal-frame > picture > img {
  transition: scale 1.7s var(--m-ease) var(--anim-delay);
}
.js .mosaic [data-anim="clip"]:not(.is-visible) > .reveal-frame > img,
.js .mosaic [data-anim="clip"]:not(.is-visible) > .reveal-frame > picture > img { scale: 1.06; }
.js .mosaic [data-anim="clip"] > .reveal-frame::after { display: none; }

/* Tiles hold their panel like photos do. */
.js .mosaic .m-tile[data-anim] {
  position: relative;
  overflow: hidden;
  opacity: 1;
  transform: none;
  transition: none;
}

.js .mosaic [data-anim="clip"]::before,
.js .mosaic .m-tile[data-anim]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  width: auto;
  height: auto;
  border: 0;
  border-bottom: 3px solid var(--limone);
  background: var(--band) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 240' fill='none' stroke='%23e9b824' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M100 62c34 0 60 30 60 72s-26 72-60 72-60-30-60-72 26-72 60-72z'/%3E%3Cpath d='M100 40v22'/%3E%3Cpath d='M104 44c14-16 34-20 52-14-4 20-20 34-40 34-6 0-10-2-12-4-2-4-2-10 0-16z'/%3E%3Cpath d='M96 46C84 32 66 28 50 33c4 18 18 30 36 30 5 0 9-2 11-4 2-3 2-8-1-13z'/%3E%3C/svg%3E") center / clamp(44px, 16%, 92px) auto no-repeat;
  box-shadow: 0 4px 16px 2px color-mix(in srgb, var(--limone) 55%, transparent);
  pointer-events: none;
  translate: 0 4px;
  transition: translate 1.05s var(--m-ease) var(--anim-delay);
}
/* Far enough past the top edge that the lemon glow leaves with it. */
.js .mosaic [data-anim="clip"].is-visible::before,
.js .mosaic .m-tile[data-anim].is-visible::before { translate: 0 calc(-100% - 28px); }

/* Everything inside a block, and the headings that sit on the page between
   rows, is simply there: no words rising, no lines drawing in. */
.js .mosaic .m-tile [data-anim],
.js .mosaic .m-tile [data-anim="mask"] .word > span,
.js .mosaic .m-head [data-anim],
.js .mosaic .m-head [data-anim="mask"] .word > span {
  opacity: 1;
  transform: none;
  transition: none;
}
.js .mosaic .section-title[data-anim="mask"]::after { transform: none; transition: none; }

.js .mosaic [data-anim].is-visible { transform: none; }

/* The logo photo: the logo fades up, then the line beneath it, then the
   buttons, on a short timer of their own (pure CSS), so the Reserve button
   never waits on the photo, its download or site.js. On a quick connection
   the photo unveils underneath them at the same moment. */
.js .m-hero__content > :not(h1) {
  animation: m-hero-in 1.1s var(--m-ease) var(--hero-delay, 0.3s) both;
}
.js .m-hero__content > .m-eyebrow { --hero-delay: 0.48s; }
.js .m-hero__content > .m-actions { --hero-delay: 0.62s; }
/* Arriving under the page curtain, the words wait for it to clear. */
html.arrive .m-hero__content > :not(h1) { animation-delay: calc(var(--hero-delay, 0.3s) + 0.35s); }
@keyframes m-hero-in {
  from { opacity: 0; translate: 0 12px; }
  to { opacity: 1; translate: 0 0; }
}

/* Depth: with a mouse or trackpad, photos marked data-depth sit a little
   taller than their block so site.js can drift them without an edge ever
   showing. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .m-photo[data-depth] img {
    top: -6%;
    bottom: auto;
    height: 112%;
    will-change: transform;
  }
}

/* Hover: the photo leans in, slowly. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .js .mosaic .m-photo:not(.gallery-item).is-visible:hover > .reveal-frame { scale: 1.04; }
}

/* Phones: quicker, shorter motion. One column scrolls past fast, so every
   reveal is briefer and travels less than on a computer. */
@media (max-width: 640px) {
  .js .mosaic [data-anim="clip"]::before,
  .js .mosaic .m-tile[data-anim]::before { transition-duration: 0.85s; }
  .js .mosaic [data-anim="clip"]:not(.is-visible) > .reveal-frame > img,
  .js .mosaic [data-anim="clip"]:not(.is-visible) > .reveal-frame > picture > img { scale: 1.05; }
}

@media (prefers-reduced-motion: reduce) {
  .m-photo img { transform: none !important; scale: none; }
  .js .mosaic [data-anim="clip"] > .reveal-frame,
  .js .mosaic [data-anim="clip"] > .reveal-frame > * { translate: none; scale: none; }
  .js .mosaic [data-anim="clip"]::before,
  .js .mosaic .m-tile[data-anim]::before { display: none; }
  .js .mosaic [data-anim],
  .js .mosaic [data-anim] .word > span,
  .js .mosaic [data-anim="clip"] > .reveal-frame,
  .js .mosaic [data-anim="clip"] > .reveal-frame > * {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .js .m-hero__content > :not(h1) { animation: none; }
}

/* Holiday biscotti price */
.m-price {
  margin: 0.25rem 0 1.25rem;
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--ink);
}
.m-tile--linen .m-price { color: var(--ink); }
.m-price strong {
  font-size: 2.1rem;
  font-weight: 500;
  margin-right: 0.3rem;
}
