/* ==========================================================================
   Menu Page Styles
   Core styling for menu items, categories, and secondary navigation.
   ========================================================================== */

/* ==========================================================================
   1. Card Layout & Base
   ========================================================================== */

.api-items-grid {
  align-items: stretch;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(2, 1fr);
  justify-items: center;
}

.api-items-container .section-title {
  /* Clears the sticky main header (~80px) plus the anchor-nav strip (~44px). */
  scroll-margin-top: 8.5rem;
}

/* Page-end rhythm: the last category's own padding-bottom is the whole gap to
   the footer, so the footer adds nothing. That padding needs the :last-of-type
   override below — the global `section:last-of-type { padding-bottom: 0 }`
   outranks a bare class selector, which used to zero it and leave the last
   row of prices flush against the footer. */
footer {
  margin-top: 0;
}

/* Tile: text panel left, photo (or placeholder) right. Every tile is imaged,
   so names, prices, and photos each sit on one rail per grid column. */
/* The seigaiha tile ships pre-blended (wave.jpg overlay-composited onto
   --color-parchment) rather than blended at paint time. iOS Safari does not
   apply background-blend-mode here, so the raw navy pattern came through at
   full strength on phones while desktop showed the intended wash. Baking the
   blend removes the dependency. Keep background-color as the fallback layer;
   do not reintroduce background-blend-mode. */
.item-card {
  background-color: var(--color-parchment);
  background-image: url("/static/wave-parchment.webp?v=1786769922988");
  background-repeat: repeat;
  border-radius: var(--radius-medium);
  box-shadow: var(--elevation-2);
  box-sizing: border-box;
  display: grid;
  grid-template-columns: 1fr 38%;
  margin: 0;
  min-height: 11.5rem;
  overflow: hidden;
  position: relative;
  width: 100%;
}

.item-card::before {
  background-color: rgba(243, 240, 235, 0.736);
  border-radius: var(--radius-medium);
  bottom: 0;
  content: '';
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
  z-index: 1;
}

/* Text-only category: no item has a photo, so drop the image column and let
   cards size to content. Scoped to the grid class (0,2,0) so it beats the
   responsive .item-card rules at every breakpoint. */
.api-items-grid--text-only .item-card {
  grid-template-columns: 1fr;
  min-height: 0;
}

/* ==========================================================================
   2. Item Details & Content
   ========================================================================== */

.item-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--spacing-4) 1.1rem;
  position: relative;
  z-index: 2;
}

/* The photo fills the tile; it must not size it. As a grid item an <img>
   contributes its intrinsic height at the column's width, so a portrait dish
   photo stretched the whole row — a 400x800 photo turned a 184px tile into a
   432px one — and every dish ended up a different height. It showed up on
   Android Chrome first because the single-column layout makes the photo
   column wide enough for that contribution to clear the tile's floor. The
   frame is an empty box that stretches to the row, and the photo is painted
   inside it out of flow, so the tile is sized by its text and by
   .item-card's min-height, and nothing else. */
.item-photo-frame {
  /* A shape floor, not a fixed shape. The frame still stretches to whatever
     the row ends up being, but it contributes width / 1.5 to that row, so the
     tile grows as the photo column widens. Without it the single-column
     layout above ~800px held the 11.5rem floor while the column stretched to
     ~360px, flattening the photo into a 2:1 strip beside a text panel that
     was mostly empty on items with no description. Every tile in a layout
     shares one column width, so they all contribute the same height and the
     rows stay uniform. */
  aspect-ratio: 3 / 2;
  /* Both are load-bearing with the ratio above, because a ratio-derived size
     suppresses the default stretch on that axis. Without the height the frame
     sat 3/2 tall in a taller row and left a parchment gap under the photo;
     with the height alone the ratio ran the other way and sized the WIDTH off
     it, overflowing the 38% track by ~90px into the text. Pinned on both axes
     the ratio no longer decides the used size — it only supplies the height
     the frame contributes while the row is being sized, which is the one job
     it has here. */
  height: 100%;
  overflow: hidden;
  position: relative;
  width: 100%;
  z-index: 2;
}

.item-photo {
  display: block;
  height: 100%;
  inset: 0;
  object-fit: cover;
  position: absolute;
  width: 100%;
}

.item-photo--real {
  cursor: pointer;
  transition: transform 0.5s ease;
}

.item-photo--real:hover {
  transform: scale(1.04);
}

/* Typography */
.item-title,
.description,
.ingredients,
.variation-type {
  overflow-wrap: break-word;
  word-wrap: break-word;
}

.item-title {
  font-size: var(--font-size-medium);
  font-weight: bold;
  line-height: 1.3;
}

.description {
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  display: -webkit-box;
  font-size: var(--font-size-base);
  margin: 0.3rem 0 0;
  opacity: 0.75;
  overflow: hidden;
}

.clickable-image {
  cursor: pointer;
}

/* Variations & Prices — every price sits on a dotted-leader row; the group
   anchors to the tile bottom so prices read from a consistent bottom-right. */
.item-prices {
  margin-top: auto;
  padding-top: var(--spacing-2);
}

.variation-row {
  align-items: baseline;
  display: flex;
  font-size: var(--font-size-base);
  gap: var(--spacing-2);
  margin-top: 0.25rem;
}

.variation-type {
  opacity: 0.75;
}

.price-leader {
  border-bottom: 1.5px dotted rgba(166, 127, 91, 0.55);
  flex: 1;
  min-width: 1.1rem;
  transform: translateY(-0.28em);
}

.variation-price {
  font-weight: bold;
  white-space: nowrap;
}

/* ==========================================================================
   3. Combination & Category Containers
   ========================================================================== */

/* Combination tile: the static combo section rendered in the same tile
   language as the API cards — text left, photo right, price on a dotted
   leader in the title row. */
/* Pre-blended tile — see the note on .item-card above. */
.combo-card {
  background-color: var(--color-parchment);
  background-image: url("/static/wave-parchment.webp?v=1786769922988");
  background-repeat: repeat;
  border-radius: var(--radius-medium);
  box-shadow: var(--elevation-2);
  box-sizing: border-box;
  display: grid;
  /* Same photo column as .item-card so the photo rail lines up down the page. */
  grid-template-columns: 1fr 38%;
  overflow: hidden;
  position: relative;
  width: 100%;
}

.combo-card::before {
  background-color: rgba(243, 240, 235, 0.736);
  border-radius: var(--radius-medium);
  bottom: 0;
  content: '';
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
  z-index: 1;
}

.combo-card picture {
  height: 100%;
  min-height: 24rem;
  position: relative;
  width: 100%;
  z-index: 2;
}

.combo-photo {
  display: block;
  height: 100%;
  min-height: 100%;
  object-fit: cover;
  position: relative;
  width: 100%;
}

.combo-body {
  display: flex;
  flex-direction: column;
  padding: var(--spacing-6);
  position: relative;
  z-index: 2;
}

/* Title, dotted leader, and price share one baseline; if the row runs out of
   room the leader-and-price pair wraps to its own full-width line, matching
   the unlabeled leader rows on the item tiles. */
.combo-title-row {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-3);
}

.combo-title {
  color: var(--color-carbon-black);
  display: block;
  font-family: var(--font-display);
  font-size: var(--font-size-extra-large);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: var(--line-height-tight);
  margin: 0;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}

.combo-leader {
  border-bottom: 0.125rem dotted rgba(166, 127, 91, 0.55);
  flex: 1;
  min-width: 2rem;
  transform: translateY(-0.35em);
}

.combo-price {
  color: var(--color-faded-copper);
  font-family: var(--font-display);
  font-size: var(--font-size-extra-large);
  font-weight: 600;
  white-space: nowrap;
}

.combo-description {
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  margin: var(--spacing-4) 0 var(--spacing-5);
  max-width: 34rem;
  opacity: 0.85;
}

.combo-options {
  display: grid;
  gap: var(--spacing-6);
  /* Fits however many steps the combo has (lunch: 2, dinner: 3). */
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  margin-top: auto;
}

.combo-option-heading {
  border-bottom: 0.0625rem solid rgba(166, 127, 91, 0.25);
  color: var(--color-faded-copper);
  font-family: var(--font-primary);
  font-size: var(--font-size-extra-small);
  font-weight: 500;
  letter-spacing: 0.16em;
  margin: 0 0 var(--spacing-3);
  padding-bottom: var(--spacing-2);
  text-align: left;
  text-transform: uppercase;
}

.combo-option-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.combo-option-list li {
  font-size: var(--font-size-base);
  line-height: 1.5;
  margin: 0 0 0.45rem;
}

/* Steps where the guest picks exactly one dish: an inline "or" prefixes every
   choice after the first, mirroring the printed menu. */
.combo-option-list--or li + li::before {
  color: var(--color-faded-copper);
  content: "or ";
  font-style: italic;
}

/* A category heading gets the same gap above and below it, so it reads as
   sitting between two card groups rather than drifting toward one. The space
   above is this section's padding-bottom (the previous category owns it); the
   space below is the heading's own margin. Both are the one token. */
.menu-category-section {
  display: block;
  padding-bottom: var(--stack-heading);
}

/* The ornament is decoration that belongs INSIDE the gap below the text, not
   part of the heading, so its footprint is subtracted from the margin. Without
   that it adds its own height to the box and the text sits off-centre between
   the two card groups — 39px above but 66px below. Both the ::after and the
   margin derive from the same two values, so resizing the ornament keeps the
   text centred instead of silently shifting it. */
.menu-category-section .section-title {
  --ornament-height: 2.2rem;
  --ornament-overlap: 0.625rem; /* how far it tucks up under the text */
  font-size: var(--font-size-display-small);
  letter-spacing: 0.03em;
  margin-bottom: calc(var(--stack-heading) - var(--ornament-height) + var(--ornament-overlap));
}

.menu-category-section .section-title::after {
  height: var(--ornament-height);
  margin-top: calc(var(--ornament-overlap) * -1);
  width: 11rem;
}

/* Drop the leading gap above the first category so the menu starts tighter
   under the pill bar. Scoped to menu sections so the global
   section:first-of-type padding (used by the home page) is untouched. */
.menu-category-section:first-of-type {
  padding-top: 0;
}

/* The mirror of the rule above, and it has to be written this way: the global
   `section:last-of-type` (0,1,1) outranks a bare `.menu-category-section`
   (0,1,0), so without a class+pseudo selector here the last category loses its
   padding and the final prices touch the footer. */
.menu-category-section:last-of-type,
.menu-category-section:last-child {
  padding-bottom: var(--stack-heading);
}
/* ==========================================================================
   4. Secondary Navigation (.anchor-nav)
   ========================================================================== */

/* Zero-cost IntersectionObserver target placed just above .anchor-nav. 1px tall
   so it tracks reliably, pulled up so it adds no visible space between the
   banner and the bar. */
.anchor-nav-sentinel {
  height: 1px;
  margin-bottom: -1px;
  visibility: hidden;
}

/* The quick-nav sits flush under the hero banner — the banner's 2rem bottom
   margin only applies on menu pages without a category bar. */
.banner-with-text:has(+ main > .anchor-nav) {
  margin-bottom: 0;
}

/* Quiet underline-tab strip: a single horizontally-scrolling row of text
   links at every viewport. The active section gets the same copper underline
   the header nav uses for the current page. */
.anchor-nav {
  -webkit-backdrop-filter: blur(0.75rem);
  align-items: center;
  backdrop-filter: blur(0.75rem);
  background-color: rgba(243, 240, 235, 0.92);
  border-bottom: 0.0625rem solid rgba(0, 0, 0, 0.08);
  box-sizing: border-box;
  display: flex;
  flex-wrap: nowrap;
  gap: 1.5rem;
  margin: 0 0 var(--spacing-6) 0;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 0.625rem 1rem;
  position: sticky;
  scrollbar-width: none;
  /* Flush under the scrolled header: 72px content + 1px border, so the bar
     tucks 1px beneath the header's bottom edge (header z-index is higher). */
  top: 4.5rem;
  transition: box-shadow 0.25s ease;
  width: 100%;
  z-index: 999;
}

/* Subtle lift only while pinned (.stuck via menu.js). */
.anchor-nav.stuck {
  box-shadow: var(--elevation-1);
}

.anchor-nav::-webkit-scrollbar {
  display: none;
}

.anchor-nav a {
  border-bottom: 0.125rem solid transparent;
  color: var(--color-coffee-bean);
  flex: 0 0 auto;
  padding: 0.3rem 0.125rem;
  text-decoration: none;
  transition: color 0.2s ease, border-color 0.2s ease;
  white-space: nowrap;
}

/* Center the group when it fits the bar; the auto margins collapse to 0
   once the links overflow, so scrolling still starts from the left edge. */
.anchor-nav a:first-child {
  margin-left: auto;
}

.anchor-nav a:last-child {
  margin-right: auto;
}

.anchor-nav a:hover {
  color: var(--color-carbon-black);
}

.anchor-nav a.active {
  border-bottom-color: var(--color-faded-copper);
}

.anchor-nav a.active h2 {
  color: var(--color-carbon-black);
  font-weight: 700;
}

.anchor-nav h2 {
  color: inherit;
  font-size: var(--font-size-base);
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1;
  margin: 0;
}

/* ==========================================================================
   4b. Menu Status Card (empty menu / load failure)
   ========================================================================== */

.menu-status-box {
  background-color: var(--color-parchment);
  border: 0.0625rem solid rgba(166, 127, 91, 0.25);
  border-radius: var(--radius-medium);
  box-shadow: var(--elevation-1);
  /* Symmetric, on the same rhythm as a category heading — this box stands in
     for a whole category, so it should sit in the flow the same way one does. */
  margin: var(--stack-heading) auto;
  max-width: 30rem;
  padding: var(--spacing-6) var(--spacing-5);
  text-align: center;
}

.menu-status-message {
  font-size: var(--font-size-medium);
  font-weight: 600;
  margin-bottom: 0.75rem;
}

.menu-status-phone a {
  color: var(--color-faded-copper);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}

/* ==========================================================================
   5. Image Preview Modal
   ========================================================================== */

.image-preview-modal {
  align-items: center;
  background-color: rgba(0, 0, 0, 0.88);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-sizing: border-box;
  display: none;
  height: 100%;
  justify-content: center;
  left: 0;
  overflow-y: auto;
  padding: 1.5rem 1rem;
  position: fixed;
  top: 0;
  width: 100%;
  z-index: 1000;
}

.image-preview-modal.is-open {
  display: flex;
}

.modal-wrapper {
  align-items: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin: auto;
  max-height: calc(100vh - 2.5rem);
  max-width: min(92vw, 68rem);
  width: 100%;
}

.modal-image-container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
}

.modal-content {
  animation-duration: 0.4s;
  animation-name: zoom;
  border: 0.0625rem solid #e6b345; /* Yellow/gold outline matching central hero image */
  border-radius: 0.75rem; /* Curved angles */
  box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.85);
  display: block;
  max-height: clamp(45vh, 62vh, 72vh);
  max-width: 100%;
  object-fit: contain;
  overflow: hidden;
}

.modal-details {
  background: rgba(22, 18, 15, 0.94);
  border: 0.0625rem solid rgba(230, 179, 69, 0.4);
  border-radius: 0.75rem; /* Matching curved angles */
  box-sizing: border-box;
  flex: 0 0 auto;
  margin-top: 0.875rem;
  max-height: 30vh;
  overflow-y: auto;
  padding: clamp(1rem, 1.2vw + 0.6rem, 1.6rem);
  text-align: left;
  width: 100%;
}

.modal-title-row {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.25rem;
}

.modal-title {
  color: var(--color-pure-white, #ffffff);
  font-family: var(--font-primary);
  font-size: clamp(1.2rem, 1.3vw + 0.7rem, 1.85rem);
  font-weight: 600;
  margin: 0;
}

.modal-price {
  color: #e6b345;
  font-family: var(--font-primary);
  font-size: clamp(1.05rem, 1.1vw + 0.6rem, 1.55rem);
  font-weight: 600;
  white-space: nowrap;
}

.modal-description {
  color: rgba(255, 255, 255, 0.9);
  font-family: var(--font-primary);
  font-size: clamp(0.9rem, 0.6vw + 0.7rem, 1.18rem);
  line-height: 1.5;
  margin: 0.35rem 0 0;
}

/* Base close button — must stay ahead of the media queries below, which share
   its specificity and would otherwise lose to it on source order. */
.close-preview {
  color: #f1f1f1;
  font-size: var(--font-size-3x-large);
  font-weight: bold;
  position: absolute;
  right: 2.1875rem;
  top: 0.9375rem;
  transition: 0.3s;
  z-index: 1001;
}

.close-preview:hover,
.close-preview:focus {
  color: #e6b345;
  cursor: pointer;
  text-decoration: none;
}

/* Large Desktop Monitors (1200px+ width, 800px+ height) */
@media (min-width: 1200px) and (min-height: 800px) {
  .modal-wrapper {
    max-width: min(88vw, 78rem);
  }

  .modal-content {
    max-height: 68vh;
  }
}

/* Mobile Devices (screens 600px and under) */
@media (max-width: 37.5rem) {
  .image-preview-modal {
    padding: 1rem 0.75rem;
  }

  .modal-wrapper {
    max-width: 100%;
    max-height: calc(100vh - 2rem);
  }

  .modal-content {
    max-height: clamp(36vh, 48vh, 55vh);
    border-radius: 0.625rem;
  }

  .modal-details {
    border-radius: 0.625rem;
    margin-top: 0.625rem;
    padding: 0.875rem 1rem;
  }

  .close-preview {
    top: 1rem;
    right: 1rem;
    width: 2.5rem;
    height: 2.5rem;
    background: rgba(30, 24, 20, 0.75);
    border: 0.0625rem solid rgba(230, 179, 69, 0.3);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.35rem;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    line-height: 1;
  }
}

@keyframes zoom {
  from {
    opacity: 0;
    transform: scale(0.85);
  }

  to {
    opacity: 1;
    transform: scale(1);
  }
}

/* ==========================================================================
   6. Media Queries & Responsive Logic
   ========================================================================== */

/* Large Tablets & Desktop Grid */
@media (max-width: 65rem) {
  .api-items-grid {
    grid-template-columns: 1fr;
  }
  .menu-category-section .section-title {
    font-size: var(--font-size-extra-large);
  }
  .description {
    -webkit-line-clamp: unset;
    display: block;
    overflow: visible;
  }
}

/* Mobile Phones & Stacking Logic */
@media (max-width: 37.5rem) {
  .api-items-grid {
    gap: 0.75rem;
    grid-template-columns: 1fr;
  }

  /* Narrower photo panel so the text keeps a workable measure. */
  .item-card {
    grid-template-columns: 1fr 34%;
    min-height: 9.5rem;
  }

  .item-body {
    padding: 0.75rem 0.9rem;
  }

  /* Combination tile collapses to a single column, photo on top. */
  .combo-card {
    grid-template-columns: 1fr;
  }

  .combo-card picture {
    min-height: 14rem;
    order: -1;
  }

  .combo-photo {
    min-height: 14rem;
  }

  .combo-title {
    white-space: normal;
  }

  .combo-options {
    gap: var(--spacing-4);
    grid-template-columns: 1fr;
  }

  .description {
    font-size: var(--font-size-extra-small);
    line-height: 1.4;
  }

  .section-title {
    margin-bottom: 1.5rem;
  }
}

/* ==========================================================================
   7. Skeleton Loading State
   ========================================================================== */

.skeleton-container {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(2, 1fr);
  width: 100%;
}

@media (max-width: 65rem) {
  .skeleton-container {
    grid-template-columns: 1fr;
  }
}

/* Mirrors the tile shape: text block left, photo block right. */
.skeleton-card {
  background-color: var(--color-parchment);
  border-radius: var(--radius-medium);
  height: 11.5rem;
  overflow: hidden;
  padding: 1rem calc(38% + 1rem) 1rem 1rem;
  position: relative;
}

.skeleton-photo {
  bottom: 0;
  position: absolute;
  right: 0;
  top: 0;
  width: 38%;
}

.skeleton-pulse {
  animation: pulse 1.5s infinite ease-in-out;
  background: linear-gradient(90deg,
      rgba(166, 127, 91, 0.05) 25%,
      rgba(166, 127, 91, 0.1) 50%,
      rgba(166, 127, 91, 0.05) 75%);
  background-size: 200% 100%;
}

@keyframes pulse {
  0% {
    background-position: 200% 0;
  }

  100% {
    background-position: -200% 0;
  }
}

/* Safari ghosting fix (menu.js): the pulse animation is frozen and the
   container hidden before the skeleton DOM is cleared, so the compositor
   drops the ghost layers instead of leaving a stale image behind. */
.skeleton-frozen {
  animation: none;
}

.skeleton-container.is-hidden {
  display: none;
}

.skeleton-title {
  border-radius: var(--radius-small);
  height: 1.5rem;
  margin-bottom: 1rem;
  width: 60%;
}

.skeleton-desc {
  border-radius: var(--radius-small);
  height: 1rem;
  margin-bottom: 0.5rem;
  width: 90%;
}

.skeleton-desc:last-child {
  width: 70%;
}

.skeleton-price {
  border-radius: var(--radius-small);
  bottom: 1rem;
  height: 1.25rem;
  position: absolute;
  right: calc(38% + 1rem);
  width: 3rem;
}
