/* ============================================================
   KanTaw (ကံတော်) — Global styles
   Base tokens are fixed on :root. Per-page accent variants are
   set via body[data-page="…"] and consumed through CSS variables,
   so the same Tailwind utility (bg-accent / text-gold) shifts hue
   per page without any layout changes.
   ============================================================ */

:root {
  /* Chinese fallbacks, in one place so no font stack forgets them. The
     self-hosted 'Noto Sans SC' is a subset and does not cover every character
     in the menu, so these system faces have to catch the rest. */
  --cjk-fallback: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Songti SC', 'SimSun';

  /* Fixed base palette — used on every page */
  --ink: #1A1512;   /* warm near-black (was neutral #1A1A1A) so darks match the warm palette */
  --paper: #FAF7F2;
  --brand-red: #A31E22;
  --brand-gold: #C9A227;

  /* Accent variants — default to full brand strength (Home) */
  --accent-red: #A31E22;
  --accent-gold: #C9A227;

  /* Derived */
  --accent-red-soft: color-mix(in srgb, var(--accent-red) 8%, var(--paper));
}

/* Per-page accent registers (see design doc §2) */
body[data-page="banquet"]  { --accent-red: #C4272C; --accent-gold: #D9AE3B; } /* warmest, festive */
body[data-page="fishballs"]{ --accent-red: #B8383B; --accent-gold: #C9A227; } /* calm, appetizing */
body[data-page="ourstory"] { --accent-red: #8C3033; --accent-gold: #B89A3E; } /* quiet, editorial */
body[data-page="prayer"]   { --accent-red: #6E2A2C; --accent-gold: #B39456; } /* restrained, respectful */
/* detail.html inherits its accent from ?category= via main.js */

/* ------------------------------------------------------------ */

html { scroll-behavior: smooth; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: 'Inter', 'Noto Sans SC', var(--cjk-fallback), 'Noto Sans Myanmar', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Language-specific font tuning. main.js sets <html lang>.
   Every stack must keep the CJK fallbacks: dish rows print Chinese alongside
   Myanmar and English whatever the interface language is, so a stack without
   them drops those names entirely. */
html[lang="zh"] body { font-family: 'Noto Sans SC', var(--cjk-fallback), 'Inter', sans-serif; }
html[lang="my"] body { font-family: 'Noto Sans Myanmar', 'Noto Sans SC', var(--cjk-fallback), 'Inter', sans-serif; line-height: 1.9; }

.font-wordmark { font-weight: 700; letter-spacing: -0.01em; }

/* ---- Typographic polish: no orphan words ---- */
h1, h2, h3, .headline-hero, .headline-xl, .headline-lg { text-wrap: balance; }
p { text-wrap: pretty; }

/* ---- Keyboard focus ring (accessibility) ---- */
:focus-visible { outline: 2px solid var(--accent-red); outline-offset: 3px; border-radius: 4px; }
/* dark sections read better with a gold ring */
.bg-ink :focus-visible, #kt-chat-panel :focus-visible { outline-color: var(--accent-gold); }

/* ---- Subtle paper grain: fixed, non-blocking, adds warmth/texture ---- */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 9999; pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) { body::after { opacity: .025; } }

/* Headlines — Apple-style semibold + tight tracking */
h1, h2, h3 { font-weight: 600; line-height: 1.06; letter-spacing: -0.025em; }
/* The clamp MINIMUM is the number that matters on a phone: below roughly 520px
   the vw term drops under it and the floor takes over, so a floor set too high
   stops the headline shrinking at all. At 2.75rem, "Served at your table."
   measured 405px inside a 390px content column on a 430px screen — it punched
   out of the viewport and made every page scroll sideways. These floors let the
   text keep shrinking down to a 320px screen. */
.headline-hero { font-size: clamp(1.75rem, 8.5vw, 5.5rem); line-height: 1.03; }
.headline-xl { font-size: clamp(1.5rem, 6vw, 4rem); }
.headline-lg { font-size: clamp(1.35rem, 4.5vw, 2.75rem); }
html[lang="my"] h1, html[lang="my"] h2, html[lang="my"] h3 { line-height: 1.35; }

/* ---- Site-wide display font: Newsreader on every headline ----
   Applied everywhere (not just the homepage) so the whole site feels like one
   brand. Newsreader has no Myanmar/Chinese glyphs, so those scripts fall
   through the stack to the self-hosted Noto fonts automatically, per-glyph —
   Chinese and Myanmar headlines are completely unaffected by this change. */
h1, h2, h3,
.headline-hero, .headline-xl, .headline-lg {
  font-family: 'Newsreader', 'Noto Sans SC', 'Noto Sans Myanmar', Georgia, serif;
  font-weight: 600; letter-spacing: -0.01em;
}

/* Hero watermark: our own coin mark, enlarged and bled off the right edge.
   Replaces the pagoda — a building reads as "visit our venue", and we cater at
   yours. Using the logo at two scales (small in the nav, huge here) is what
   makes it read as a brand rather than decoration. Kept very faint so it never
   competes with the headline; the previous pagoda had no opacity set at all on
   desktop, which is why it dominated the screen. */
.kt-coin-bg::after {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background-image: url(../img/logo/kantaw-mark.svg);
  background-repeat: no-repeat;
  background-position: right -8% center;
  background-size: auto 125%;
  opacity: .08;
  pointer-events: none;
}

/* Narrow screens: drop it behind and below the text rather than beside it. */
@media (max-width: 700px) {
  .kt-coin-bg::after {
    background-position: center bottom -14%;
    background-size: auto 34%;
    opacity: .05;
  }
}

/* Subtle dot-lattice texture — replaces photography as visual richness */
.kt-pattern {
  background-image: radial-gradient(color-mix(in srgb, var(--accent-gold) 55%, transparent) 1px, transparent 1px);
  background-size: 18px 18px;
}
.kt-pattern-dark {
  background-image: radial-gradient(color-mix(in srgb, var(--paper) 14%, transparent) 1px, transparent 1px);
  background-size: 16px 16px;
}

/* Cartoon badge photos replacing the coin-mark on the "what we offer" cards.
   border-radius:50% + overflow:hidden clips the source square image into a
   circle, cropping away the corners outside the drawn badge. Same size on
   every card (including the hero card) for a consistent row. */
.quick-card { position: relative; }
/* Gold coin-rim around the badge: red-on-cream alone reads as the Japanese
   flag; the red + gold pairing is what makes it read Chinese. Done as a ring
   in CSS so it works with the existing round PNG art, no re-drawing needed. */
.quick-photo { display: inline-flex; width: 96px; height: 96px; border-radius: 50%;
  overflow: hidden; margin: 0 auto; position: absolute; top: -3rem; left: 50%;
  background: var(--brand-red);
  transform: translateX(-50%);
  box-shadow: 0 0 0 3px var(--accent-gold), 0 0 0 4.5px var(--paper),
              0 6px 12px -4px rgba(26,21,18,.25);
  transition: transform .3s cubic-bezier(.34,1.56,.64,1), box-shadow .3s ease; }
/* The badge PNGs carry their own thick black outline. Scaling the art up inside
   the circular clip pushes that outline past the crop edge, so the gold rim
   above is the only ring left — red disc, gold rim, no black band between. */
/* Measured from the source PNGs: the red disc spans only 80% of the image, so
   the outer 10% on each side is solid black. Filling the circular clip with red
   needs >=125%; 136% leaves a safe margin so no black survives anti-aliasing.
   The red background underneath is a backstop for any sub-pixel gap. */
.quick-photo img { width: 136%; height: 136%; max-width: none; object-fit: cover;
  display: block; position: relative; top: -18%; left: -18%; }
.quick-card:hover .quick-photo { transform: translateX(-50%) translateY(-8px) rotate(-6deg) scale(1.08);
  box-shadow: 0 0 0 3px var(--accent-gold), 0 0 0 4.5px var(--paper),
              0 10px 18px -4px rgba(26,21,18,.3); }
.quick-card:active .quick-photo { transform: translateX(-50%) translateY(-2px) scale(.96); transition-duration: .1s; }

/* Prayer Set moves straight up — no tilt, no bounce. Rocking or shaking an
   incense censer is disrespectful in Chinese custom, so the playful rotate that
   suits the food icons is wrong here. Same specificity as the rules above, so
   this must stay after them to win. */
.quick-card--solemn:hover .quick-photo { transform: translateX(-50%) translateY(-8px);
  box-shadow: 0 0 0 3px var(--accent-gold), 0 0 0 4.5px var(--paper),
              0 10px 18px -4px rgba(26,21,18,.3); }
.quick-card--solemn:active .quick-photo { transform: translateX(-50%) translateY(-2px); transition-duration: .1s; }

/* Apple-style eyebrow label */
.eyebrow { font-size: clamp(.8rem, 1.6vw, .95rem); font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent-gold); }
html[lang="my"] .eyebrow { letter-spacing: .04em; text-transform: none; }

/* Apple-style inline text link with chevron */
.link-arrow { display: inline-flex; align-items: center; gap: .3rem; color: var(--accent-red);
  font-weight: 600; font-size: 1.05rem; transition: gap .18s ease; }
.link-arrow:hover { gap: .55rem; }
.link-arrow svg { transition: transform .18s ease; }

/* ---- Full-bleed layout primitives ---- */
.kt-hero { min-height: 86vh; min-height: 86svh; display: flex; align-items: center; justify-content: center; }
.kt-panel { padding-block: clamp(5rem, 13vh, 9rem); scroll-margin-top: 4.5rem; }
section[id] { scroll-margin-top: 4.5rem; }

/* Gold divider */
.rule-gold {
  width: 56px; height: 3px; border: 0;
  background: var(--accent-gold);
  border-radius: 2px;
}

/* ---- Buttons ---- */
.btn {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .75rem 1.5rem; border-radius: 9999px;
  font-weight: 600; font-size: .95rem;
  transition: transform .18s ease, background-color .18s ease, opacity .18s ease;
}
.btn:active { transform: translateY(1px); }
/* Transparent border of the same width as .btn-outline's, so a filled and an
   outlined button standing side by side are exactly the same height — without
   it the border made the outline button 3px taller. */
.btn-primary { background: var(--accent-red); color: #fff; border: 1.5px solid transparent; }
.btn-primary:hover { opacity: .92; }
.btn-outline { border: 1.5px solid var(--accent-red); color: var(--accent-red); }
.btn-outline:hover { background: var(--accent-red); color: #fff; }

/* ---- Fortune-coin icon mark ---- */
/* The KanTaw mark. Was drawn in CSS (circle + square cutout); now points at the
   real logo file, so every existing .coin-mark on the site picks it up without
   touching a single line of markup. Sized in em so it still scales with the
   font-size set at each call site. */
.coin-mark {
  display: inline-block; width: 1.6em; height: 1.6em;
  background: url(../img/logo/kantaw-mark.svg) center / contain no-repeat;
}

/* ---- Scroll fade-up (see main.js IntersectionObserver) ---- */
.reveal { opacity: 0; transform: translateY(24px);
  transition: opacity .9s cubic-bezier(.16,1,.3,1), transform .9s cubic-bezier(.16,1,.3,1); }
.reveal.is-visible { opacity: 1; transform: none; }
/* Optional stagger: add data-delay="1|2|3" to reveal children */
.reveal[data-delay="1"] { transition-delay: .08s; }
.reveal[data-delay="2"] { transition-delay: .16s; }
.reveal[data-delay="3"] { transition-delay: .24s; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---- Scroll cue ---- */
@keyframes bob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(8px); } }

/* ---- FAQ accordion ---- */
.faq-item { border-bottom: 1px solid color-mix(in srgb, var(--ink) 12%, transparent); }
.faq-q { width: 100%; text-align: left; display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; padding: 1.25rem 0; font-weight: 600; cursor: pointer; }
/* Minimalist +/− toggle (replaces the chevron; markup unchanged) */
.faq-q .chev { display: none; }
.faq-q::after { content: "+"; flex: none; width: 1em; text-align: center;
  font-size: 1.5rem; font-weight: 400; line-height: 1; color: var(--accent-gold); }
.faq-item.open .faq-q::after { content: "\2212"; }
.faq-a { max-height: 0; overflow: hidden; transition: max-height .3s ease; }
.faq-a > div { padding: 0 0 1.25rem; opacity: .85; }

/* ---- Language toggle ---- */
.lang-btn { padding: .25rem .55rem; border-radius: 6px; font-size: .85rem; font-weight: 600; opacity: .55; transition: opacity .15s ease, background-color .15s ease, color .15s ease; }
.lang-btn:hover { opacity: .9; }
.lang-btn.active { opacity: 1; background: color-mix(in srgb, var(--accent-red) 12%, transparent); color: var(--accent-red); }

/* ---- Nav on phones ----
   Every item in the nav row is shrink-0, so nothing compresses: the bar
   demanded 476px minimum (logo block 173 + controls 263 + padding 40) and
   forced the whole document wider than the screen. Every page then scrolled
   sideways and clipped its left edge. Rather than let anything shrink into
   illegibility, drop the romanised wordmark and tighten the controls — the
   coin mark plus ကံတော် still identifies the site. */
@media (max-width: 639px) {
  /* nav.max-w-6xl (0,2,1) — plain `header nav` (0,0,2) loses to Tailwind's
     .px-5 (0,1,0) and the padding override silently did nothing. */
  header nav.max-w-6xl { padding-left: .75rem; padding-right: .75rem; }
  .kt-nav-rom { display: none; }                 /* the "KanTaw" romanisation */
  .lang-btn { padding: .2rem .35rem; font-size: .78rem; }
  header [data-menu-toggle] { padding: .25rem; }
  header nav.max-w-6xl > div:last-child { gap: .35rem; }
  header .btn-primary { padding-left: .6rem; padding-right: .6rem; }
}
@media (max-width: 400px) {
  .lang-btn { padding: .2rem .22rem; font-size: .72rem; }
  /* keep ကံတော် — just set it smaller rather than dropping the name */
  header .font-wordmark { font-size: 1.25rem; }
}

/* ---- Collapsed menu links (hover highlight on pointer devices) ---- */
.mobile-link { display: block; padding: .65rem .75rem; border-radius: 10px; transition: background-color .15s ease, color .15s ease; }
.mobile-link:hover { background: color-mix(in srgb, var(--accent-red) 8%, transparent); color: var(--accent-red); }

/* ---- Back to top ----
   Bottom-right. It used to be raised to bottom:88px to clear the chat launcher
   (56px tall at bottom:20px); chat.js is parked until AskDesk is deployed, so
   on a desktop it can drop to the corner.

   NOT on a phone, though. 88px was doing a second job nobody wrote down: on
   mobile the browser's own chrome overlays the bottom of the viewport — iOS
   Safari's toolbar, Android Chrome's bar, the home indicator — so a control at
   bottom:20px sits underneath it and is unreachable or invisible. The media
   query below keeps the phone clearance.

   env(safe-area-inset-bottom) only resolves where the page declares
   viewport-fit=cover, which today is index.html alone; everywhere else it
   falls back to 0px and the fixed offset carries it.

   WHEN CHAT IS SWITCHED BACK ON: raise the desktop value to 88px too, or the
   two buttons land on top of each other. */
#kt-to-top {
  position: fixed; right: 25px; z-index: 60;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--paper); color: var(--brand-red);
  border: 1.5px solid color-mix(in srgb, var(--brand-red) 45%, transparent);
  box-shadow: 0 6px 18px rgba(0,0,0,.14);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: opacity .25s ease, transform .25s ease, visibility .25s, background-color .15s ease, color .15s ease;
}
#kt-to-top.show { opacity: 1; visibility: visible; transform: none; }
#kt-to-top:hover { background: var(--brand-red); color: #fff; }
/* Phone: clear the browser's bottom chrome — see the note above. */
@media (max-width: 767px) {
  #kt-to-top { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); right: 18px; }
}

/* ---- Page rhythm: colour blocks, proof strip, motif dividers ----
   The page was cream from top to bottom, which reads as one long quiet
   document. A deep-red band mid-page plus a proof strip gives it the
   cream -> red -> cream cadence that makes a Chinese restaurant page feel
   festive rather than administrative. */
.kt-band-red { position: relative; overflow: hidden;
  background: var(--brand-red); color: var(--paper); }
.kt-band-red .headline-lg, .kt-band-red h2, .kt-band-red h3 { color: var(--paper); }
.kt-band-red .eyebrow { color: var(--brand-gold); }
.kt-band-red .text-accent { color: var(--brand-gold); }
.kt-band-red .link-arrow { color: var(--brand-gold); }
/* Same coin mark as the hero, so red bands carry the brand rather than a
   borrowed pagoda. On red the coin's own red field disappears and only its gold
   ring and fret read, which is why this sits higher than the hero's .08.
   background-size:contain lets one rule serve both the tall banquet band and
   the short footer — the coin simply scales to whatever height it's in. */
.kt-band-red::after { content: ""; position: absolute; inset: 0; z-index: 0;
  background-image: url(../img/logo/kantaw-mark.svg);
  background-repeat: no-repeat; background-position: right center;
  background-size: contain; opacity: .16; pointer-events: none; }
.kt-band-red > * { position: relative; z-index: 1; }

/* Proof strip — gold numerals on red, the trust signal a stranger planning a
   wedding actually looks for. */
.kt-proof { background: var(--brand-red); color: var(--paper); }
/* Tailwind only compiles classes it finds in the markup, and bare `grid-cols-3`
   is used nowhere else on the site, so it never made it into app.css — without
   this the strip collapses to a single stacked column. */
.kt-proof .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kt-proof-n { font-family: 'Newsreader', Georgia, serif; font-size: clamp(1.75rem, 5vw, 2.75rem);
  line-height: 1; color: var(--brand-gold); font-weight: 600; }
.kt-proof-l { font-size: .75rem; letter-spacing: .14em; text-transform: uppercase;
  opacity: .8; margin-top: .5rem; }

/* Motif divider — a small coin between sections instead of blank space. */
.kt-divider { display: flex; align-items: center; gap: 1rem;
  max-width: 22rem; margin: 0 auto; padding-block: .5rem; }
.kt-divider::before, .kt-divider::after { content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--brand-gold) 60%, transparent), transparent); }
.kt-divider i { display: block; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--brand-gold); position: relative; flex: none; }
.kt-divider i::after { content: ""; position: absolute; inset: 3px;
  border: 1.5px solid var(--brand-gold); }

/* ---- Quick-access service cards (homepage hub) ---- */
/* The badges overhang 3rem above their own card (see .quick-photo below), so
   the grid's row gap needs to clear that overhang — otherwise a second row's
   badge overlaps the card text sitting above it in the first row. */
#offer .grid { row-gap: 3.5rem; margin-top: 4.5rem; }
/* Faint gold hairline instead of grey — carries the red/gold pairing down into
   the card frame rather than stopping at the badge. */
.quick-card { display: block; text-align: center; padding: 3.5rem .75rem 1rem; border-radius: 14px;
  background: #fff; border: 1px solid color-mix(in srgb, var(--brand-gold) 38%, transparent);
  transition: box-shadow .2s ease, transform .2s ease, border-color .2s ease; }
.quick-card:hover { transform: translateY(-3px);
  box-shadow: 0 8px 22px color-mix(in srgb, var(--ink) 10%, transparent);
  border-color: var(--brand-gold); }

/* Featured card: solid red block instead of a bigger box, so every tile keeps
   the same footprint while one still leads the row. */
.quick-card h3 { color: var(--ink); }
.quick-card p { color: var(--ink); opacity: .65; }
.quick-card:hover h3 { color: var(--accent-red); }

.quick-card--featured { background: var(--brand-red); border-color: var(--brand-red); }
.quick-card--featured h3 { color: #F7E9C8; }
.quick-card--featured p { color: #EBD6BC; opacity: .95; }
.quick-card--featured:hover { border-color: var(--brand-gold); }
/* The h3 carries Tailwind's `group-hover:text-accent`, which compiles to
   `.group:hover .group-hover\:text-accent` — specificity (0,3,0). That outranks
   a plain `.quick-card--featured:hover h3` (0,2,1), so on hover the title took
   --accent-red and vanished against this card's own red background. Repeating
   the class lifts us to (0,3,1) so the gold wins. */
.quick-card--featured.quick-card--featured:hover h3 { color: var(--brand-gold); }

/* ---- Card ---- */
.card { background: #fff; border-radius: 16px; overflow: hidden;
  box-shadow: 0 1px 2px rgba(26,26,26,.04), 0 8px 24px rgba(26,26,26,.06); }
.card-media { aspect-ratio: 4/3; background: var(--accent-red-soft); position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: center; color: var(--accent-gold); }
/* A real photo covers the coin-mark placeholder. The <img> is transparent until it
   actually loads (main.js adds .loaded), so a missing file just shows the placeholder
   with no broken-image icon. Uses opacity (not display:none) so loading="lazy" still
   fires — a display:none image never lazy-loads. */
.media-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .5s ease; }
.media-img.loaded { opacity: 1; }

/* ---- Promotions placeholder ---- */
.promo-empty { border: 1.5px dashed color-mix(in srgb, var(--accent-gold) 55%, transparent);
  border-radius: 16px; color: color-mix(in srgb, var(--ink) 55%, transparent); }

/* ---- Live chat widget (chat.js) ---- */
#kt-chat-launch { position: fixed; right: 20px; bottom: 20px; z-index: 60;
  width: 56px; height: 56px; border-radius: 50%; background: var(--brand-red); color: #fff;
  border: 2px solid var(--brand-gold); box-shadow: 0 8px 24px rgba(0,0,0,.2);
  display: flex; align-items: center; justify-content: center; cursor: pointer; }
#kt-chat-panel { position: fixed; right: 20px; bottom: 88px; z-index: 60;
  width: min(360px, calc(100vw - 40px)); height: 460px; background: #fff; border-radius: 16px;
  box-shadow: 0 12px 40px rgba(0,0,0,.22); display: none; flex-direction: column; overflow: hidden; }
#kt-chat-panel.open { display: flex; }
.kt-msg { max-width: 82%; padding: .55rem .8rem; border-radius: 12px; margin: .35rem 0; font-size: .9rem; line-height: 1.4; }
.kt-msg.user { background: var(--brand-red); color: #fff; margin-left: auto; border-bottom-right-radius: 4px; }
.kt-msg.bot { background: #F1ECE4; color: var(--ink); border-bottom-left-radius: 4px; }

/* ============================================================
   MENU (menu.html — rendered from assets/data/menu.json)
   Every row carries all three languages at once: Myanmar as the
   primary line, English and Chinese beneath it. These are not
   switched by the language buttons — the dish names are typed by
   hand in the data file, not machine translated.
   ============================================================ */
.menu-section { margin-top: 3rem; }
.menu-section:first-child { margin-top: 1rem; }

.menu-head { display: flex; flex-direction: column; gap: .15rem;
  padding-bottom: .6rem; margin-bottom: .35rem;
  border-bottom: 2px solid color-mix(in srgb, var(--brand-gold) 55%, transparent); }
.menu-head-my { font-size: clamp(1.35rem, 3.4vw, 1.75rem); line-height: 1.3; }
.menu-head-alt { font-family: 'Inter', 'Noto Sans SC', var(--cjk-fallback), sans-serif; font-size: .78rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  color: color-mix(in srgb, var(--ink) 52%, transparent); }

.menu-note { margin: .5rem 0 .25rem; font-size: .92rem; line-height: 1.6;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
  background: color-mix(in srgb, var(--brand-gold) 9%, transparent);
  border-left: 3px solid var(--brand-gold); border-radius: 0;
  padding: .7rem .9rem; }
.menu-note-alt { font-size: .82rem; color: color-mix(in srgb, var(--ink) 55%, transparent); }

.menu-list { list-style: none; margin: 0; padding: 0; }
.menu-row { display: flex; gap: .85rem; align-items: baseline;
  padding: .62rem 0;
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 8%, transparent); }
.menu-row:last-child { border-bottom: 0; }

.menu-no { flex: none; min-width: 2.6rem;
  font-family: 'Inter', sans-serif; font-size: .78rem; font-weight: 700;
  letter-spacing: .04em; color: var(--brand-gold); }
.menu-names { display: flex; flex-direction: column; gap: .1rem; }
.menu-my { font-size: 1.02rem; line-height: 1.55; }
.menu-alt { font-family: 'Inter', 'Noto Sans SC', var(--cjk-fallback), sans-serif; font-size: .84rem; line-height: 1.5;
  color: color-mix(in srgb, var(--ink) 58%, transparent); }

/* Signature dishes: set `"signature": true` on the item in menu.json */
/* display:inline-block is load-bearing — Tailwind's preflight sets svg{display:block},
   which drops the star onto a line of its own under the dish name. */
.menu-star { display: inline-block; width: 1em; height: 1em; margin-left: .4em;
  vertical-align: -.1em; fill: var(--brand-gold); }
.menu-row.is-signature .menu-my { font-weight: 600; }

/* The menu page wraps its content in a table so the print masthead can repeat
   as a <thead>. On screen those table semantics are unwanted, so every part is
   reset to a plain block — the layout inside is unaffected. */
.print-sheet, .print-sheet > thead, .print-sheet > tbody,
.print-sheet > * > tr, .print-sheet > * > tr > td { display: block; }

.print-only { display: none; }

/* ---- Print / save-as-PDF ----------------------------------
   The printed menu IS this page, so it can never drift out of
   sync with the site. Chrome is hidden, the coin sits behind as a
   watermark, and sections avoid breaking across pages. ------- */
@media print {
  @page { size: A4; margin: 12mm 12mm 14mm; }

  html, body { background: #fff !important; }

  /* Scroll-reveal never runs when a page is being printed — IntersectionObserver
     does not fire for the print rendering — so anything still carrying .reveal
     stays at opacity:0 and prints as blank space that still occupies its full
     height. Force it visible for print. */
  .reveal { opacity: 1 !important; transform: none !important; }
  body::after { display: none !important; }          /* paper-grain overlay */
  header, footer, #contact, #kt-chat-launch, #kt-chat-panel,
  #kt-to-top, .no-print, [data-partial] { display: none !important; }

  .kt-panel { padding-block: 0 !important; }
  .print-only { display: block; }

  /* Repeated on every page by the <thead> it lives in. position:fixed was the
     obvious alternative but cannot work here: at top:0 it lands on the same
     spot the list starts and overprints it, and any negative offset that moves
     it clear of the text stops it repeating past page one. */
  .print-sheet { display: table; width: 100%; }
  .print-sheet > thead { display: table-header-group; }
  .print-sheet > tbody { display: table-row-group; }
  .print-sheet > * > tr { display: table-row; }
  .print-sheet > * > tr > td { display: table-cell; padding: 0; }

  .print-masthead { display: flex; align-items: center; gap: .6rem;
    padding-bottom: .35rem; margin: 0 0 .7rem;
    border-bottom: 2.5px solid #C9A227; }
  .print-masthead > img { width: 40px; height: 40px; }
  .print-word { font-family: 'Newsreader', Georgia, serif; font-size: 15pt;
    margin: 0; color: #A31E22; }
  /* Names Inter explicitly: this line is Latin only, and inheriting the
     Myanmar stack sends it through the CJK fallbacks, which have no Latin
     glyphs — the address and phone number silently disappeared. */
  .print-sub { font-family: 'Inter', sans-serif; font-size: 8pt; margin: .1rem 0 0; color: #555; }

  /* Sections must be allowed to flow across pages. Marking them
     break-inside:avoid pushed any section too tall for the remaining space
     onto a fresh page, which is what left half of each page empty. Only the
     heading and the individual rows are protected. */
  /* Keep a category together. This was the cause of the 11-page version, but
     only because the list was one tall column then; in two columns each section
     is about half as tall, so it fits without leaving big gaps. */
  .menu-section { break-inside: avoid; page-break-inside: avoid; margin-top: .9rem; }
  .menu-head { break-inside: avoid; break-after: avoid; page-break-after: avoid;
    padding-bottom: .3rem; margin-bottom: .2rem; }
  .menu-head-my { font-size: 13pt; }
  .menu-head-alt { font-size: 7pt; }
  .menu-note { padding: .35rem .5rem; font-size: 8pt; margin: .3rem 0; }
  .menu-note-alt { font-size: 7pt; }

  /* Two columns: a printed menu of short rows wastes half the sheet in one
     column, and this roughly halves the page count. */
  .menu-list { columns: 2; column-gap: 9mm; }
  .menu-row { break-inside: avoid; page-break-inside: avoid;
    padding: .2rem 0; gap: .5rem; }
  .menu-my { font-size: 9.5pt; line-height: 1.35; }
  .menu-alt { font-size: 7pt; line-height: 1.3; }
  .menu-no { font-size: 7pt; min-width: 1.9rem; }
  .menu-star { width: .85em; height: .85em; }

  /* ---- Banquet sheet in print ----
     Tighter than on screen, and the dish list goes to two columns so a table
     of ten fits without pushing the next one onto a fresh page. */
  .bmenu-h { font-size: 12pt; padding-bottom: .3rem; margin: 0 0 .2rem; }
  .bmenu-foot { margin-top: 1rem; font-size: 8pt; }

  .kt-set { max-width: none; padding: .7rem 0 .6rem; break-inside: avoid; page-break-inside: avoid; }
  .kt-set + .kt-set { margin-top: 1rem; }
  .kt-set-label { margin-bottom: .5rem; font-size: 7pt; letter-spacing: .14em; }
  /* Deliberately NOT columns:2 here. A multi-column list inside a
     break-inside:avoid block makes Chrome reserve a whole page for it, which
     pushed the table onto page 2 and left page 1 two-thirds empty. */
  .kt-set-list { gap: .3rem; }
  .kt-set-dish { break-inside: avoid; font-size: 9pt; line-height: 1.3;
    padding-bottom: .18rem; grid-template-columns: 2.3rem 1fr; }
  .kt-set-no { font-size: 6.5pt; }
  .kt-set-note { margin-top: .5rem; font-size: 8pt; }
  .kt-set-price { font-size: 7pt; }

  a[href]::after { content: none !important; }        /* no printed URLs */
}

/* ---- Famous dishes: full-width alternating rows ----
   One dish per row, photo on one side and the story on the other, sides
   swapping down the page. A 4-across grid caps every photo at a quarter of the
   width and leaves room for one line of text; this gives each dish a photo
   worth looking at and room to say how it is cooked. */
.feature-dish { display: grid; gap: clamp(1.25rem, 4vw, 3rem); align-items: center;
  margin-top: clamp(2.5rem, 6vh, 4.5rem); }
.feature-photo { border-radius: 1rem; overflow: hidden; }
.feature-name { font-family: 'Newsreader', 'Noto Sans SC', var(--cjk-fallback), Georgia, serif;
  font-size: clamp(1.5rem, 3.2vw, 2.1rem); line-height: 1.25; font-weight: 600; }
.feature-rule { width: 44px; height: 3px; border: 0; margin: .9rem 0 0;
  background: var(--brand-gold); border-radius: 2px; }

@media (min-width: 768px) {
  .feature-dish { grid-template-columns: 1fr 1fr; }
  /* Explicit flip class rather than :nth-child — the section heading is a
     sibling, so nth-child would count it and flip the wrong rows. */
  .feature-dish--flip .feature-photo { order: 2; }
}

/* ---- Language swap ----
   All three languages sit in the markup and CSS reveals the one matching
   <html lang>. Same approach as .menu-opt: it cannot fall out of sync with a
   missed event, and it survives the browser's print re-render. */
.lang-swap { display: none; }
html[lang="my"] .lang-swap[lang="my"],
html[lang="en"] .lang-swap[lang="en"],
html[lang="zh"] .lang-swap[lang="zh"] { display: inline; }

/* ---- Banquet example table ----
   Set as a printed menu card, not a data grid: one narrow centred column
   framed by gold hairlines. The earlier three-column box read like a
   spreadsheet — dishes scanned left-to-right in rows, which is not how anyone
   reads a menu, and the boxed tint fought the airy panels around it. */
.kt-set { max-width: 34rem; margin-inline: auto; padding: 1.7rem 0 1.5rem;
  border-top: 2px solid var(--brand-gold);
  border-bottom: 1px solid color-mix(in srgb, var(--brand-gold) 50%, transparent); }
.kt-set + .kt-set { margin-top: 2.75rem; }

.kt-set-label { display: flex; justify-content: center; align-items: baseline;
  flex-wrap: wrap; gap: .55rem; margin: 0 0 1.35rem;
  font-family: 'Inter', 'Noto Sans SC', var(--cjk-fallback), sans-serif;
  font-size: .68rem; font-weight: 700; letter-spacing: .17em; text-transform: uppercase;
  color: var(--brand-red); }
.kt-set-count { font-weight: 500; letter-spacing: .1em;
  color: color-mix(in srgb, var(--ink) 45%, transparent); }

/* Left-aligned inside the centred card: the numbers line up in a gold gutter,
   which is what makes a list of codes readable at a glance. */
.kt-set-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .78rem; }
.kt-set-dish { display: grid; grid-template-columns: 2.7rem 1fr; align-items: baseline;
  font-size: 1.02rem; line-height: 1.45; text-align: left; }
.kt-set-no { font-family: 'Inter', sans-serif; font-size: .7rem; font-weight: 700;
  letter-spacing: .06em; color: var(--brand-gold); }

.kt-set-note { margin: 1.5rem 0 0; text-align: center; font-size: .92rem; line-height: 1.65;
  color: color-mix(in srgb, var(--ink) 70%, transparent); text-wrap: pretty; }
.kt-set-price { margin: .4rem 0 0; text-align: center; font-size: .82rem; line-height: 1.6;
  color: color-mix(in srgb, var(--ink) 48%, transparent); }

/* The signature dish sits inside a section, so it must not out-shout the
   section's own heading — headline-xl (up to 64px) was larger than the
   headline-lg above it and inverted the hierarchy. */

/* ---- Dish photos: swipeable on phones ----
   Four 3:4 portraits stacked in one column ran to 2083px — two entire screens
   of photographs between the visitor and the table they came to read. As a
   horizontal strip the same four cost about a third of one screen, and
   swiping a row of dishes is the expected gesture on a phone anyway. */
@media (max-width: 639px) {
  .kt-dish-strip {
    grid-auto-flow: column; grid-auto-columns: 62%;
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    /* bleed to the screen edges so the next card peeks in and invites the swipe */
    margin-inline: -1.25rem; padding-inline: 1.25rem 2.5rem;
    scrollbar-width: none;
  }
  .kt-dish-strip::-webkit-scrollbar { display: none; }
  .kt-dish-strip > * { scroll-snap-align: start; }
}

/* ---- Prayer set options ----
   Framed with a hairline rather than a filled card: this page is deliberately
   the quietest on the site, and a tinted box would read as merchandising. */
.kt-prayer-set { border-top: 2px solid color-mix(in srgb, var(--accent-gold) 60%, transparent);
  padding-top: 1rem; }
.kt-prayer-set h3 { font-size: 1.1rem; font-weight: 600; }
.kt-prayer-set p { margin-top: .5rem; font-size: 1rem; line-height: 1.7;
  color: color-mix(in srgb, var(--ink) 72%, transparent); }

/* ---- Rental capacity strip ----
   Reuses the homepage proof band. .kt-proof forces three columns; here the
   labels are longer, so allow them to stack on a phone. */
@media (max-width: 639px) {
  .kt-proof .grid { grid-template-columns: 1fr; gap: 2rem; }
}

/* ---- Banquet menu sheet (banquet-menu.html) ---- */
.bmenu-h { font-size: clamp(1.35rem, 3vw, 1.75rem); text-align: center;
  padding-bottom: .5rem; margin-bottom: .25rem;
  border-bottom: 2px solid color-mix(in srgb, var(--brand-gold) 55%, transparent); }
/* .bmenu-serve-grid removed with the "How we serve" block — this sheet is the
   menu, and the three service formats belong on banquet-dishes.html. */
.bmenu-foot { margin-top: 2.5rem; text-align: center; font-size: .9rem; line-height: 1.7;
  color: color-mix(in srgb, var(--ink) 62%, transparent); text-wrap: pretty; }

/* How a dish is made. :empty collapses the line on dishes whose method hasn't
   been written yet, so a half-filled section still looks deliberate. */

/* Pre-order tag. Lead time is a property of a dish, not a category — splitting
   pre-order dishes into their own section would pull a pork roll out of Pork,
   which is where someone looking for pork will go. */
.menu-tag { display: inline-block; margin-left: .5em; vertical-align: .1em;
  font-family: 'Inter', sans-serif; font-size: .62rem; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase; white-space: nowrap;
  color: var(--brand-red); border: 1px solid color-mix(in srgb, var(--brand-red) 40%, transparent);
  border-radius: 999px; padding: .12em .55em; }
@media print { .menu-tag { font-size: 6pt; padding: .05em .4em; } }
/* Per-dish choices. All three languages are in the markup; only the one
   matching <html lang> is displayed. Doing the swap in CSS rather than in JS
   means it survives a missed event and survives Chrome's print re-render, so
   the printed sheet is always in the language shown on screen. */
.menu-opt { display: none; margin-left: .35em;
  color: color-mix(in srgb, var(--ink) 45%, transparent); font-weight: 400; }
html[lang="my"] .menu-opt[lang="my"],
html[lang="en"] .menu-opt[lang="en"],
html[lang="zh"] .menu-opt[lang="zh"] { display: inline; }
.menu-my .menu-opt { font-size: .82em; }

/* Protein-code legend. Codes are language-neutral, so each dish shows one
   compact "(C, P, F)" instead of the choices repeated in three languages. */
@media print {
      }

/* Printed-sheet QR: someone holding the paper menu in Myanmar can scan it and
   read the same menu in English or Chinese, and order from there. */
@media print {
  .print-masthead { justify-content: flex-start; }
  .print-id { flex: 1; }
}

/* ─────────────────────────────────────────────────────────────
   LONG-FORM COPY  (data-i18n-para)
   The story copy is written one line per beat, so paragraphs are
   short and the rhythm matters. Spacing is tighter than normal
   body copy or the page reads as a list of stranded sentences.
   ───────────────────────────────────────────────────────────── */
/* Left-aligned here, not via Tailwind's `text-left`: that class is not in the
   compiled app.css, so it silently does nothing and long copy inherits the
   centring from the section wrapper. Ragged-right is what long prose needs;
   .kt-prose--center is for the short poetic blocks that are meant to centre. */
.kt-prose { max-width: 34rem; margin-inline: auto; text-align: left; }
.kt-prose--center { text-align: center; }
.kt-prose--center .kt-beat { border-top: 0; padding-top: 0; }
.kt-prose p {
  margin-top: 1.05em;
  line-height: 1.9;
  opacity: .82;
}
.kt-prose p:first-child { margin-top: 0; }
.kt-prose strong { font-weight: 600; opacity: 1; color: var(--ink); }

/* A line the author set entirely in bold is a beat — give it room and a gold
   rule. The class is applied by the renderer, not by a CSS selector: a
   paragraph that merely contains a bold name still has that <strong> as its
   only ELEMENT child, so p:has(> strong:only-child) matched ordinary prose
   too and turned every mentioned name into a pull-quote. */
.kt-prose p.kt-beat {
  margin-top: 1.6em;
  padding-top: 1.1em;
  border-top: 1px solid color-mix(in srgb, var(--accent-gold) 45%, transparent);
  font-family: var(--font-display, inherit);
  font-size: 1.15em;
  line-height: 1.6;
  opacity: 1;
}

/* Chapter heading inside a story page */

/* Spacing between chapters */

/* ═════════════════════════════════════════════════════════════
   OUR STORY — one chronological spine
   The page used to tell the history twice: five prose chapters,
   then a six-row milestone list covering the same years. That
   repetition is what made it feel long and flat. Chapters and
   milestones are now a single dated run down one rail, so each
   year is stated once and every entry has a home for a photo.
   ═════════════════════════════════════════════════════════════ */

/* ── Hero ── */
.kt-story-hero {
  position: relative;
  isolation: isolate;
  padding-block: clamp(3rem, 9vh, 5rem);
  /* No pagoda. It is a Burmese temple silhouette on a page about a Fuzhou
     Chinese family — decorative stock texture that is not this business's
     story. The founder's shopfront photo is not the replacement either: it is
     896x1195, so full-bleed it would upscale 2.2x and go soft, a landscape
     crop would cut off either the signboard or him, and a legibility overlay
     would bury the sign. It earns more as its own picture in the 1987 entry. */
  background: linear-gradient(155deg,
    color-mix(in srgb, var(--accent-red) 22%, var(--ink)),
    var(--ink) 62%);
  text-align: center;
  overflow: hidden;
}
/* Our own mark, bled off the right — the same brand device as the homepage. */
.kt-story-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: url(../img/logo/kantaw-mark.svg) no-repeat right -8% center / auto 150%;
  opacity: .07;
  pointer-events: none;
}
@media (max-width: 700px) {
  .kt-story-hero::before { background-position: center bottom -18%; background-size: auto 62%; opacity: .05; }
}

/* Back link sits top-left INSIDE the hero. Below it, it sat alone on an empty
   paper band between the hero and the first entry and read as orphaned; and it
   cannot go beside the centred display title without breaking that centring. */
/* Used by every dark hero. A bare label is fine on a flat gradient but
   disappears over a photograph, where what sits behind it is whatever the
   picture happens to show — so it carries its own chip and is legible on
   both. One class, so the two heroes cannot drift apart. */
.kt-hero-back {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .45rem .9rem .45rem .7rem;
  border-radius: 999px;
  font-size: .84rem;
  font-weight: 600;
  color: var(--paper);
  background: color-mix(in srgb, var(--ink) 52%, transparent);
  border: 1px solid color-mix(in srgb, var(--paper) 22%, transparent);
  backdrop-filter: blur(6px);
  transition: background .2s ease, color .2s ease;
}
.kt-hero-back:hover {
  background: color-mix(in srgb, var(--ink) 72%, transparent);
  color: var(--accent-gold);
}
.kt-story-hero .eyebrow { color: var(--accent-gold); }
.kt-story-hero h1 { color: var(--paper); }
/* Unscoped on purpose: every dark hero uses it. Scoped under .kt-story-hero it
   silently did nothing on the fish ball page, which had the class but none of
   the styling — the two heroes were only consistent by eye, not by code. */
.kt-hero-lede {
  max-width: 34rem;
  margin: 1.5rem auto 0;
  color: color-mix(in srgb, var(--paper) 84%, transparent);
  font-size: clamp(1rem, 2.1vw, 1.2rem);
  line-height: 1.75;
  text-wrap: pretty;
}

/* ── The spine ──
   No rail and no circular badges. A continuous line with pills on it reads as
   a stock timeline widget dropped into an editorial page; the years carry the
   chronology perfectly well as type in the margin, which is how print does it.
   The column is also capped: prose sits at a 38rem measure for readability, so
   a 64rem container left a permanent ~290px void down the right of every
   entry. ── */
.kt-spine { max-width: 58rem; margin-inline: auto; }

.kt-era { display: grid; grid-template-columns: 1fr; gap: .35rem 0; }
.kt-era + .kt-era { margin-top: clamp(3.25rem, 9vh, 6rem); }
@media (min-width: 900px) {
  .kt-era { grid-template-columns: 9.5rem 1fr; gap: 0 2.75rem; align-items: start; }
}

.kt-era-year {
  font-family: 'Newsreader', 'Noto Sans SC', 'Noto Sans Myanmar', Georgia, serif;
  font-size: clamp(1.6rem, 4vw, 3.4rem);
  line-height: .95;
  color: color-mix(in srgb, var(--accent-gold) 88%, transparent);
  letter-spacing: -0.02em;
  /* lining, equal-width figures so 1973 / 1980 / 1988 stack in a true column
     rather than jittering by a few pixels each */
  font-variant-numeric: lining-nums tabular-nums;
}
@media (min-width: 900px) {
  .kt-era-year { text-align: right; padding-top: .18em; }
}

.kt-era-title {
  font-family: 'Newsreader', 'Noto Sans SC', 'Noto Sans Myanmar', Georgia, serif;
  font-size: clamp(1.3rem, 3vw, 1.9rem);
  line-height: 1.25;
  margin-bottom: .35rem;
  text-wrap: balance;
}
.kt-era-rule {
  width: 40px; height: 2px; border: 0; margin: 0 0 1.25rem;
  background: var(--accent-gold);
}

/* Prose, plus a photo column on the entries that have a photograph.
   Entries without one simply run full width — the grid must not assume
   every era has an image, because most of them do not. */
.kt-era-inner { display: grid; gap: clamp(1.25rem, 3vw, 2rem); }
@media (min-width: 900px) {
  .kt-era--pic .kt-era-inner { grid-template-columns: 1fr 18rem; align-items: start; }
  /* When a photo shares the row the prose fills what is left, rather than
     holding a 38rem measure and pushing the picture off the edge. */
  .kt-era--pic .kt-era-prose.kt-prose { max-width: none; }
}
.kt-era-prose.kt-prose { max-width: 38rem; margin-inline: 0; }

/* An era whose body is a single line — 1987 is a photograph and a fact, not a
   chapter. Standing a tall portrait beside one sentence leaves the text column
   nearly empty, so here the picture leads at full width and the line sits
   under it. */
@media (min-width: 900px) {
  /* 1987 is one sentence and a photograph. Stacking a tall portrait above a
     single line left 574px of empty row beside it, so the picture sits next to
     the line instead and the two are centred against each other. */
  .kt-era--photo-led .kt-era-inner {
    grid-template-columns: 20rem 1fr !important;
    align-items: center;
    gap: 2rem;
  }
}
.kt-era--photo-led .kt-era-figure { max-width: 20rem; order: -1; }
/* Portrait, not landscape: the only photograph of this era is a tall shot with
   the signboard at the top and the founder at the bottom. Cropping it to 4:3
   loses one or the other, and both are the reason the picture matters. */
.kt-era--photo-led .kt-era-figure .card-media { aspect-ratio: 3 / 4; }

.kt-era-figure { margin: 0; }
/* These are archive photographs, not product shots — a soft lift makes them
   read as prints laid on the paper rather than flat panels cut into it. */
.kt-era-figure .card-media {
  box-shadow: 0 1px 2px color-mix(in srgb, var(--ink) 12%, transparent),
              0 12px 28px color-mix(in srgb, var(--ink) 12%, transparent);
}
.kt-era-figure .card-media { border-radius: .9rem; aspect-ratio: 3 / 4; }
.kt-era-cap {
  margin-top: .6rem;
  font-size: .76rem;
  line-height: 1.5;
  opacity: .62;
}

/* Drop cap — English only. Myanmar stacks diacritics above and below the base
   letter and Chinese is full-width, so an enlarged ::first-letter collides
   with the line above in both. */
html[lang="en"] .kt-era:not(.kt-era--photo-led) .kt-era-prose.kt-prose > p:first-child::first-letter {
  float: left;
  font-family: 'Newsreader', Georgia, serif;
  font-size: 3.1em;
  line-height: .82;
  padding: .06em .12em 0 0;
  color: var(--accent-red);
}

/* Decorative vertical Chinese — the reference site uses a vertical calligraphy
   strip as an accent. Ornamental, hidden from screen readers, dropped on
   narrow screens where there is no room for it. */
.kt-vert {
  writing-mode: vertical-rl;
  font-family: 'Noto Sans SC', var(--cjk-fallback), serif;
  font-size: clamp(1.1rem, 2vw, 1.5rem);
  letter-spacing: .3em;
  color: color-mix(in srgb, var(--accent-gold) 55%, transparent);
  user-select: none;
}
@media (max-width: 1100px) { .kt-vert { display: none; } }

/* ── The tradition statement, standing on its own ── */
.kt-quote {
  max-width: 44rem;
  margin-inline: auto;
  text-align: center;
}
.kt-quote .kt-prose { max-width: none; text-align: center; }
.kt-quote .kt-prose p {
  font-family: 'Newsreader', 'Noto Sans SC', 'Noto Sans Myanmar', Georgia, serif;
  font-size: clamp(1.15rem, 2.6vw, 1.6rem);
  line-height: 1.55;
  opacity: .95;
}
/* The beat rule is for running prose; inside a quote it just adds clutter. */
.kt-quote .kt-prose p.kt-beat {
  border-top: 0;
  padding-top: 0;
  margin-top: 1.1em;
  font-size: inherit;
}
.kt-quote .kt-prose strong { color: var(--accent-red); }
.kt-quote-mark {
  display: block;
  font-family: 'Newsreader', Georgia, serif;
  font-size: 4rem;
  line-height: .6;
  color: color-mix(in srgb, var(--accent-gold) 60%, transparent);
  margin-bottom: .75rem;
}

/* ── How we cook: photo cards instead of three bare buttons ── */
.kt-cook-grid {
  display: grid;
  gap: clamp(1rem, 2.5vw, 1.75rem);
  grid-template-columns: 1fr;
}
@media (min-width: 700px) { .kt-cook-grid { grid-template-columns: repeat(3, 1fr); } }
.kt-cook-card {
  display: block;
  text-align: left;
  border-radius: 1rem;
  overflow: hidden;
  background: var(--paper);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--ink) 10%, transparent);
  transition: transform .25s ease, box-shadow .25s ease;
}
.kt-cook-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 28px color-mix(in srgb, var(--ink) 14%, transparent);
}
.kt-cook-photo { aspect-ratio: 4 / 3; }
.kt-cook-label { padding: 1rem 1.1rem 1.25rem; }
.kt-cook-name {
  font-family: 'Newsreader', 'Noto Sans SC', 'Noto Sans Myanmar', Georgia, serif;
  font-size: 1.05rem;
  line-height: 1.35;
}
.kt-cook-go {
  margin-top: .5rem;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--accent-red);
}

/* ═════════════════════════════════════════════════════════════
   DELIVERY — a dish row that carries its full story
   The pork stew used to be split across the page: a showcase row
   with the photo and one line, and a separate story section with
   no photo at all. Its text now sits in its own row, beside its
   own photo. The other two rows are the same component with short
   copy, so they take longer text later without any layout change.
   ═════════════════════════════════════════════════════════════ */

.feature-tagline {
  margin-top: .3rem;
  font-size: .95rem;
  letter-spacing: .01em;
  color: color-mix(in srgb, var(--ink) 55%, transparent);
}

/* A row whose text is a full story rather than a line. The photo is floated
   inside the text so the opening paragraphs run beside it and the remainder
   closes underneath, using the full container width rather than leaving a tall
   empty column under the picture.

   The float width matches the grid column the other rows use (47.5% + a 3rem
   gutter), so the photo is exactly the size it is in the Egg Noodle row and
   the text beside it lands on the same measure. */
.feature-dish--story { display: block; }
/* Contain the float, or the next dish row rides up beside it. */
.feature-dish--story::after { content: ""; display: block; clear: both; }

.feature-dish--story .feature-figure { margin: 0 0 1.25rem; }
.feature-dish--story .feature-story.kt-prose { max-width: none; margin-inline: 0; }
.feature-dish--story .feature-story.kt-prose p { margin-top: .9em; }
.feature-dish--story .feature-story.kt-prose > p:first-child { margin-top: 0; }

/* The closing bolded line always starts below the photo. Its gold rule is a
   full-width divider, so when the prose happens to end before the photo does
   — as the Chinese and Myanmar copy do, being shorter than the English — the
   line would otherwise sit in the narrow column beside the picture with a
   stunted rule, while English cleared the float by accident of length. */
.feature-dish--story .kt-beat { clear: both; }

@media (min-width: 700px) {
  .feature-dish--story .feature-figure {
    float: left;
    width: 47.5%;
    margin: .35rem 3rem 1.25rem 0;
  }
  /* Alternating rows float the other way, keeping the left/right rhythm. */
  .feature-dish--flip.feature-dish--story .feature-figure {
    float: right;
    margin: .35rem 0 1.25rem 3rem;
  }
}
/* Below 700px the figure is not floated: full width, text underneath it. */

/* ═════════════════════════════════════════════════════════════
   FISH BALLS — photo hero
   Structurally the same panel as .kt-story-hero: full bleed, same
   padding rhythm, same .kt-hero-back and .kt-hero-lede classes.
   The only difference is what sits behind the text — a photograph
   here, the coin watermark there. Sharing the classes is what keeps
   the two pages consistent as either one changes.

   NOTE ON THE PHOTO: main.jpg is currently 960x1280 (portrait), so
   full bleed enlarges it and `cover` crops it to a narrow band. A
   landscape file at 2400px or wider drops in here with no CSS change.
   ═════════════════════════════════════════════════════════════ */
.kt-fb-hero {
  position: relative;
  isolation: isolate;
  padding-block: clamp(3rem, 9vh, 5rem);
  background: var(--ink) url(../img/fishballs/main.jpg) center / cover no-repeat;
  text-align: center;
  overflow: hidden;
}
.kt-fb-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--ink) 34%, transparent) 0%,
    color-mix(in srgb, var(--ink) 50%, transparent) 55%,
    color-mix(in srgb, var(--ink) 82%, transparent) 100%);
}
.kt-fb-hero .eyebrow { color: var(--accent-gold); }
.kt-fb-hero h1 { color: var(--paper); }
/* Over a photograph the scrim alone cannot guarantee contrast. */
.kt-fb-hero .eyebrow,
.kt-fb-hero h1,
.kt-fb-hero .kt-hero-lede {
  text-shadow: 0 1px 14px color-mix(in srgb, var(--ink) 70%, transparent);
}

/* Step rows on the fish ball page: the prose fills its grid column rather than
   holding .kt-prose's 34rem reading measure, which inside a 468px column would
   have done nothing except sit left-aligned at an arbitrary width. */
.feature-step.kt-prose { max-width: none; margin-inline: 0; }
.feature-step.kt-prose > p:first-child { margin-top: 0; }

/* ── Behind a KanTaw Banquet: three columns, matching the other step sections
      on that page. Previously one 640px column stacked 1560px tall. ── */
.kt-bq-head {
  margin-top: .45rem;
  font-family: 'Newsreader', 'Noto Sans SC', 'Noto Sans Myanmar', Georgia, serif;
  font-size: 1.12rem;
  line-height: 1.35;
  text-wrap: balance;
}
.kt-bq-head[hidden] { display: none; }
/* Fill the column rather than holding .kt-prose's 34rem reading measure,
   which inside a ~300px column would simply do nothing. */
.kt-bq-body.kt-prose { max-width: none; margin-inline: 0; margin-top: .9rem; }
.kt-bq-body.kt-prose p { margin-top: .8em; font-size: .95rem; }
.kt-bq-body.kt-prose > p:first-child { margin-top: 0; }

/* ── Banquet sets side by side ──
   The cards were 34rem, centred and stacked, which left most of the container
   empty and made the section three screens long. ── */
.kt-set-grid { display: grid; gap: clamp(1.25rem, 3vw, 2.25rem);
  grid-template-columns: 1fr; justify-items: center; }
@media (min-width: 820px) { .kt-set-grid { grid-template-columns: repeat(3, 1fr); } }

.kt-set-grid .kt-set + .kt-set { margin-top: 0; }   /* the grid gap spaces them */
/* width:100% is load-bearing, not belt-and-braces. .kt-set centres itself with
   `margin-inline: auto`, and a grid item with an auto margin is sized to
   fit-content instead of stretching — the stacked card collapsed from its 34rem
   measure to 338px. The grid centres with justify-items instead. */
.kt-set-grid .kt-set { width: 100%; }
/* Below the breakpoint the cards are still stacked, so .kt-set keeps its 34rem
   measure and stays centred — dropping it there would stretch one list of short
   dish names across the full container on a tablet. */
@media (min-width: 820px) {
  /* In a column the card fills its track instead of holding a reading measure. */
  .kt-set-grid .kt-set { max-width: none; margin-inline: 0; }
  /* Tightened to fit a ~300px track without every dish name wrapping twice. */
  .kt-set-grid .kt-set-dish { font-size: .95rem; grid-template-columns: 2.2rem 1fr; }
  .kt-set-grid .kt-set-list { gap: .62rem; }
  .kt-set-grid .kt-set-label { margin-bottom: 1.1rem; }
}
/* The count sits in a gold pill so a three-word phrase beside the set name
   still reads as a tag rather than as a second half of the title. */
.kt-set-grid .kt-set-count {
  flex: none;
  padding: .18rem .55rem; border-radius: 999px;
  background: color-mix(in srgb, var(--brand-gold) 22%, transparent);
  color: color-mix(in srgb, var(--ink) 70%, transparent);
  font-size: .7rem; font-weight: 700; letter-spacing: .02em; white-space: nowrap;
}
/* The label is a flex row; at 304px the name and the pill will not both fit on
   one line in every language, so let it wrap instead of squeezing the name. */
.kt-set-grid .kt-set-label { flex-wrap: wrap; gap: .45rem; }
.kt-set-foot { margin-top: clamp(1.75rem, 4vh, 2.5rem); }

@media print { .kt-set-grid { grid-template-columns: 1fr; } }

/* ── Banquet page: breaking up the repeated 01/02/03 grid ──
   Four sections in a row used the same numbered three-column grid, for four
   different kinds of content. Only two of them were sequences. ── */

/* "How we serve": three alternatives, marked with a rule rather than a number.
   .rule-gold is 56x3; this is the same bar with the spacing the column needs. */
.kt-serve-mark { width: 40px; height: 3px; border: 0; border-radius: 2px;
  background: var(--accent-gold); margin: 0 auto 1.1rem; }

/* "Why families choose us": the hall photograph beside the three reasons. */
.kt-why { display: grid; gap: clamp(2rem, 5vw, 3.5rem);
  grid-template-columns: 1fr; align-items: center; }
@media (min-width: 860px) { .kt-why { grid-template-columns: 1fr 1fr; } }
.kt-why-cap { margin-top: .85rem; font-size: .88rem; text-align: center;
  color: color-mix(in srgb, var(--ink) 55%, transparent); }
@media (min-width: 860px) { .kt-why-cap { text-align: left; } }

/* Reasons read in no order, so they are separated by a hairline rather than
   numbered. Left-aligned: these are sentences, not labels. */
.kt-why-list { text-align: left; }
.kt-reason + .kt-reason { margin-top: 1.5rem; padding-top: 1.5rem;
  border-top: 1px solid color-mix(in srgb, var(--accent-gold) 32%, transparent); }
.kt-reason-h { font-family: 'Newsreader', 'Noto Sans SC', 'Noto Sans Myanmar', Georgia, serif;
  font-size: 1.22rem; line-height: 1.3; }
.kt-reason-b { margin-top: .45rem; line-height: 1.65;
  color: color-mix(in srgb, var(--ink) 72%, transparent); text-wrap: pretty; }

/* "How it works": a real sequence, so it keeps its numbers — but drawn as a
   connected rail, which reads as a process and not as a third text grid. */
.kt-stepper-label { text-align: center; margin-top: clamp(2.5rem, 6vh, 4rem); }
.kt-stepper { list-style: none; margin: 1.75rem 0 0; padding: 0;
  display: grid; gap: 1.9rem; grid-template-columns: 1fr; }
@media (min-width: 760px) { .kt-stepper { grid-template-columns: repeat(3, 1fr); gap: 0; } }

.kt-step { position: relative; text-align: center; padding-inline: .8rem; }
.kt-step-no { position: relative; z-index: 1;
  display: inline-grid; place-items: center;
  width: 2.5rem; height: 2.5rem; border-radius: 999px;
  border: 2px solid color-mix(in srgb, var(--accent-gold) 85%, transparent);
  font-family: 'Inter', sans-serif; font-size: .78rem; font-weight: 700;
  letter-spacing: .04em; color: var(--accent-gold); }

/* The rail is drawn only BETWEEN the discs — two half-lines per step, each
   stopping 1.55rem short of the centre (the 1.25rem radius plus a gap) — so no
   line ever passes behind a number and the discs need no opaque fill to punch
   a hole in it. That matters here: this section sits on a red-tinted field, and
   a paper-filled disc would read as a hole rather than a ring. */
@media (min-width: 760px) {
  .kt-step::before, .kt-step::after {
    content: ''; position: absolute; top: 1.25rem; height: 2px;
    background: color-mix(in srgb, var(--accent-gold) 38%, transparent);
  }
  .kt-step::before { left: 0; right: 50%; margin-right: 1.55rem; }
  .kt-step::after { right: 0; left: 50%; margin-left: 1.55rem; }
  .kt-step:first-child::before, .kt-step:last-child::after { content: none; }
}
.kt-step-name { margin-top: .85rem; font-size: 1.05rem; font-weight: 600; line-height: 1.3; }
.kt-step-note { margin-top: .4rem; font-size: .88rem; line-height: 1.55;
  color: color-mix(in srgb, var(--ink) 62%, transparent); text-wrap: pretty; }

@media print { .kt-stepper { grid-template-columns: 1fr; }
  .kt-step::before, .kt-step::after { content: none; } }

/* ---- Collapsed menu: collapse the HEIGHT, not just the opacity ----
   The menu lives inside the sticky header, so it occupies real layout space —
   303px of it. Fading alone left that height in place for the whole animation
   and then dropped it in a single frame when display:none landed, so the entire
   page lurched upward AFTER the fade had finished. Animating grid-template-rows
   0fr -> 1fr collapses the height over the same duration, so the page slides
   instead of snapping.

   display:grid is written as :not(.hidden) deliberately. Tailwind's .hidden and
   a bare [data-mobile-menu] have equal specificity, so with styles.css loading
   after app.css a plain rule would beat .hidden and the menu could never hide.

   Duration must stay in step with KT_MENU_MS in main.js. */
[data-mobile-menu]:not(.hidden) { display: grid; }
[data-mobile-menu] {
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows .22s ease, opacity .16s ease;
}
[data-mobile-menu].is-open { grid-template-rows: 1fr; opacity: 1; }

/* min-height:0 is required — a grid item's automatic minimum size is its
   content, which would refuse to shrink below it and defeat the 0fr row.

   Horizontal padding ONLY. A 0fr row collapses the content box, not the
   padding box, so vertical padding here survives the collapse and leaves a
   strip behind — which display:none then drops in one frame, reintroducing
   exactly the jump this is meant to remove. The vertical rhythm comes from
   .mobile-link's own padding instead. */
.kt-menu-inner {
  display: flex; flex-direction: column;
  overflow: hidden; min-height: 0;
  padding-inline: .75rem;
}

@media (prefers-reduced-motion: reduce) {
  [data-mobile-menu] { transition: none; }
}

/* A pull-quote that completes a colon is the tail of the sentence above it,
   not a quotation — main.js tags it .kt-beat--cont. Without this the gold rule
   and the 1.6em gap fall between "…a traditional Fuzhou fish ball:" and the
   words that finish it. Keeps the display size and weight; drops the divider. */
.kt-prose p.kt-beat--cont { margin-top: .55em; padding-top: 0; border-top: 0; }

/* The non-pork note follows a photo row, so centred at container width it read
   as a caption for the photograph above it. It needs a reading measure and a
   separator — but NOT a full-width border-top: the note is narrower than the
   container, so that rule began part-way across and appeared to slice through
   the photograph above. A short centred bar (the site's own .rule-gold idiom)
   marks the break without touching anything.

   The top margin lives here, not on a Tailwind class. It was mt-14, which is
   NOT in the committed app.css and so silently did nothing — invisible until
   the border landed flush against the bottom of the photo. */
.kt-fb-note {
  max-width: 44rem; margin-inline: auto;
  margin-top: clamp(3rem, 7vh, 4.5rem);
}
.kt-fb-note::before {
  content: ""; display: block;
  width: 56px; height: 3px; margin: 0 auto 1.6rem;
  background: var(--accent-gold); border-radius: 2px;
}

/* ---- Story spine: tighter leading ----
   .kt-prose sets line-height 1.9 site-wide, which on this page stacked ~48
   lines of biography and added roughly 270px of pure air. 1.7 is where
   editorial body text usually sits; the paragraph gap comes down with it.
   Scoped to .kt-era-prose on purpose — the delivery and fish ball pages have
   just been tuned and are left exactly as they are. */
.kt-era-prose.kt-prose p { line-height: 1.7; margin-top: .95em; }

/* ---- Offering set cards: meats / + / dishes on three lines ----
   As one sentence the five-set folded mid-phrase ("… Prawn +" then "12 Dishes"),
   which read as an accident of width rather than a decision. Three centred
   beats give the card a deliberate shape and make the two cards scan alike
   even though one lists three items and the other five. */
.kt-prayer-set .kt-set-meats { margin-top: .7rem; text-wrap: balance; }
.kt-prayer-set .kt-set-plus {
  display: block; margin: .5rem 0; line-height: 1;
  font-size: 1rem; font-weight: 700; color: var(--accent-gold);
}
.kt-prayer-set .kt-set-dishes {
  margin-top: 0; font-weight: 600;
  color: color-mix(in srgb, var(--ink) 85%, transparent);
}

/* ---- Story spine: giving a single-paragraph chapter an entry point ----
   Every era is now one unbroken block of prose. A reader needs somewhere for
   the eye to land, and the drop cap only works in English — Myanmar stacks
   diacritics above and below the base letter, Chinese is full-width, and an
   enlarged ::first-letter collides with the line above in both.

   ::first-line works in all three: it emphasises whatever falls on the opening
   line regardless of script, and it costs no markup and changes no text. Kept
   deliberately quiet — a shade darker and fractionally larger, enough to read
   as an opening rather than as a heading. */
.kt-era-prose.kt-prose > p:first-child::first-line {
  color: var(--ink);
  font-size: 1.06em;
}
/* The 1987 entry is one sentence beside a photograph, not a chapter — giving
   its single line "opening line" emphasis would just make it look bold. */
.kt-era--photo-led .kt-era-prose.kt-prose > p:first-child::first-line {
  color: inherit;
  font-size: inherit;
}

/* ---- The closing tradition section ----
   Centred, because the copy is five short declarations rather than continuous
   prose — one statement per line, which is exactly what centring is for. It was
   briefly left-aligned when this block was a single twelve-line paragraph;
   centred prose gives the eye no left edge to return to. If it ever becomes
   long-form again, left-align it again.

   The closing beat KEEPS its gold rule here. .kt-prose--center suppresses that
   rule (a divider above centred text usually reads as clutter), but with five
   separated statements above it the rule is what marks the last one as the
   conclusion rather than a sixth item. */
.kt-trad-body { text-align: center; margin-inline: auto; }

/* ---- Offering set: the pork detail, on its own quiet line ----
   It used to sit in brackets inside the main list, which pushed that line to
   two or three lines and broke it mid-word ("Pork (Three-" / "Layer Pork
   Belly)"). Pulled out, the list above reads as three or four clean items and
   the detail reads as what it is — a note about one of them.

   Separated by a hairline rather than a gap: at this size a gap alone reads as
   a fourth item in the list rather than a footnote to it. */
.kt-prayer-set .kt-set-variant {
  margin-top: 1rem;
  padding-top: .85rem;
  border-top: 1px solid color-mix(in srgb, var(--accent-gold) 30%, transparent);
  font-size: .84rem;
  line-height: 1.55;
  color: color-mix(in srgb, var(--ink) 58%, transparent);
  text-wrap: balance;
}
.kt-prayer-set .kt-set-variant:empty { display: none; }

/* ---- Offering set: one item per line ----
   data-i18n-para emits a <p> per line, so these are real paragraphs and carry
   .kt-prose-ish margins by default. They are a list, not prose — tighten them
   to a stacked rhythm, and give the pork line (the only one with a bracketed
   detail) room to wrap without colliding with its neighbours. */
.kt-prayer-set .kt-set-meats p { margin: 0; line-height: 1.55; }
.kt-prayer-set .kt-set-meats p + p { margin-top: .4rem; }

/* A column with two body paragraphs needs a gap between them; each is its own
   data-i18n-para block, so the usual paragraph margin does not apply across
   the boundary. */
.kt-bq-body + .kt-bq-body { margin-top: .9rem; }

/* ---- Sticky header: nearly solid ----
   The bar carried bg-paper/90 — paper at 90% with an 8px backdrop blur. Over a
   bright photograph that much show-through made it read as a panel hovering
   above the page rather than a bar attached to the top of it, and it put a
   moving image directly behind the wordmark and the language buttons.

   97% keeps the frosted character but gives the type something solid to sit on.

   Written here rather than as bg-paper/97 on the element: Tailwind is compiled
   to a committed app.css with no build step, and only the /90 opacity variant
   exists in that build. bg-paper/97 would compile to nothing and leave the
   header fully transparent. The class stays on the element as a fallback in
   case this stylesheet ever fails to load; header.sticky (0,1,1) outranks
   .bg-paper\/90 (0,1,0), so this wins when both are present. */
header.sticky {
  background: color-mix(in srgb, var(--paper) 97%, transparent);
}
