/* ============================================================
   Autumn Scoop — Mobile app shell (v7)
   Loaded AFTER styles.css on both index.html and cake.html.

   Everything phone-specific lives here so styles.css stays the
   desktop file it has always been. One breakpoint governs the
   whole layer: 860px — deliberately the same number as
   cake.html's `matchMedia('(max-width: 860px)')`, so CSS and the
   React component can never disagree about what "mobile" means.

   Contents
     0 · Tokens + global quality floor
     1 · Bottom tab bar
     2 · Collapsing header
     3 · Sticky action bar (cake page)
     4 · Bottom sheets
     5 · Full-bleed photo content
     6 · Motion + reduced motion
   ============================================================ */

/* ---------- 0 · Tokens ------------------------------------- */
:root {
  /* Chrome heights. The cake page stacks its action bar on top of
     the tab bar, and page content reserves the sum — nothing is
     ever parked underneath either one. */
  /* 60px, not 58: the illustrated icons are 28px and the Thai labels set on
     two more points of line-height than the Latin ones. */
  --as-tabbar-h:      60px;
  --as-tabbar-block:  calc(var(--as-tabbar-h) + env(safe-area-inset-bottom, 0px));
  --as-actionbar-h:   68px;

  /* Focus ring — Terracotta on cream measures 3.7:1, Vanilla Cream
     on terracotta the same, so both directions clear the 3:1 floor. */
  --as-focus:         var(--terracotta);
  --as-focus-on-deep: var(--vanilla-cream);

  --as-ease:          cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Never `hidden` — it makes every ancestor a scroll container and
   silently kills `position: sticky` on descendants. `clip` stops the
   horizontal overflow without that side effect. */
html, body { overflow-x: clip; }

/* Long words and long headings can't force a horizontal scrollbar. */
h1, h2, h3, h4, h5, h6, p, .store-card__value, .faq-item__btn {
  overflow-wrap: anywhere;
  min-width: 0;
}

/* Visible, never-animated focus ring on everything interactive. */
:where(a, button, input, select, textarea, [role="button"], [tabindex]):focus-visible {
  outline: 2px solid var(--as-focus);
  outline-offset: 2px;
  border-radius: var(--r-1);
}
/* Inside the terracotta nav/story/form fields the cream ring is the
   legible one. */
.nav :focus-visible,
.story :focus-visible,
#cake-order :focus-visible {
  outline-color: var(--as-focus-on-deep);
}

/* Disabled + pressed states for anything that didn't already declare them. */
:where(button, [role="button"]):disabled,
:where(button, [role="button"])[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.55;
}

/* Image-bearing grid tracks: a bare `1fr` is `minmax(auto, 1fr)`, and a
   wide image inflates that `auto` floor until the row overflows the
   viewport. `minmax(0, 1fr)` pins the floor at zero. */
.inside__cards,
.voices__grid,
.series__cards,
.contact__bar { grid-auto-columns: minmax(0, 1fr); }

@media (max-width: 900px) { .inside__cards { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 560px) { .series__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px) { .voices__grid  { grid-template-columns: minmax(0, 1fr); } }


/* ============================================================
   1 · BOTTOM TAB BAR
   Injected by app-shell.js, so `html.js-tabbar` gates every rule
   here: without JS there is no bar and no reserved space for one.
   ============================================================ */
.tabbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 90;                 /* under the sheets (200+), over the page */
  display: none;               /* phone only — enabled in the query below */
  background: var(--vanilla-cream);
  border-top: 1px solid var(--hairline);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  /* Not a shadow: a hairline plus the cream field is how the rest of
     the site separates surfaces (see .faqs__tabs, .series__divider). */
}
.tabbar__inner {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  height: var(--as-tabbar-h);
}
.tabbar__tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 48px;            /* touch target floor */
  padding: 4px 4px 5px;
  color: var(--ink-soft);
  position: relative;
  transition: color 200ms var(--as-ease);
  -webkit-tap-highlight-color: transparent;
}
/* The icons are the brand's own illustrations, so the active state can't be
   carried by `currentColor` the way a stroked glyph's would be. It comes from
   opacity instead — inactive tabs sit back, the active one comes forward —
   plus the Terracotta label and the rule above it. */
.tabbar__icon {
  width: 28px;
  height: 28px;
  flex: none;
  object-fit: contain;
  opacity: 0.5;
  transition: opacity 200ms var(--as-ease);
}
.tabbar__tab.is-active .tabbar__icon { opacity: 1; }
.tabbar__label {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;         /* a wrapped tab label is a bug, not a layout */
}
/* Active state: Terracotta type on the Vanilla Cream field, marked by the
   same short rule the site already uses for .faqs__tab and .eyebrow-mark —
   the brand's own vocabulary rather than a generic pill or blob. */
.tabbar__tab.is-active { color: var(--terracotta); }
.tabbar__tab.is-active::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 26px;
  height: 2px;
  background: var(--terracotta);
}
.tabbar__tab:active { color: var(--terracotta); opacity: 0.7; }
@media (hover: hover) {
  .tabbar__tab:hover { color: var(--ink); }
  .tabbar__tab.is-active:hover { color: var(--terracotta); }
}
.tabbar__tab:focus-visible { outline-offset: -3px; }

/* Bilingual without JS: both strings ship in the markup and the
   document's own `lang` attribute — which index.html's applyLang() and
   cake.html's setNavLang() both already set — picks one. */
.tabbar__label [lang="th"] { display: none; }
html[lang="th"] .tabbar__label [lang="en"] { display: none; }
html[lang="th"] .tabbar__label [lang="th"] { display: inline; }
/* Thai needs its own metrics here: the tracked 10px/line-height-1 setting is
   built for Latin small caps, and it pushes Thai tone marks (the ้ in หน้าหลัก)
   up into the icon while making the glyphs hard to read. Thai has no uppercase,
   so the transform is simply dropped rather than fought. */
html[lang="th"] .tabbar__label {
  font-family: "IBM Plex Sans Thai", var(--font-body);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0;
  text-transform: none;
}

@media (max-width: 860px) {
  html.js-tabbar .tabbar { display: block; }

  /* Reserve the bar's height so nothing on any page can hide behind it. */
  html.js-tabbar body { padding-bottom: var(--as-tabbar-block); }

  /* Body padding only adds room after the last section — a 100dvh hero still
     runs the full screen height and puts its CTA under the bar. The hero ends
     where the chrome begins instead. */
  html.js-tabbar .hero { min-height: calc(100dvh - var(--as-tabbar-block)); }

  /* Anchor targets must clear the fixed header, and now also the bar. */
  html { scroll-padding-bottom: var(--as-tabbar-block); }

  /* Header controls came in under the floor too: the hamburger at 38px and the
     EN|TH toggle at 44px (styles.css sets 44 for the ≤1024 tablet case). */
  .nav__menu-btn {
    min-width: 48px;
    min-height: 48px;
    align-items: center;
    justify-content: center;
  }
  .lang-toggle { min-height: 48px; min-width: 48px; }

  /* Index's own controls were all under the floor: flavor filter chips at 39px,
     FAQ tabs at 31px, contact links at 31px, the Instagram link at 26px.
     Grown with padding rather than min-height so the filter chip keeps its
     baseline-aligned count, and so the FAQ tab's underline — positioned 16px
     below the tab, against the container's 16px bottom padding — keeps landing
     exactly on the container's rule. */
  .flavors__filter { padding-block: 15px; }
  .faqs__tab { padding-block: 15px; }
  /* The active-tab underline is anchored 16px below the tab so that on desktop,
     where the four tabs sit on one row, it lands on the container's rule. These
     tabs wrap to two rows on a phone, where that same offset leaves the mark
     floating in the gap between rows — so here it hugs its own label instead. */
  .faqs__tab::after { bottom: 10px; }

  /* These two carry a border-bottom that reads as the link's underline, so the
     extra target has to grow upward: flex-end keeps text and underline where
     they are and puts the added tap area above them. */
  .contact__bar-value,
  .gallery__follow {
    min-height: 48px;
    align-items: flex-end;
  }

  /* The dropdown's own links measured 47px — one pixel under the floor.
     This has to come BEFORE the hide rules below: `.nav__links .nav__link--cake`
     carries the same specificity as `.nav__links .nav__link`, so if this rule
     came second it would win the tie and put the Cake link back. */
  .nav__links .nav__link {
    display: flex;
    align-items: center;
    min-height: 48px;
  }

  /* The four destinations that earned a tab drop out of the hamburger;
     it keeps the secondary ones (Our Story, What's Inside, Voices, FAQs).
     Matched on href so no markup carries a duplicate-of-a-tab flag. */
  .nav__links .nav__link[href="#home"],
  .nav__links .nav__link[href="#flavors"],
  .nav__links .nav__link[href="#contact"],
  .nav__links .nav__link--cake { display: none; }
}


/* ============================================================
   2 · COLLAPSING HEADER
   The class lands on <html>, not on .nav — cake.html's navbar is
   re-rendered by React, which would blow away a class set directly
   on the element. Nothing ever re-renders <html>.
   ============================================================ */
@media (max-width: 860px) {
  .nav {
    transition: transform 260ms var(--as-ease),
                background 280ms var(--as-ease),
                border-color 280ms var(--as-ease);
    will-change: transform;
  }
  html.nav-tucked .nav { transform: translateY(-100%); }
  /* An open hamburger pins the header down — the dropdown hangs off it. */
  html.nav-open .nav { transform: translateY(0); }
}


/* ============================================================
   3 · STICKY ACTION BAR — cake page
   Replaces v6's floating pill, which was `position: fixed` with
   nothing reserving space beneath it and so sat on top of the
   gallery photos, the flavor grid, and the delivery form.
   ============================================================ */
.ck-actionbar {
  position: fixed;
  left: 0; right: 0;
  bottom: var(--as-tabbar-block);   /* stacks on the tab bar, never fights it */
  z-index: 85;
  display: none;
  align-items: center;
  gap: var(--s-3);
  height: var(--as-actionbar-h);
  padding: 0 var(--s-4);
  background: var(--vanilla-cream); /* solid, not translucent — it sits over photos */
  border-top: 1px solid var(--hairline);
}
.ck-actionbar__meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.ck-actionbar__label {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ink-mute);
  white-space: nowrap;
}
.ck-actionbar__value {
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.15;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ck-actionbar__btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  padding: 0 22px;
  border-radius: var(--r-pill);
  background: var(--terracotta);
  color: var(--vanilla-cream);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  white-space: nowrap;
  transition: background 200ms var(--as-ease);
}
.ck-actionbar__btn:active { background: var(--wood-brown); }
@media (hover: hover) {
  .ck-actionbar__btn:hover { background: var(--wood-brown); }
}

/* At 320px the eyebrow-tracked button claimed 203 of 320px and ellipsed the
   progress text next to it — which is the half that actually says anything.
   The button gives the room back rather than the label losing its meaning. */
@media (max-width: 380px) {
  .ck-actionbar { padding: 0 var(--s-3); gap: var(--s-2); }
  .ck-actionbar__btn { padding: 0 14px; letter-spacing: var(--tracking-wide); }
  .ck-actionbar__value { font-size: 16px; }
}

@media (max-width: 860px) {
  body.page-cake .ck-actionbar { display: flex; }
  /* Real reserved space for BOTH bars — this is the fix for the
     photos, the flavor grid and the form fields that v6 covered up. */
  html.js-tabbar body.page-cake {
    padding-bottom: calc(var(--as-tabbar-block) + var(--as-actionbar-h));
  }
}


/* ============================================================
   4 · BOTTOM SHEETS
   The flavor, cake-photo and highlight popups were centered
   dialogs; on a phone they all become sheets. renderMobileSummary
   already worked this way — these classes are that pattern, made
   shared so all four stay identical.
   ============================================================ */
.as-sheet-scrim {
  position: absolute;
  inset: 0;
  background: rgba(43, 29, 20, 0.45);
}
.as-sheet {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  display: flex;
  flex-direction: column;
  max-height: 86dvh;
  background: var(--vanilla-cream);
  border-radius: var(--r-4) var(--r-4) 0 0;   /* top corners only */
  padding: 0 var(--s-4) calc(var(--s-5) + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;               /* scrolling the sheet can't scroll the page */
  animation: asSheet 320ms var(--as-ease) both;
}
/* Grab handle strip — sticky so it stays reachable as the body scrolls. */
.as-sheet__grip {
  position: sticky;
  top: 0;
  z-index: 5;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  margin-bottom: var(--s-2);
  background: var(--vanilla-cream);
}
/* The visible bar stays 46×5; content-box padding grows the tap area around it
   to 70×49 without drawing anything, because the fill is clipped to the
   content box. */
.as-sheet__handle {
  box-sizing: content-box;
  width: 46px;
  height: 5px;
  padding: 22px 12px;
  border-radius: var(--r-pill);
  background: var(--hairline-strong);
  background-clip: content-box;
  cursor: pointer;
}
/* Same idea: a 48×48 target, with the 34px circle drawn behind the glyph so it
   still reads as the small, quiet button it was. */
.as-sheet__close {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 48px; height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  color: var(--ink-soft);
  cursor: pointer;
}
.as-sheet__close::before {
  content: "";
  position: absolute;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--hairline);
  transition: background 180ms var(--as-ease);
}
.as-sheet__close svg { position: relative; }
.as-sheet__close:active::before { background: var(--paper-warm); }
@media (hover: hover) {
  .as-sheet__close:hover::before { background: var(--paper-warm); }
}

/* Body scroll lock while any sheet is open. */
html.as-sheet-open,
html.as-sheet-open body { overflow: hidden; }


/* ============================================================
   5 · FULL-BLEED PHOTO CONTENT
   Photo-led sections run edge to edge on a phone; type stays inset.
   ============================================================ */
/* The founder portrait is a fixed 300px square inside 20px of column padding.
   At 320px that makes the story grid's single column 340px wide — the text
   column stretches to match and the whole section runs 20px past the screen.
   (`overflow-x: clip` hides the scrollbar, so it showed up as quietly cropped
   type rather than as a visible overflow.) Capped to the space available;
   aspect-ratio keeps it a circle. */
@media (max-width: 900px) {
  .story__photo-frame {
    width: min(300px, 100%);
    height: auto;
    aspect-ratio: 1;
  }
}

@media (max-width: 860px) {
  .gallery .container { padding-inline: 0; }
  .gallery__head,
  .gallery__footer { padding-inline: clamp(20px, 5vw, 80px); }
  .gallery__grid { column-gap: 4px; }
  .gallery__item {
    margin-bottom: 4px;
    border-radius: 0;   /* an edge-to-edge grid with rounded corners reads as a mistake */
  }
}


/* ============================================================
   6 · MOTION
   Two new primitives for the whole site: the header tuck
   (transform) and the sheet rise (transform). Nothing else was
   added; both animate transform only.
   ============================================================ */
@keyframes asSheet {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  /* The header must not merely snap away — under reduced motion it
     simply never hides. */
  html.nav-tucked .nav { transform: none; }
  .as-sheet { animation: none; }
}
