/* ==========================================================================
   Yangda theme — Phase 0 (foundation)
   Loaded after the compiled parent theme CSS (@themeStyles) via a <link>
   added in _layouts/default.blade.php and _layouts/static.blade.php.
   Plain CSS (not Sass) — see docs/theme/05-full-site-rollout-plan.md for why:
   child-theme Sass isn't compiled by the asset pipeline without a bespoke
   meta/assets.json bundle, which duplicates the parent manifest. A plain
   CSS file published via `php artisan vendor:publish --tag=igniter-assets`
   is the verified-working path.
   ========================================================================== */

/* ---------- fonts (self-hosted, see themes/yangda-orange/assets/fonts/) ---------- */
@font-face{
  font-family:"Cabinet Grotesk";
  font-style:normal;
  font-weight:700;
  font-display:swap;
  src:url("../fonts/cabinetgrotesk-700.woff2") format("woff2");
}
@font-face{
  font-family:"Cabinet Grotesk";
  font-style:normal;
  font-weight:800;
  font-display:swap;
  src:url("../fonts/cabinetgrotesk-800.woff2") format("woff2");
}
@font-face{
  font-family:"Plex Sans Var";
  font-style:normal;
  font-weight:100 700;
  font-display:swap;
  src:url("../fonts/plexsans-var.woff2") format("woff2");
}
@font-face{
  font-family:"Plex Mono";
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url("../fonts/plexmono-400.woff2") format("woff2");
}
@font-face{
  font-family:"Plex Mono";
  font-style:normal;
  font-weight:600;
  font-display:swap;
  src:url("../fonts/plexmono-600.woff2") format("woff2");
}

/* ---------- design tokens ---------- */
:root{
  --stamp:#C4222C;
  --stamp-deep:#9C1B23;
  --stamp-tint:#F6E1E0;
  /* Mockup's `.btn-stamp` uses this dedicated variable, not --stamp directly — and
     unlike --stamp, --cta-red is never redefined inside the dark-mode media query
     below. CTA buttons stay a fixed brand red regardless of light/dark preference;
     only the hover state (--stamp-deep, which *does* shift in dark mode) changes.
     Real, user-visible bug: with --stamp used directly for .btn-primary, the button
     silently shifted to the lighter dark-mode --stamp (#E2565C) on any browser with a
     dark color-scheme preference — invisible to every check this session ran in a
     headless browser defaulting to light mode, only caught once the user compared
     actual screenshots from their own browser. */
  --cta-red:#C4222C;
  --page-bg:#F5F5F2;
  --surface:#FFFFFF;
  --surface-2:#EBEAE5;
  --text:#1B1B1D;
  --text-muted:#6B6B66;
  --border:#E1E0DA;
  --jade:#2F6B52;
  --jade-tint:#E4EFE8;
  /* Same family as the cash-payment icon's gold coin (pay-cash.svg) — reused here
     for the flash-toast "warning" level, the one semantic color this palette never
     had a home for yet. */
  --amber:#8A6414;
  --amber-tint:#FBF0D9;

  --brand-ink:#17171A;
  --brand-ink-2:#0F0F11;
  --brand-on-ink:#F2F1EC;
  --brand-on-ink-muted:#A9A7A0;
  --brand-border:rgba(242,241,236,.14);
  /* Brighter accent red reserved for the dark header/ink surfaces (cart badge,
     active-nav underline) — the mockup deliberately uses this instead of the
     duller --stamp button red on dark backgrounds, since --stamp reads muddy
     against --brand-ink. Fixed value regardless of light/dark page mode, since
     the header is always dark. */
  --brand-stamp:#E2545C;
  /* Same reasoning as --brand-stamp above — the mockup's hero status chip uses this
     brighter jade instead of the light-surface --jade token when sitting on the dark
     hero background. Missing entirely before; the status chip was falling back to the
     light-surface --jade, which is the same class of "wrong token on dark surface" bug. */
  --brand-jade:#6FAF94;

  /* Found during the local/menus pass: Bootstrap's whole primary-color utility
     family (.text-primary, .bg-primary, .border-primary, .link-primary, focus
     rings, etc.) is built on rgba(var(--bs-primary-rgb),...), and the parent
     theme's compiled CSS sets --bs-primary/--bs-primary-rgb to the old orange
     (#ff4900 / 255,73,0) at :root. Overriding both here is a single fix that
     reaches every one of those utilities site-wide — this is the same root
     cause behind the header .text-primary bug fixed in Phase 0 and the
     local-header .link-primary rule fixed in Phase 1, generalized. Both of
     those scoped !important rules are left in place (harmless — they now just
     agree with this), but no more one-off patches should be needed for
     anything built on --bs-primary(-rgb) going forward. */
  --bs-primary:#C4222C;
  --bs-primary-rgb:196,34,44;
}

/* Dark mode — ported from .theme-preview/index.html's own
   `@media (prefers-color-scheme: dark)` block, which this project's real
   pages never had until an earlier pass (only the mockup's two static pages
   did). The mockup also declares a `:root[data-theme="dark"]` manual-toggle
   variant, but no toggle control exists anywhere in its markup — dead CSS,
   not ported since there's nothing real to wire it to (confirmed again this
   pass: automatic-only is the deliberate choice, not an oversight).

   That earlier pass covered the design tokens + yangda-* component classes
   built on them (hero, info-strip, dish grid, footer) but explicitly not
   Bootstrap's own components — cart, checkout, account, the legal pages,
   forms, dropdowns, modals all stayed hardcoded light regardless of system
   dark mode. This block now also closes that gap, by redefining Bootstrap
   5.3's own --bs-* custom properties (every Bootstrap component — .card,
   .form-control, .dropdown-menu, .modal-content, .table, .list-group — reads
   colors through these, so redefining the properties reaches all of them at
   once, the same "one fix, many components" trick already used for
   --bs-primary/--bs-primary-rgb above).

   These values are copied from Bootstrap's own compiled [data-bs-theme=dark]
   block (public/vendor/igniter-orange/css/app.css) — Bootstrap's already-
   designed, coherent dark palette — with ONE deliberate deviation:
   --bs-link-color/--bs-link-hover-color come hardcoded to Bootstrap's own
   default-primary orange (#ff9266) in that block, not derived from
   --bs-primary. Copying them as-is would have reintroduced the exact
   wrong-orange bug already fixed multiple times in this file (search
   "old orange" above) — overridden to var(--stamp)/var(--stamp-deep) instead,
   which the block below already redefines for dark mode. Everything else
   here (grays, borders, form validation colors, success/warning/info/danger)
   is generic Bootstrap chrome with no brand color baked in, safe to copy
   verbatim. .form-select's dropdown-arrow icon and .btn-close's dismiss-icon
   filter are the two Bootstrap components whose dark variant isn't just a
   color swap (an embedded SVG data URI and a CSS filter, respectively) —
   .form-select is real (address-book country picker, contact form subject,
   cart item options, timeslot picker); .form-switch/.accordion aren't used
   anywhere in this theme (confirmed via grep), so their own icon-swap rules
   were left out rather than carried over as dead weight. */
@media (prefers-color-scheme: dark){
  :root{
    --page-bg:#131313;
    --surface:#1C1C1C;
    --surface-2:#242423;
    --text:#F0EFEA;
    --text-muted:#A6A49C;
    --border:#333230;
    --stamp:#E2565C;
    --stamp-deep:#C33B41;
    --stamp-tint:#3A1518;
    --jade:#6FB496;
    --jade-tint:#1B2A22;
    --amber:#E8C168;
    --amber-tint:#3A2E12;

    color-scheme:dark;
    --bs-body-color:#dee2e6;
    --bs-body-color-rgb:222,226,230;
    --bs-body-bg:#212529;
    --bs-body-bg-rgb:33,37,41;
    --bs-emphasis-color:#fff;
    --bs-emphasis-color-rgb:255,255,255;
    --bs-secondary-color:rgba(222,226,230,.75);
    --bs-secondary-color-rgb:222,226,230;
    --bs-secondary-bg:#343a40;
    --bs-secondary-bg-rgb:52,58,64;
    --bs-tertiary-color:rgba(222,226,230,.5);
    --bs-tertiary-color-rgb:222,226,230;
    --bs-tertiary-bg:#2b3035;
    --bs-tertiary-bg-rgb:43,48,53;
    --bs-secondary-text-emphasis:#a7acb1;
    --bs-success-text-emphasis:#7eca8f;
    --bs-info-text-emphasis:#74c7d4;
    --bs-warning-text-emphasis:#ffda6a;
    --bs-danger-text-emphasis:#ea868f;
    --bs-light-text-emphasis:#f8f9fa;
    --bs-dark-text-emphasis:#dee2e6;
    --bs-secondary-bg-subtle:#161719;
    --bs-success-bg-subtle:#08210e;
    --bs-info-bg-subtle:#052025;
    --bs-warning-bg-subtle:#332701;
    --bs-danger-bg-subtle:#2c0b0e;
    --bs-light-bg-subtle:#343a40;
    --bs-dark-bg-subtle:#1a1d20;
    --bs-secondary-border-subtle:#41464b;
    --bs-success-border-subtle:#186429;
    --bs-info-border-subtle:#0e616e;
    --bs-warning-border-subtle:#997404;
    --bs-danger-border-subtle:#842029;
    --bs-light-border-subtle:#495057;
    --bs-dark-border-subtle:#343a40;
    --bs-heading-color:inherit;
    --bs-link-color:var(--stamp);
    --bs-link-hover-color:var(--stamp-deep);
    --bs-link-color-rgb:226,86,90;
    --bs-link-hover-color-rgb:195,59,65;
    --bs-code-color:#e685b5;
    --bs-highlight-color:#dee2e6;
    --bs-highlight-bg:#664d03;
    --bs-border-color:#495057;
    --bs-border-color-translucent:hsla(0,0%,100%,.15);
    --bs-form-valid-color:#75b798;
    --bs-form-valid-border-color:#75b798;
    --bs-form-invalid-color:#ea868f;
    --bs-form-invalid-border-color:#ea868f;
  }
  .form-select{
    --bs-form-select-bg-img:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E");
  }
  .btn-close{
    filter:var(--bs-btn-close-white-filter);
  }
  /* .bg-white/.bg-light are hardcoded Bootstrap utilities
     (background-color:rgba(var(--bs-white-rgb),...)!important /
     rgba(var(--bs-light-rgb),...)!important) — literal colors, not derived
     from --bs-body-bg/--bs-card-bg the way component classes are, so none of
     the redefinitions above reach them. They're also the single most common
     "panel" background in this theme: 17 templates use .bg-white directly
     (cart-box, checkout, search boxes, pickers, filters, the fulfillment
     modal...) and were confirmed still solid white in dark mode even after
     --bs-body-bg was fixed — this is what that gap actually was. Not
     redefining --bs-white-rgb itself: it's a literal "true white" Bootstrap
     uses in unrelated places too (button text on the red buttons, for one)
     that must stay white regardless of page theme; overriding the two
     utility classes directly is the same "target the actual site of the
     bug" reasoning as skipping the .btn-primary rewrite in favor of !important. */
  .bg-white{
    background-color:var(--surface) !important;
  }
  .bg-light,
  .label-light,
  .panel-light{
    background-color:var(--surface-2) !important;
  }
}

/* Mockup's `.wrap{max-width:1180px}` applies at every width; Bootstrap's `.container`
   instead steps through 540/720/960/1140/1320px per breakpoint. Only the widest step
   (xxl, >=1400px -> 1320px) is actually wider than the mockup's fixed cap — every
   narrower breakpoint is already <=1180px, so only xxl needs pulling in. Scoping to
   just that breakpoint avoids touching the (already-correct) tablet/laptop container
   widths, which a blanket unconditional override would have widened instead of
   narrowed at those sizes. */
@media (min-width:1400px){
  .container{
    max-width:1180px;
  }
}
/* Mockup's `.wrap{padding-inline:28px}` vs Bootstrap's default 12px each side
   (`--bs-gutter-x:1.5rem`, halved for padding) — applies at every width in the mockup,
   not just wide screens, so this isn't breakpoint-gated the way the max-width fix above
   is. Overriding the custom property itself (`--bs-gutter-x`) doesn't work here: Bootstrap
   redeclares it locally on `.container`/`.row` themselves (not just :root), and a local
   declaration on the element always wins over one only reaching it via inheritance,
   regardless of the ancestor rule's specificity or source order — confirmed via
   getComputedStyle after a --bs-gutter-x:root override had no effect. Setting the actual
   padding properties directly sidesteps that. */
.container{
  padding-left:28px;
  padding-right:28px;
}

/* ---------- base typography & body ---------- */
body{
  background-color:var(--page-bg);
  color:var(--text);
  font-family:"Plex Sans Var","IBM Plex Sans","Noto Sans SC","PingFang SC","Microsoft YaHei",-apple-system,"Segoe UI",sans-serif;
}
/* zh_CN reads noticeably smaller than de/en at the same declared font-size: none of
   the custom Latin webfonts (Plex Sans Var, Cabinet Grotesk) carry CJK glyphs, so every
   Chinese character already renders via a fallback system font (Noto Sans SC/PingFang
   SC/Microsoft YaHei), and those sit smaller within their em-box than the Latin
   webfonts do — same declared rem size, visibly smaller glyphs. Scoped to the actual
   locale value _pages/_layouts set on <html lang="..."> (App::getLocale(), literally
   "zh_CN" — see ti-ext-langselector), not a :lang() pseudo-class match, since that
   value has an underscore, not the hyphen :lang() BCP-47 matching expects. Scaling the
   root font-size instead of individual rules means every rem-based size in this file
   (nearly all of them) scales together, automatically, with no per-selector upkeep. */
html[lang="zh_CN"]{
  font-size:112.5%;
}
/* CJK fallback: real headings (e.g. local-header's <h1>{{ $locationInfo->name }}</h1>)
   can contain "Yangda 阳达" as a single string — Cabinet Grotesk has no CJK
   glyphs, so browsers fall through to Noto Sans SC for those characters
   automatically, no markup split needed. Same reasoning as .yangda-brand-name. */
h1,h2,h3,h4,h5,h6,
.card-title,
.heading-section h2{
  font-family:"Cabinet Grotesk","Helvetica Neue","Noto Sans SC","PingFang SC","Microsoft YaHei",Arial,sans-serif;
  font-weight:700;
  letter-spacing:-0.01em;
}
a{color:var(--stamp)}
a:hover{color:var(--stamp-deep)}
/* .btn-link sets its color via Bootstrap's own --bs-btn-color custom property chain
   (`.btn{color:var(--bs-btn-color)}`, 0,1,0), which beats a plain `a` selector
   (0,0,1) regardless of load order — confirmed via matched CSSOM rules. */
.btn-link{color:var(--stamp) !important}
.btn-link:hover{color:var(--stamp-deep) !important}
/* Global, added during the cart-box pass: every prior scoped `.text-muted` rule
   (local-header, etc.) resolved to this exact same token — widening it to a
   plain global rule reaches cart-box, order-preview, and everywhere else
   `.text-muted` appears without needing a rule per component. Bootstrap's own
   `.text-muted{color:var(--bs-secondary-color)!important}` carries !important,
   so this needs it too. */
.text-muted{color:var(--text-muted) !important}

/* ---------- global button / card / border tokens (Phase 0 scope: colors + radius only) ---------- */
/* Bug found during the cart-box pass, present since Phase 0: Bootstrap's compiled
   `.btn-primary{--bs-btn-color:#000;...}` baked BLACK text in at Sass-compile time,
   sized for contrast against the old orange (#FF4900) — every primary button
   (checkout, cart-toggle, register, etc.) has been rendering black-on-red this whole
   time because only background/border were overridden, never the text color.
   `color` needs `!important`: `.btn{color:var(--bs-btn-color)}` and `.btn-primary`'s
   own `--bs-btn-color:#000` are both still in the cascade and win over an
   unqualified re-declaration in some button states (hover/active/disabled all set
   their own `--bs-btn-*-color:#000` too). Confirmed via computed styles on the
   cart page's checkout button — computed to rgb(0,0,0) before this fix. */
.btn-primary{
  background-color:var(--cta-red) !important;
  border-color:var(--cta-red) !important;
  color:#fff !important;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active{
  background-color:var(--stamp-deep) !important;
  border-color:var(--stamp-deep) !important;
  color:#fff !important;
}
/* Same bug as the black-text one above, different property: Bootstrap's compiled
   `.btn-primary{--bs-btn-disabled-bg:#ff4900;--bs-btn-disabled-border-color:#ff4900}`
   also bakes in the old orange for the disabled state specifically, and its own
   selector (`.btn.disabled,.btn:disabled,fieldset:disabled .btn`) out-specifies the
   plain `.btn-primary` rule above (two classes beats one) — so the checkout
   "Bestätigen" button flashed orange for the split second it's `wire:loading.class=
   "disabled"` while the payment-method Livewire round-trip is in flight. Adding
   `!important` to the base rule's background/border above covers every state at
   once (disabled included) the same way `color` already did, rather than adding a
   fourth near-duplicate `:disabled`-specific block. */
/* Mockup's `.btn` is one flat, fixed-size pill (padding:11px 22px, 14px/600, radius:999px,
   no shadow) used unmodified for both the header CTA and the larger hero CTA. The real
   markup instead adds Bootstrap's `.btn-lg` to the hero button, which brings its own
   padding/font-size AND a `border-radius:var(--bs-border-radius-lg)` (8px, not a pill) plus
   Bootstrap's default inset/drop box-shadow — none of which theme.css had neutralized
   before, so the hero CTA rendered bigger, less rounded, and with a bevel the header CTA
   didn't have. Forcing one consistent size/shape here, `!important` throughout since
   `.btn-lg` sets these via the same higher-order custom-property chain (`--bs-btn-*`) that
   already required `!important` for `.btn-primary`'s color above. */
.btn-primary,
.btn-primary.btn-lg,
.btn-primary.btn-sm{
  padding:11px 22px !important;
  font-size:14px !important;
  font-weight:600 !important;
  border-radius:999px !important;
  box-shadow:none !important;
}
.card{
  border-color:var(--border);
  border-radius:14px;
}
.border,
.border-top,
.border-bottom,
.border-start,
.border-end,
.list-group-item,
.table{
  border-color:var(--border) !important;
}
/* Bug found during the local/menus pass, same class as `.btn-primary`/`.progress-bar`:
   Bootstrap's compiled `.nav-pills{--bs-nav-pills-link-active-bg:#ff4900;...}` hardcodes
   the old orange as a root-level default. Used by the menu category tabs
   (components/category-list.blade.php) and the opening-hours tab widget inside the
   local-header "More info" offcanvas (includes/local/hours.blade.php) — both reached
   globally, no Blade changes needed. */
.nav-pills{
  --bs-nav-pills-link-active-bg:var(--stamp);
}

/* ==========================================================================
   Header
   ========================================================================== */
.yangda-header{
  background-color:var(--brand-ink);
  border-bottom:1px solid var(--brand-border);
}
/* header.blade.php's `py-md-0` strips all vertical padding at desktop width, leaving
   a ~54px-tall bar with no room below the nav links — the active-link underline
   (bottom:-6px, added above) rendered past the header's own bottom border and onto
   the page content beneath it instead of sitting inside the dark header. The mockup's
   `.site-header .wrap{min-height:76px}` is the real fix: restore the header to its
   intended height via min-height rather than fighting the padding utility class. */
.yangda-header .container{
  min-height:76px;
}
.yangda-header .navbar-brand{
  display:flex;
  align-items:center;
  gap:12px;
}
.yangda-header .img-logo{
  height:40px;
  width:auto;
  border-radius:50%;
}
.yangda-header .text-logo{
  font-family:"Cabinet Grotesk","Helvetica Neue","Noto Sans SC","PingFang SC","Microsoft YaHei",Arial,sans-serif;
  font-weight:700;
  font-size:1.25rem;
  color:var(--brand-on-ink);
}
.yangda-brand-text{
  display:flex;
  flex-direction:column;
  line-height:1.2;
}
/* font-family fallback (not a manual span around the CJK half of the string,
   the way .theme-preview/index.html does it with a dedicated .brand-cn span)
   — site_name is one admin-editable field holding "Yangda 阳达" as a single
   string, so splitting it in Blade to wrap just the CJK half would be fragile
   (breaks the moment an admin changes the name's shape). A CJK fallback font
   in the stack achieves the same rendering result the robust way: Cabinet
   Grotesk has no CJK glyphs, so the browser transparently falls through to
   Noto Sans SC for "阳达" within the same text node, no markup change needed. */
.yangda-brand-name{
  font-family:"Cabinet Grotesk","Helvetica Neue","Noto Sans SC","PingFang SC","Microsoft YaHei",Arial,sans-serif;
  font-weight:700;
  font-size:1.375rem;
  color:var(--brand-on-ink);
}
.yangda-brand-sub{
  font-size:.6875rem;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--brand-on-ink-muted);
}
.yangda-header-actions{
  display:flex;
  align-items:center;
  gap:14px;
}
/* Language selector rendered with displayStyle="list" (plain <ul class="list-inline">
   of buttons/spans) — restyled here as a pill row to match the mockup's `.lang-pills`,
   without touching the extension's own Blade view. Mockup's version has no outer padding
   or gap between items — `overflow:hidden` on the bordered container alone clips the
   corners, and the items sit flush against each other and the edge; the previous version
   here added an outer padding + per-item gap + per-item border-radius, giving a
   "pill within a pill" look the mockup doesn't have. */
.yangda-header .lang-selector .list-inline{
  display:flex;
  /* the extension's own markup puts Bootstrap's `gap-2` utility on this <ul> — same
     class of problem as the `.p-0`/`.fw-bold` fixes elsewhere in this file: Bootstrap's
     gap utilities are `!important` (`.gap-2{gap:.5rem!important}`), so a plain override
     silently loses regardless of selector specificity. Confirmed via getComputedStyle
     still reporting 8px after a first attempt without !important. Appropriate for the
     old "gap between separate pills" design, not this flush segmented-control one —
     left uncanceled, it added 16px across the 3 items, making the whole control
     measurably wider than the mockup's (136px vs 116px, via getBoundingClientRect()). */
  gap:0 !important;
  /* Same !important problem as gap-2 above: the extension's markup also puts
     `align-items-center` on this <ul>, which centers each <li> at its own intrinsic
     (content) height instead of stretching it to the tallest sibling — left the active
     "DE" pill visibly shorter than the container, with dark gaps above/below it. */
  align-items:stretch !important;
  border:1px solid var(--brand-border);
  border-radius:999px;
  overflow:hidden;
  margin:0;
}
/* display:flex here (rather than the li's default inline-block-ish list-item box) is
   what lets the button/span below stretch to fill the full pill height — otherwise each
   li sizes to the container's height (via the ul's own flex stretch) but its text child
   still only takes its own content height, leaving a visible gap above/below the active
   "DE" fill that made the whole control read as shorter than it actually was. */
.yangda-header .lang-selector .list-inline-item{
  margin:0;
  display:flex;
}
/* !important on padding: the extension's own view puts `p-0` (Bootstrap utility,
   `padding:0!important`) on the inactive-language <button>s, which otherwise beats
   a plain-specificity override regardless of selector length. Confirmed via matched
   CSSOM rules against the live page — inactive buttons rendered with 0 padding,
   collapsing them out of the pill row instead of sitting flush with the active DE pill. */
.yangda-header .lang-selector .list-inline-item button,
.yangda-header .lang-selector .list-inline-item span{
  display:flex;
  align-items:center;
  justify-content:center;
  flex:1;
  padding:6px 11px !important;
  font-family:"Plex Mono","IBM Plex Mono",monospace;
  font-size:.6875rem;
}
.yangda-header .lang-selector .list-inline-item button{
  font-weight:400;
  color:var(--brand-on-ink-muted) !important;
}
.yangda-header .lang-selector .list-inline-item button:hover{
  color:var(--brand-on-ink) !important;
}
.yangda-header .lang-selector .list-inline-item span{
  font-weight:600;
  background-color:var(--brand-on-ink);
  color:var(--brand-ink) !important;
}
/* Mockup's `.cart-btn` is a bare icon (`position:relative;color:...` only, no
   width/height/border/circle) — the circular bordered button here was a one-off
   addition never matched to the mockup's actual (much lighter) treatment. */
.yangda-cart-btn{
  position:relative;
  display:inline-flex;
  align-items:center;
  color:var(--brand-on-ink) !important;
}
.yangda-cart-btn svg{
  width:21px;
  height:21px;
}
.yangda-cart-btn:hover{
  color:var(--brand-on-ink-muted) !important;
}
/* Icon replacement for the header's logged-in/-out account entry (previously the
   "Mein Konto" text dropdown in main-menu.blade.php's horizontal nav, and a
   .yangda-account-pill/.yangda-account-link pair below) — same bare-icon treatment
   as .yangda-cart-btn right next to it, not a bordered/pill button, so the two utility
   icons read as one matched pair. */
.yangda-account-icon-btn{
  position:relative;
  display:inline-flex;
  align-items:center;
  color:var(--brand-on-ink) !important;
}
.yangda-account-icon-btn svg{
  width:21px;
  height:21px;
}
.yangda-account-icon-btn:hover{
  color:var(--brand-on-ink-muted) !important;
}
/* Guest-state header CTA (includes/account-cta.blade.php). First shipped as a solid
   red btn-primary pill matching the old "Jetzt bestellen" CTA it replaced — but that
   made a utility action (most visitors are guests, but logging in isn't the thing the
   site wants them to do) visually compete with the actual page content. Restyled as a
   small, muted pill instead — same border/radius language as the language-selector
   pill next to it, not a bold CTA. */
.yangda-account-pill{
  display:flex;
  align-items:stretch;
  border:1px solid var(--brand-border);
  border-radius:999px;
  overflow:hidden;
}
.yangda-account-pill a{
  display:flex;
  align-items:center;
  padding:7px 14px;
  font-size:.8125rem;
  font-weight:600;
  white-space:nowrap;
  text-decoration:none;
  color:var(--brand-on-ink-muted) !important;
}
.yangda-account-pill a:hover{
  color:var(--brand-on-ink) !important;
}
.yangda-account-pill a:first-child{
  border-right:1px solid var(--brand-border);
}
/* Same muted-pill treatment for the logged-in state's single "Mein Konto" link —
   kept as its own class (not .btn-primary) for the same reason as above. */
.yangda-account-link{
  display:inline-flex;
  align-items:center;
  padding:7px 14px;
  font-size:.8125rem;
  font-weight:600;
  border:1px solid var(--brand-border);
  border-radius:999px;
  color:var(--brand-on-ink-muted) !important;
  text-decoration:none;
}
.yangda-account-link:hover{
  color:var(--brand-on-ink) !important;
}
.yangda-cart-badge{
  position:absolute;
  top:-6px;
  right:-7px;
  min-width:16px;
  height:16px;
  padding:0 4px;
  border-radius:999px;
  background-color:var(--brand-stamp);
  color:#fff;
  font-size:.625rem;
  font-weight:700;
  line-height:16px;
  text-align:center;
}
/* "2x" quantity prefix in the cart drawer/page item list (cartbox/items.blade.php).
   #cart-box (vendor's cart-box.blade.php) is hardcoded `bg-white`, not theme-var-based,
   so it stays white regardless of light/dark mode — --cta-red is the right token here
   (same reasoning as its own doc comment above: fixed brand red, never redefined in the
   dark-mode media query below, unlike --stamp which intentionally lightens for dark
   surfaces and would lose contrast on this always-white card). */
.yangda-cart-qty{
  color:var(--cta-red);
}
/* Mockup centers .main-nav between the logo and header-actions via `margin-inline:auto`
   on the nav itself; the real markup instead gives the whole `#navbarMainHeader` collapse
   `flex-grow:1` (Bootstrap navbar default), which without an explicit justify-content just
   left-aligns the nav against the logo instead of centering it in the grown space. Confirmed
   via computed style (`justify-content: normal`) rather than assumed from the CSS source. */
.yangda-header #navbarMainHeader{
  justify-content:center;
}
/* !important below: the parent theme's compiled CSS targets these same elements via
   `.navbar-top .navbar-nav .nav-link` (3 classes) and Bootstrap's `.text-primary` utility
   (also !important) — both have equal-or-higher specificity than a plain `.yangda-header .nav-link`
   selector, so a normal-specificity override silently loses. Confirmed by inspecting matched
   CSSOM rules against the live page, not assumed. */
.yangda-header .nav-link{
  position:relative;
  color:var(--brand-on-ink-muted) !important;
  font-size:.875rem;
  /* the vendor nav view hardcodes `fw-bold` on the active link (Bootstrap's font-weight
     utilities also carry !important) — the mockup's active state is color+underline only,
     same weight as inactive links, so this has to win the same way the color override does. */
  font-weight:500 !important;
}
.yangda-header .nav-link:hover,
.yangda-header .nav-link.active,
.yangda-header .nav-link.text-primary{
  color:var(--brand-on-ink) !important;
}
.yangda-header .nav-link.active::after{
  content:"";
  position:absolute;
  left:0;
  right:0;
  bottom:-6px;
  height:2px;
  background-color:var(--brand-stamp);
}
.yangda-header .navbar-toggler{
  color:var(--brand-on-ink);
  border-color:var(--brand-border);
}
/* No filter needed — the toggler is inside a Bootstrap `.navbar-dark` nav, whose default
   icon is already a semi-transparent WHITE stroke (`rgba(255,255,255,.55)`, confirmed by
   inspecting the actual data-URI background-image). A leftover `filter:invert(1)...` here
   was inverting that already-correct white icon to black — brightness(2) can't recover
   it afterward, since black has zero luminance regardless of the multiplier — which is
   exactly the "black icon on dark background" bug. */
/* Mockup hides the language pills (and the header CTA, already handled by the
   existing d-none/d-md-none classes in the markup) below 768px, showing only the
   logo, cart icon, and hamburger in the compact row — everything else lives inside
   the mobile nav panel instead. Ours never hid the language pills, so they sat
   crowded next to the cart/hamburger on narrow screens. Matches navbar-expand-md's
   own breakpoint (768px) rather than the mockup's 900px, since that's already where
   this theme's nav collapses. */
@media (max-width:767.98px){
  /* Bootstrap's `.navbar > .container{flex-wrap:wrap}` exists so the collapse panel
     can drop to its own row below the brand/toggler when expanded — irrelevant here
     since our mobile nav panel is `position:fixed` (see #navbarMainHeader below), not
     part of the row flow. Adding the account icon pushed the brand+actions pair just
     over the container's content width, and with wrap still enabled that silently
     dropped the whole actions row (cart/user/toggler) onto its own line instead of
     visibly overflowing — confirmed via getBoundingClientRect showing both blocks at
     different `top` offsets. Forcing nowrap here keeps them on one line; the icons
     end up a few px into the container's own right padding rather than the full 28px,
     which is not visually noticeable at this size. */
  .yangda-header .container{
    flex-wrap:nowrap;
  }
  .yangda-header-actions{
    gap:10px;
  }
  /* Scoped to the desktop actions row only (not `.yangda-header .lang-selector`,
     which would also match the second instance now rendered inside the mobile
     nav panel below) — that instance sits next to the mobile "Jetzt bestellen"
     button instead, so mobile visitors have somewhere to switch language. */
  .yangda-header-actions .lang-selector{
    display:none;
  }
  /* Bootstrap's collapse pushes page content down when opened — functional, but not
     what "doesn't work that great" should mean on a restaurant site people browse
     one-handed. The mockup's own .mobile-nav is a fixed full-height panel instead.
     Reusing Bootstrap's collapse mechanics (no markup duplication, keeps the existing
     aria wiring) but overriding its layout once open: fixed position covering
     everything below the header, scrollable if content overflows, instead of
     shifting the hero/content section downward.

     height:auto!important is still needed, but on its own it removes the animation
     entirely (a previous version of this rule shipped that way, and lost the slide
     motion along with the bug — user feedback was that the slide itself looked good,
     just not the jump at the end). Bootstrap's JS animates this element by writing an
     explicit *inline* pixel height (0 -> its normal-flow scrollHeight, ~300px for 3
     links + a button) during .collapsing, then clears that inline style once .show
     lands; without height:auto!important, that inline height wins for the whole
     .collapsing phase, so the panel opens to a short ~300px box first, then SNAPS to
     the full viewport height the instant .show replaces .collapsing (the original "then
     bugs and adds a panel" report). Fixing the jump by neutralizing height animation
     entirely made the toggle instant instead — not what was wanted either. Replacing
     Bootstrap's height-based animation with a transform-based one instead: height stays
     pinned to auto (full panel size) throughout via !important, so there's nothing to
     jump, while a transform slide gives the actual motion.

     First attempt at the transform version put `translateY(-100%)` on .collapsing and
     `translateY(0)` only on .show, expecting the transition to animate between them —
     but sampled several frames through an actual open (not just before/after) and found
     it sits fully off-screen, motionless, for Bootstrap's whole ~350ms .collapsing phase
     (its own internal height-transition timing, unrelated to mine), and only starts
     sliding once .show lands ~350ms later. Net effect: a dead pause, then a slide — not
     the immediate motion that was actually wanted.

     Second attempt moved the off-screen transform to the *base* (closed, display:none)
     selector so the target would change the instant .collapsing appears instead of
     waiting for .show — but sampling showed it jumping straight to the open position
     with no visible motion at all. A `transition` can't animate away from a display:none
     starting point in a single step: the browser needs the element actually rendered at
     its "from" position for at least one frame before a property change will interpolate,
     which is exactly the reflow-forcing dance Bootstrap's own JS does for its height
     animation (reading offsetHeight specifically to force that frame) — nothing was doing
     that for transform, so display:none -> display:block and transform:-100% -> 0 landed
     in the same paint with nothing to animate between.

     A CSS @keyframes animation doesn't have that limitation — its 0% frame IS the
     starting point, it doesn't need to diff against a previously-rendered value — so it
     plays correctly the instant .collapsing/.show makes the element visible, confirmed
     by sampling frames through an actual open afterward.

     That version animated via `transform:translateY()`, which physically moves the
     panel's whole box through space to arrive at its resting position — since the box
     is 767px tall and its rest position is top:77px, sliding it in from
     translateY(-100%) means its box starts at roughly y=-690 to y=77 and moves down
     from there, so for most of the animation part of that box (with z-index:1030) is
     sitting on top of y=0-77 — exactly where the header lives — visibly covering it
     until the panel finishes arriving. Switched to animating `clip-path` on this same
     element instead: the box stays pinned at its final position the entire time,
     nothing physically moves through the header's territory.

     That fixed the header-covering bug, but introduced a different one on close:
     animating `clip-path` directly on this fixed, full-screen element means the browser
     has to progressively reveal and composite the REAL page underneath (the hero photo,
     specifically) at every frame of the transition — screenshotting several frames of an
     actual close showed visible rendering artifacts right at the moving clip boundary,
     worst at the hero photo. Root cause: this element wasn't just a static opaque box
     with an animated crop window — its own visibility WAS the animation, so the
     compositor had to keep re-deciding what of the real page to paint through the
     shrinking gap on every frame.

     Final structure: this outer element is now completely static once shown — no
     animation on it at all, just a plain opaque `background-color` box with
     `overflow:hidden`. All the motion moves to `.yangda-mobile-nav-inner` (a plain
     child div, see header.blade.php), animated via `transform:translateY()` — since a
     `@keyframes` animation doesn't need the display:none-safe treatment (per the note
     above) this works immediately same as the clip-path version did. Because the OUTER
     box never changes and is already fully opaque, there's no real page content for the
     browser to reveal/composite at any point — it's always looking at the same solid
     .yangda-mobile-nav-inner sliding within an unchanging opaque frame, and the outer's
     own `overflow:hidden` clips any part of that sliding child that would otherwise
     extend above the header, the same way the clip-path version did, just without
     needing the outer box itself to be what's animating. */
  @keyframes yangda-mobile-nav-open{
    from{ transform:translateY(-100%); }
    to{ transform:translateY(0); }
  }
  @keyframes yangda-mobile-nav-close{
    from{ transform:translateY(0); }
    to{ transform:translateY(-100%); }
  }
  #navbarMainHeader.show,
  #navbarMainHeader.collapsing{
    position:fixed;
    /* Set on every open by header.blade.php's own script, from the header's actual
       measured bottom edge — accounts for the announcement bar's height when it's
       showing (it isn't part of this fixed panel's own box, so CSS alone can't see
       it). 77px static fallback for the no-JS case only. */
    top:var(--yangda-mobile-nav-top, 77px);
    left:0;
    right:0;
    bottom:0;
    height:auto !important;
    z-index:1030;
    margin:0;
    padding:0;
    background-color:var(--brand-ink);
    overflow:hidden;
    display:block;
  }
  #navbarMainHeader .yangda-mobile-nav-inner{
    height:100%;
    overflow-y:auto;
    padding:8px 28px 40px;
    animation:yangda-mobile-nav-open .3s ease forwards;
  }
  /* .collapsing alone can't distinguish open from close (Bootstrap uses it for both
     directions) — header.blade.php adds/removes this marker class off Bootstrap's own
     hide.bs.collapse/hidden.bs.collapse/show.bs.collapse events specifically so this
     rule can target the closing case only. */
  #navbarMainHeader.is-closing .yangda-mobile-nav-inner{
    animation:yangda-mobile-nav-close .3s ease forwards;
  }
  #navbarMainHeader .nav{
    flex-direction:column;
  }
  #navbarMainHeader .nav-link{
    font-size:1.125rem;
    padding:14px 2px !important;
    border-bottom:1px solid var(--brand-border);
  }
  #navbarMainHeader .nav-link.active::after{
    display:none;
  }
  .yangda-mobile-nav-cta-row{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:14px;
  }
  .yangda-mobile-nav-cta-row .yangda-account-link,
  .yangda-mobile-nav-cta-row .yangda-account-pill{
    margin-left:auto;
  }
  /* Dropped on mobile to keep the guest-state control compact — the login page itself
     already links to register, so this isn't the only way to reach it. !important
     needed: `.yangda-account-pill a` (class+tag, 0-1-1) otherwise outranks this
     single-class selector (0-1-0) regardless of source order. */
  .yangda-account-pill-register{
    display:none !important;
  }
  .yangda-account-pill a:first-child{
    border-right:none;
  }
  /* Matches the account pill/link's own (smaller, muted) height rather than the old
     btn-primary CTA's, now that neither control is trying to look like a bold button. */
  .yangda-mobile-nav-cta-row .lang-selector .list-inline-item button,
  .yangda-mobile-nav-cta-row .lang-selector .list-inline-item span{
    padding:7px 14px !important;
    font-size:.8125rem;
  }
}

/* language selector (local.langselector extension) inherits nav-link styling above;
   fallback in case it renders its own link/button markup */
.yangda-header .lang-selector,
.yangda-header .lang-selector a,
.yangda-header .lang-selector button{
  color:var(--brand-on-ink-muted);
}

/* ==========================================================================
   Footer
   ========================================================================== */
.yangda-footer{
  background-color:var(--brand-ink);
  color:var(--brand-on-ink-muted);
}
.yangda-footer a{
  color:var(--brand-on-ink-muted) !important;
  text-decoration:none;
}
/* Checkout page only, mobile only: the footer's own Bootstrap .container
   adds its own 12px gutter on top of the page's 16px outer margin (28px
   total), one layer deeper than the checkout form card above it (which
   sits directly in the page-level row, just the 16px outer margin) - with
   the mobile cart-preview column removed (see checkout.blade.php), that
   28px-vs-16px mismatch became visible as the footer looking narrower than
   the card above it. Scoped to this page/width only since the footer's
   normal 28px inset is correct everywhere else on the site. */
@media (max-width:991.98px){
  .yangda-footer-checkout .container{
    padding-left:16px;
    padding-right:16px;
  }
}
.yangda-footer a:hover{
  color:var(--brand-on-ink) !important;
}
/* The vendor's footer-menu.blade.php hardcodes `d-none d-sm-block` on its "header"-type
   item titles (display:none!important below 576px) — presumably meant for a different,
   headingless stacked-accordion mobile footer that was never built here. On real phone
   widths (390px, well under 576px) this made "Rechtliches" disappear entirely while its
   links stayed visible underneath. Forcing it visible at every width instead — this
   theme's mobile footer is meant to look like the desktop one, just narrower. */
.yangda-footer .footer-title{
  display:block !important;
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-size:.6875rem;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--brand-on-ink);
  font-weight:600;
}
.yangda-footer hr{
  border-color:var(--brand-border);
  opacity:1;
}
.yangda-footer .social-icons a{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:34px;
  height:34px;
  border:1px solid var(--brand-border);
  border-radius:999px;
  color:var(--brand-on-ink-muted);
}
.yangda-footer .social-icons a:hover{
  border-color:var(--brand-on-ink-muted);
  color:var(--brand-on-ink);
}
.yangda-footer .subscribe-group .form-control{
  background-color:rgba(242,241,236,.06);
  border-color:var(--brand-border);
  color:var(--brand-on-ink);
}
.yangda-footer .subscribe-group .form-control::placeholder{
  color:var(--brand-on-ink-muted);
}
.yangda-footer .subscribe-group .btn-light{
  background-color:var(--stamp);
  border-color:var(--stamp);
  color:#fff;
}
.yangda-footer .subscribe-group .btn-light:hover{
  background-color:var(--stamp-deep);
  border-color:var(--stamp-deep);
}

/* ==========================================================================
   local-header (components/local-header.blade.php) — restaurant info panel.
   Appears on 6 pages: cart, local/menus, local/reviews, reservation/reservation,
   reservation/success, account/reservation. See docs/theme/05, Phase 1.

   !important used on Bootstrap utility classes (.text-muted, .link-dark,
   .link-primary all carry !important in Bootstrap's own compiled CSS) and on
   .breadcrumb-item, which the parent theme also targets directly — same
   specificity lesson as Phase 0, checked via computed styles before shipping,
   not assumed.
   ========================================================================== */
.yangda-local-header{
  background-color:var(--surface);
  border:1px solid var(--border);
  border-radius:14px;
  padding:20px;
}
.yangda-local-header .text-muted{
  color:var(--text-muted) !important;
}
.yangda-local-header .breadcrumb-item{
  color:var(--text-muted);
}
.yangda-local-header .breadcrumb-item + .breadcrumb-item::before{
  color:var(--border);
}
/* Global (not scoped to .yangda-local-header): also used by
   includes/local/location-card.blade.php on the /locations list — the same
   open/closed semantic applies wherever it appears. */
.yangda-status-open{
  color:var(--jade) !important;
}
.yangda-status-closed{
  color:var(--stamp) !important;
}
.yangda-local-header .link-dark,
.yangda-local-header .link-primary{
  color:var(--stamp) !important;
  text-decoration:underline;
}
.yangda-local-header .link-dark:hover,
.yangda-local-header .link-primary:hover{
  color:var(--stamp-deep) !important;
}

/* ==========================================================================
   account-menu (includes/navs/account-menu.blade.php) — sidebar on 7 account
   pages. .text-reset and .text-muted both carry !important in Bootstrap's
   own CSS, so matching !important here too (same lesson as local-header).
   ========================================================================== */
.yangda-account-menu .nav-link{
  color:var(--text) !important;
  border-radius:9px;
  padding:10px 12px;
}
.yangda-account-menu .nav-link:hover{
  background-color:var(--page-bg);
}
.yangda-account-menu .nav-link .fa{
  color:var(--text-muted) !important;
  width:18px;
}
.yangda-account-menu .nav-link.active{
  color:var(--stamp) !important;
  background-color:var(--stamp-tint);
}
.yangda-account-menu .nav-link.active .fa{
  color:var(--stamp) !important;
}
@media (max-width:575.98px){
  /* Vendor markup (vendor/tastyigniter/ti-theme-orange/resources/views/_pages/account/*.blade.php)
     is `.row.py-5 > .col-sm-2.pe-0 + .col-sm-10` — below the sm breakpoint the two columns
     stack with no vertical gap between them at all (.row's own gutters are horizontal-only),
     so the page content card sat right under the last nav link with barely more than its own
     padding between them. Margin instead of a row-level gap utility, since this only needs to
     apply once the columns are actually stacked. */
  .yangda-account-menu{
    margin-bottom:24px;
  }
}

/* ==========================================================================
   pages-menu (includes/navs/pages-menu.blade.php) — sidebar on every CMS page
   (About Us, Policy, Terms, etc., rendered via _layouts/static.blade.php).
   Same visual language and same !important reasoning as account-menu.
   ========================================================================== */
.yangda-pages-menu .nav-link{
  color:var(--text) !important;
  border-radius:9px;
  padding:10px 12px;
}
.yangda-pages-menu .nav-link:hover{
  background-color:var(--page-bg);
}
.yangda-pages-menu .nav-link.active{
  color:var(--stamp) !important;
  background-color:var(--stamp-tint);
}

/* ==========================================================================
   Form fields — global, no Blade overrides needed. Every field-*.blade.php
   partial (checkout, address book, booking, register, account settings,
   contact — 30+ call sites via components/forms/form.blade.php +
   forms/error.blade.php) renders plain, unmodified Bootstrap classes with
   nothing brand-specific baked in, so styling .form-control/.form-check-input
   globally reaches all of them at once. See docs/theme/05, Phase 1.
   ========================================================================== */
.form-control,
.form-select{
  border-color:var(--border);
}
.form-control:focus,
.form-select:focus{
  border-color:var(--stamp);
  box-shadow:0 0 0 .2rem var(--stamp-tint);
}
/* Same old-orange-baked-into-compiled-Bootstrap bug as `.btn-primary`/`.nav-pills`/
   `.progress-bar` above, this time on the loyalty-points redemption slider
   (ti-ext-loyaltypoints's `<input type="range" class="form-range">`, checkout page):
   confirmed in the parent theme's compiled CSS —
   `.form-range::-webkit-slider-thumb{background-color:#ff4900;...}`,
   `::-moz-range-thumb` the same, plus `#ffc8b3` on `:active` and an
   `rgba(255,73,0,.25)` focus ring. Never touched before now because no earlier
   phase happened to exercise a page with a range input on it. Matching
   selector/specificity to Bootstrap's own (single class + pseudo-element), so
   source order alone — this file loads after the parent theme's — is enough,
   same as `.card` above; no `!important` needed (confirmed via computed style). */
.form-range::-webkit-slider-thumb{
  background-color:var(--cta-red);
}
.form-range::-webkit-slider-thumb:active{
  background-color:var(--stamp-deep);
}
.form-range::-moz-range-thumb{
  background-color:var(--cta-red);
}
.form-range::-moz-range-thumb:active{
  background-color:var(--stamp-deep);
}
.form-range:focus::-webkit-slider-thumb{
  box-shadow:0 0 0 1px #fff, 0 0 0 .25rem var(--stamp-tint);
}
.form-range:focus::-moz-range-thumb{
  box-shadow:0 0 0 1px #fff, 0 0 0 .25rem var(--stamp-tint);
}
/* !important: Bootstrap's own floating-label CSS sets color via a higher-specificity
   state selector (`.form-floating > .form-control:focus ~ label`, etc. — confirmed via
   matched CSSOM rules, not assumed), which beats a flat `.form-floating > label` rule
   regardless of load order. Same lesson as Phase 0/local-header/account-menu. */
.form-floating > label{
  color:var(--text-muted) !important;
}
.form-check-input:checked{
  background-color:var(--stamp);
  border-color:var(--stamp);
}
.form-check-input:focus{
  border-color:var(--stamp);
  box-shadow:0 0 0 .2rem var(--stamp-tint);
}

/* Payment method picker (includes/checkout/field-payments.blade.php) — third
   pass. Second pass kept the vendor `.list-group.list-group-flush` +
   `.list-group-item` classes and just added tile borders on top — but
   Bootstrap's own `.list-group-flush > .list-group-item{border-width:0 0 1px}`
   (confirmed in the compiled CSS) outranks a plain `[data-checkout-payment]`
   attribute selector on specificity (two classes beats one attribute
   selector), so it silently zeroed the top/left/right widths back to 0 and
   left only the bottom edge — the selected tile's accent border rendered as
   a stray underline instead of a full border. Since this component's visual
   treatment is fully custom at this point anyway, dropping the Bootstrap
   list-group classes entirely (see field-payments.blade.php) removes the
   fight instead of layering another override on top of it. */
.yangda-payment-options{
  display:flex;
  flex-direction:column;
  gap:10px;
}
[data-checkout-payment]{
  border:1px solid var(--border);
  border-radius:10px;
  padding:14px 16px;
  transition:border-color .1s ease, background-color .1s ease;
}
/* The "slow/unsmooth" feel wasn't the transition itself (100ms is already
   fast) — `.selected` only exists because Blade computed it server-side
   ($paymentIsSelected), so clicking a radio doesn't touch this class at all
   until checkout.js's Livewire.dispatch round-trip finishes and re-renders
   the section (confirmed via network panel: a POST /livewire/update fires on
   click). The visible delay was that whole request, not 100–150ms of CSS.
   Tried `:has(.form-check-input:checked)` here first to react to the radio's
   native checked state with no network involved — but measured it against a
   real click (performance.now() checkpoints at dispatch / next rAF / +50ms)
   and this engine doesn't invalidate `:has()` ancestor styles anywhere near
   as fast as the checked property itself flips; it stayed unmatched past the
   next paint and only resolved ~50ms later, i.e. not the instant fix it
   looked like. Instant class toggle now comes from a small delegated
   listener instead (see field-payments.blade.php) that adds/removes this
   same `.selected` class synchronously on click. */
[data-checkout-payment].selected{
  border-color:var(--stamp);
  background-color:var(--stamp-tint);
}
[data-checkout-payment].yangda-payment-disabled{
  background-color:var(--page-bg);
}
.form-check-input[data-payment-code]:checked{
  background-color:var(--stamp);
  border-color:var(--stamp);
}

/* ==========================================================================
   Pagination — two Blade override paths feed the same `.yangda-pagination`
   class: `resources/views/vendor/igniter.system/_partials/pagination/*`
   (overrides the SYSTEM default view used by bare `->links()` calls — orders,
   reservations, addresses, reviews, locations) and
   `themes/yangda-orange/pagination/*` (overrides `igniter-orange::pagination.*`,
   used explicitly by menu-item-list only). See docs/theme/05, Phase 1.

   Bootstrap 5.3's pagination is custom-property driven on `.pagination` itself
   (`--bs-pagination-*`), so re-declaring those on `.yangda-pagination` (same
   element, same specificity, later in source) wins the cascade for every rule
   that reads them — no !important needed for the base states.
   ========================================================================== */
.yangda-pagination{
  --bs-pagination-color:var(--text);
  --bs-pagination-hover-color:var(--stamp);
  --bs-pagination-focus-color:var(--stamp);
  --bs-pagination-focus-box-shadow:0 0 0 .2rem var(--stamp-tint);
  --bs-pagination-border-color:var(--border);
  --bs-pagination-hover-border-color:var(--border);
  --bs-pagination-border-radius:9px;
}
/* Both pagination Blade views put `.active`/`.disabled` on the parent <li>
   (`.page-item`) and render the current page as a plain <span class="page-link">
   with no `.active` class of its own — but the compiled Bootstrap CSS's active-
   state rules (`.pagination>li.active>a`, `.page-link.active`) only match when
   `.active` is on the link/page-link element itself. So no compiled rule ever
   matches this markup and the current page renders with zero visual distinction
   — confirmed via matched CSSOM rules against the live app. This is a
   pre-existing gap in the vendor markup, not something this theme introduced;
   fixed here with an explicit descendant selector rather than relying on the
   (never-matching) custom properties. */
.yangda-pagination .page-item.active .page-link{
  background-color:var(--stamp) !important;
  border-color:var(--stamp) !important;
  color:#fff !important;
}
.yangda-pagination .page-item.disabled .page-link,
.yangda-pagination li.disabled > span{
  color:var(--text-muted) !important;
}

/* ==========================================================================
   Cart box (livewire/cart-box.blade.php + includes/cartbox/*) — appears on
   cart, local/menus, local/reviews, checkout. No new Blade overrides needed:
   `#cart-box`/`#cart-items`/`#cart-coupon`/`#cart-totals`/`#cart-buttons` are
   already unique ids in the parent markup (verified by grepping every
   *.blade.php in the parent theme — nothing else reuses them), so they're
   reached directly without copying any of these files into the child theme.
   `includes/cartbox/items.blade.php` and `totals.blade.php` already exist as
   child-theme overrides from an earlier, unrelated pass (SKU display, VAT
   breakdown — real business-logic changes, left untouched here). The
   checkout button (`buttons.blade.php`) and the mobile cart-toggle bar
   (`buttons-mobile.blade.php`) both already use `.btn.btn-primary`, which
   Phase 0's global `.btn-primary` rule already themes — confirmed via
   computed styles, no cart-specific button CSS needed. See docs/theme/05,
   Phase 1.
   ========================================================================== */
/* !important: the parent markup's `.rounded-3` Bootstrap utility class already
   carries `border-radius:...!important` in the compiled CSS, which otherwise
   beats a plain `#cart-box{border-radius:14px}` declaration despite the higher
   ID specificity — confirmed via computed styles (14px declared, 8px rendered
   before this fix). Same specificity-vs-!important lesson as everywhere else. */
#cart-box{
  border-radius:14px !important;
}
/* "Basket" heading (livewire/cart-box.blade.php's plain <h5>) rendered in
   Bootstrap's default heading font — every other heading on the site
   (h1/h2/h3, including this same panel's neighbors) uses Cabinet Grotesk. */
#cart-box h5{
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-weight:700;
}
/* Item names/prices and totals-table amounts (.yangda-tabular, added directly
   in includes/cartbox/items.blade.php + totals.blade.php) — money reads as
   data everywhere else on the site (menu prices, footer hours), not as plain
   body text; this panel was the one place still using default proportional
   digits. */
#cart-box .price{
  font-weight:600;
  white-space:nowrap;
}
#cart-box #cart-items .btn-outline-secondary{
  border-color:var(--border);
  color:var(--text);
}
#cart-box #cart-items .btn-outline-secondary:hover{
  background-color:var(--page-bg);
  border-color:var(--border);
  color:var(--text);
}
/* Tip picker (includes/cartbox/tip-form.blade.php): the selected amount only
   gets a plain border-secondary from the parent, no color change — bring it
   in line with the payment-method picker's existing `.selected` treatment. */
#cart-box .tip-amounts .btn-light.active{
  background-color:var(--stamp-tint);
  border-color:var(--stamp) !important;
  color:var(--stamp);
}

/* ==========================================================================
   Order preview (livewire/order-preview.blade.php + includes/order/*) —
   checkout/success, account/order. All wrapper cards already use plain
   Bootstrap `.card.shadow-sm` (no competing `.rounded-N` utility this time,
   unlike cart-box's `.rounded-3`), and `.text-muted`/`.btn-primary` are
   already themed globally, so the only thing actually unstyled here is the
   order-status progress bar. See docs/theme/05, Phase 1.
   ========================================================================== */
/* `.progress-bar` reads `background-color:var(--bs-progress-bar-bg)`, and the
   parent theme's compiled CSS sets that custom property to the OLD orange
   (`#ff4900`) as a hardcoded root-level default — same class of bug as the
   `.btn-primary` text-color issue found during the cart-box pass. `.progress`/
   `.progress-bar` are used nowhere else in the parent theme (confirmed via
   grep), so overriding globally is safe. */
.progress-bar{
  background-color:var(--stamp) !important;
}
/* includes/order/restaurant.blade.php (theme override) — was $location->getThumb(),
   the vendor media-library "thumb" conversion (400×400, `contain` fit), not the same
   curated interior photo used everywhere else on the site
   (\Igniter\YangdaContent\Models\ContentSettings::locationPhoto(), already used by
   the homepage's "Standort & Kontakt" section, .yangda-location-photo below). Switched
   the Blade source to match, and — per that same homepage precedent — round rather
   than the square-with-8px-radius this shipped with first; object-fit:cover so the
   photo's own proportions never dictate the crop, same as the hero photo above and
   .yangda-location-photo itself. */
.yangda-order-restaurant-photo{
  display:block;
  width:100%;
  aspect-ratio:1/1;
  object-fit:cover;
  border-radius:50%;
}

/* ==========================================================================
   Menu page (_pages/local/menus.blade.php) — Phase 2, the first genuinely
   bespoke composition pass. No new Blade files: `#navbar-categories` and the
   `.menu-*` classes on the item card (includes/menu/item.blade.php) are all
   unique to this page (confirmed via grep), reached directly. The category
   pill active-state color comes from the global `.nav-pills` fix above. See
   docs/theme/05, Phase 2.
   ========================================================================== */
/* Fulfillment control ("Delivery · ASAP · change") — was a bordered pill in its
   own toolbar row under the page title; moved to sit directly above the basket
   sidebar instead (both checkout forms already render this same component
   independently, so it isn't needed twice on this page). First cut down here
   was a plain line of text, which read as too quiet next to the basket panel —
   given its own matching card treatment instead (same border/radius/shadow/
   background as #cart-box: bg-white border shadow-sm rounded-3 p-3 in the
   vendor markup, 14px radius override from the rule below), so the two now
   read as a deliberate pair of stacked panels rather than one box and one
   loose line above it. */
.yangda-fulfillment-control{
  display:flex;
  align-items:center;
  gap:4px;
  padding:1rem;
  margin-bottom:16px;
  border:1px solid var(--border);
  border-radius:14px;
  box-shadow:0 .125rem .25rem rgba(0,0,0,.075);
  background-color:var(--surface);
  font-size:.8125rem;
  color:var(--text-muted);
}
.yangda-fulfillment-control a{
  color:var(--stamp) !important;
  font-weight:600;
  text-decoration:none;
}
.yangda-fulfillment-control a:hover{
  color:var(--stamp-deep) !important;
}
/* Sticky category bar — was `.bg-white border-bottom` (hardcoded white
   regardless of light/dark). `--page-bg` keeps it opaque against content
   scrolling underneath while it's stuck, in whichever mode is active. */
.yangda-category-bar{
  background-color:var(--page-bg);
  border-bottom:1px solid var(--border);
  position:relative;
}
/* Category tabs (#navbar-categories, via .layout-scrollable) overflow and scroll
   horizontally once there are more than fit — real once actual categories exist,
   not just the single-category placeholder state this was built against. With no
   affordance, the cut-off tab at the edge just looked broken instead of scrollable
   (confirmed: it's exactly the same in the mockup, incidentally — no explicit fade
   there either, its layout just happens to leave a sliver of the next tab visible).
   A real fade makes the scrollability read as intentional regardless of exact tab
   widths, mobile-only since desktop has room for every tab already. */
@media (max-width:767.98px){
  .yangda-category-bar::after{
    content:"";
    position:absolute;
    top:0;
    right:0;
    bottom:0;
    width:32px;
    background:linear-gradient(to right, transparent, var(--page-bg));
    pointer-events:none;
  }
}
/* Menu search box (includes/menu/search.blade.php) — scoped override, not a
   blanket `.bg-white` fix (that's a much larger site-wide pass, deliberately
   not taken on here — see docs/theme's dark-mode notes). */
.menu-search .input-group{
  background-color:transparent !important;
  border-color:var(--border) !important;
}
.menu-search .input-group > div,
.menu-search input.form-control,
.menu-search a.btn{
  background-color:transparent !important;
  color:var(--text) !important;
}
.menu-search input.form-control::placeholder{
  color:var(--text-muted);
  opacity:1;
}
/* Category quick-nav — plain underline-style links matching
   .theme-preview/speisekarte.html's `.category-nav`, not pill tabs. This is
   the same #navbar-categories anchor-scroll nav used since the first
   local/menus pass (component default useLinkAnchor=true, confirmed via
   Igniter\Orange\View\Components\CategoryList — never changed), just
   restyled to match the mockup's plain-link look instead of the pill
   treatment used elsewhere on this page. */
#navbar-categories .nav-link{
  color:var(--text-muted);
  font-weight:600;
  background:none !important;
}
#navbar-categories .nav-link.active,
#navbar-categories .nav-link:hover{
  color:var(--stamp) !important;
}

/* Menu list — .theme-preview/speisekarte.html's compact numbered-row layout,
   replacing the earlier 2-column card grid (includes/menu/item.blade.php
   rewritten to match: id="menu{id}", the click/wire:click/data-toggle
   behavior on the same wrapping element, and every real state — mealtime
   availability, special pricing, ingredients, favorite button — preserved,
   only the visual structure changed). */
.yangda-menu-category{
  padding-block:44px;
  /* Thicker than the 1px between individual rows (deliberately, at the user's
     request) so a category break reads as a bigger structural gap, not just
     another item boundary — the mockup itself only uses 1px here too. */
  border-bottom:2px solid var(--border);
  /* The <section> itself is the scroll-anchor target now (id="category-*-
     heading" moved here so the toggle button doesn't need its own id), not
     the heading text inside it. */
  scroll-margin-top:90px;
}
.yangda-menu-category:last-of-type{
  border-bottom:0;
}
/* Real <button>, not a styled div — the first click on a category is a
   genuine Livewire action (Livewire/MenuItemList.php's loadCategory(), see
   that class for why not-yet-loaded categories don't have their items
   queried or rendered at all), every click after that is a client-side
   Alpine toggle (see includes/menu/grouped-item.blade.php). Either way this
   needs to behave like the interactive control it is: full-width click
   target, keyboard-focusable, no default button chrome. */
.yangda-menu-category-toggle{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:16px;
  width:100%;
  padding:0;
  border:0;
  background:none;
  text-align:left;
  cursor:pointer;
}
.yangda-menu-category-toggle .yangda-menu-category-title{
  font-size:1.5rem;
  margin-bottom:0;
  display:flex;
  align-items:baseline;
  gap:10px;
}
.yangda-menu-category-count{
  font-size:.9375rem;
  font-weight:600;
  color:var(--text-muted);
}
.yangda-menu-category-chevron{
  flex-shrink:0;
  color:var(--text-muted);
  font-size:1.125rem;
  transition:transform .2s ease;
}
.yangda-menu-category-toggle:not(.collapsed) .yangda-menu-category-chevron{
  transform:rotate(180deg);
}
.yangda-menu-category-toggle-loading .yangda-menu-category-chevron{
  color:var(--stamp);
}
.yangda-menu-category-body{
  padding-top:22px;
}
.yangda-menu-list{
  display:flex;
  flex-direction:column;
}
/* Grid, not flex: the mobile breakpoint below needs to pull .yangda-menu-
   row-extra (description/meta/ingredients) and .yangda-menu-row-fav out of
   the title's column entirely onto their own full-width row each, which a
   single flex row can't do without reordering the DOM (flex-wrap only
   reflows items that no longer fit, it can't retarget which "row" a
   specific item belongs to). grid-template-areas lets the same DOM order
   resolve to a completely different arrangement per breakpoint just by
   redefining the areas. */
.yangda-menu-row{
  display:grid;
  grid-template-columns:46px 64px 1fr 44px;
  grid-template-areas:
    "dishno photo title extra-x"
    "dishno photo extra extra-x";
  column-gap:16px;
  row-gap:4px;
  padding-block:16px;
}
/* grid-template-areas has no "this name spans 2 columns" shorthand — fav
   has to occupy a real column of its own in the template string above, but
   naming that column's cells anything other than "fav" (here, extra-x) and
   pointing .yangda-menu-row-fav at "fav" via grid-area below would leave a
   4th, genuinely nameless column with nothing assigned to it, which grid
   rejects as invalid. Simplest fix: alias it directly. */
.yangda-menu-row-fav{
  grid-area:extra-x;
}
/* Border lives on the row's own wrapper div (via an adjacent-sibling selector),
   not on .yangda-menu-row itself, and as a border-top rather than border-bottom.
   Two earlier attempts both broke on the same fact: the Livewire-rendered list
   wraps every row in its own individual (unclassed) parent div for wire:key
   diffing, so each .yangda-menu-row is always its own parent's only/last child.
   `:last-child{border-bottom:0}` therefore matched EVERY row, not just the true
   last one, killing every separator line. Putting border-bottom back on every
   row instead "fixed" that but created a *different* bug this selector also
   avoids: the truly last row's own line plus the category's own thicker closing
   border-bottom both rendering, back to back, as a visible double line. A plain
   sibling selector needs no last/first detection at all — `div + div` only
   matches from the second wrapper onward, so the first row in a category
   (nothing to separate it from) and the last row (the category's own border
   already closes it) both naturally get no line, and every row in between gets
   exactly one. */
.yangda-menu-list > div + div > .yangda-menu-row{
  border-top:1px solid var(--border);
}
/* The row markup (includes/menu/item.blade.php) also carries the vendor's own
   `.shadow-hover` utility, which paints a raised-card box-shadow on hover —
   the mockup's `.menu-row` has no hover treatment at all, just a flat list.
   !important needed: `.shadow-hover:hover{box-shadow:...!important}` in the
   compiled parent CSS. */
.yangda-menu-row.shadow-hover:hover{
  box-shadow:none !important;
}
.yangda-menu-row .yangda-dish-no{
  grid-area:dishno;
  /* 24px only fit a plain 1-2 digit sku ("35") — measured "958.19" (a real
     sku value) at ~43px in this exact font/size, so the 46px column below
     leaves it a little breathing room rather than sitting flush against the
     wrap point. Some skus in the actual data are much longer still (up to
     13 characters, e.g. "xianglingjuan") — rather than widen this column
     enough to fit those too (which would waste space on every ordinary
     2-digit row), overflow-wrap lets a sku that long wrap across multiple
     lines within this same 46px column instead of overflowing into the
     photo next to it. Same fix, same reasoning, as .yangda-menu-row-title
     above. */
  padding-top:2px;
  font-size:.75rem;
  overflow-wrap:break-word;
}
.yangda-menu-row-photo{
  grid-area:photo;
  width:64px;
  height:64px;
  border-radius:9px;
  overflow:hidden;
  background-color:var(--surface-2);
}
.yangda-menu-row-photo img{
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
}
.yangda-menu-row-title-line{
  grid-area:title;
  min-width:0;
  display:flex;
  /* Not center: a long dish name wraps to 2+ lines on narrow (mobile) widths,
     and centering vertically put the price floating in the gap between the
     wrapped lines instead of level with the first one — flex-start keeps the
     price pinned to the title's top line regardless of how many more lines
     it wraps to below. */
  align-items:flex-start;
  gap:12px;
}
.yangda-menu-row-extra{
  grid-area:extra;
  min-width:0;
}
.yangda-menu-row-title{
  flex:1;
  min-width:0;
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-weight:700;
  font-size:1.125rem;
  /* min-width:0 lets this flex item's BOX shrink below its content's natural
     width, but a single word still won't break inside itself by default
     (overflow-wrap's initial value is normal) — it just paints past the
     box's right edge instead, into the price next to it. That's invisible
     to a getBoundingClientRect() check (the box itself measures correctly,
     only the painted glyphs spill out), which is why this went unnoticed
     through several rounds of that kind of testing. Only shows up when a
     dish name's FIRST word alone is wider than the space left over after
     the (flex-shrink:0, protected) price's width — a title starting with a
     long word does eventually wrap for every word after the first, since
     those land on their own second/third/... line, away from the price
     entirely; the collision is specifically the first line, price and all,
     sharing space with one word too wide for it. */
  overflow-wrap:break-word;
}
.yangda-menu-row-price{
  font-family:"Plex Mono",ui-monospace,monospace;
  font-weight:600;
  white-space:nowrap;
  /* Without this, flex-shrink's default (1) lets the row squeeze this box
     narrower than "£8.90" actually needs whenever space is tight — and
     since the text itself still won't wrap (white-space:nowrap above), it
     just overflows its own shrunk box and bleeds left into the title
     instead. .yangda-menu-row-title already has min-width:0 specifically so
     IT absorbs all the shrinking/wrapping; the price needs the opposite. */
  flex-shrink:0;
}
.yangda-menu-row-desc{
  color:var(--text-muted);
  font-size:.875rem;
  /* No margin-top here: this used to be the 2nd flex child after the title
     within one shared column, needing its own top gap. Now .yangda-menu-
     row-extra is its own grid area/row — the grid's row-gap provides the
     space between the title row and this one instead, and an extra margin
     here on top of that would double it. */
}
.yangda-menu-row-meta{
  margin-top:8px;
}
/* Went through its own history of positions (started as an orphaned block
   below the description, then squeezed inline next to the price where it
   crowded a tap target right where a thumb naturally lands) before landing
   on a full-height column at the end of the row, on desktop — grid-area:
   extra-x above puts it there, and align-self:stretch (redundant with
   grid's own default item stretch, but explicit) makes it a full-height tap
   area sized by whatever the photo/title/extra content sets, not by a fixed
   height of its own. Below 767.98px it moves again (see the media query
   near the top of this block) to its own short row under the description
   instead — a full-height *column* stops making sense once title and extra
   are stacked in the SAME column rather than side by side with the photo,
   so there's no longer one tall shape for it to run alongside. */
.yangda-menu-row-fav{
  display:flex;
  align-items:center;
  justify-content:center;
  align-self:stretch;
  width:44px;
}
.yangda-menu-row-fav .btn-favorite{
  display:flex;
  align-items:center;
  justify-content:center;
  width:100%;
  height:100%;
}
/* Placed AFTER every base .yangda-menu-row* rule above, deliberately — CSS
   resolves same-specificity ties by source order, and being inside @media
   doesn't change that. An earlier version of this block sat right after the
   grid-template-areas definition near the top of this section, above the
   base .yangda-menu-row-fav{width:44px} rule — on a narrow viewport, that
   LATER unconditional rule still won the cascade over this EARLIER
   conditional one, silently overriding this block's width:auto back to
   44px. With a fixed width like that, justify-self:stretch has nothing
   left to stretch, so it fell back to start (left) alignment instead —
   exactly the "small and on the left, not the description's width" bug
   this comment is here to stop from happening again. */
@media (max-width:767.98px){
  /* Photo + title/price stay side by side (the one pairing that reads fine
     narrow); everything else — description, mealtime/special notices,
     ingredients, the favorite button — moves to its own full-width row
     below instead of fighting the title for space in the same column,
     which is what was forcing a wrap on nearly every word of any dish name
     longer than a couple of characters. ".", not a 4th named column: fav's
     own full-width row still only spans photo+title's 2 columns (see
     .yangda-menu-row-fav below for why NOT under dishno too), and grid-
     template-areas requires every row to name the same number of columns
     as grid-template-columns declares. */
  .yangda-menu-row{
    grid-template-columns:46px 64px 1fr;
    grid-template-areas:
      "dishno photo title"
      ".      extra extra"
      ".      fav   fav";
    row-gap:6px;
  }
  .yangda-menu-row-fav{
    /* grid-area:fav, not the desktop rule's extra-x — this breakpoint's own
       grid-template-areas above never defines an area called extra-x, so
       without this override the element would fall back to grid's normal
       auto-placement instead of landing in the "fav" row this whole
       breakpoint exists to create. */
    grid-area:fav;
    /* align-self:stretch (desktop's full-height tap COLUMN alongside the
       photo) makes no sense once this is its own short row instead — but
       justify-self:stretch (full row WIDTH, matching .yangda-menu-row-extra
       above it) does: a 44px tap target floating alone at the row's right
       edge read as too small/disconnected on a real device. The icon itself
       stays centered within that wider box (base rule's justify-content:
       center, unchanged here) — pinning it to one edge of an otherwise-empty
       wide button read as a mistake, not a deliberate hold-over position. */
    justify-self:stretch;
    align-self:auto;
    width:auto;
  }
}

/* ==========================================================================
   Homepage rebuild (_pages/home.blade.php) — Phase "match the mockup".
   Replaces the stock slider+local-search composition with the bespoke
   two-column hero, hours/delivery/location info strip, and featured-dish
   grid from .theme-preview/index.html, implemented with real dynamic data
   instead of the mockup's placeholder copy. See docs/theme/06.
   ========================================================================== */
/* Mockup's hero isn't a flat fill — a warm radial glow (positioned toward the photo,
   upper-right) over a subtle diagonal dark gradient gives it real depth that a single
   solid --brand-ink color can't. This alone was a large part of the "doesn't pop"
   feedback: a flat dark rectangle reads noticeably flatter than the mockup's, even with
   every other token matched. Padding also wasn't symmetric (76px top, 64px bottom). */
.yangda-hero{
  position:relative;
  background:
    radial-gradient(1100px 480px at 82% -10%, rgba(212,74,80,.16), transparent 60%),
    linear-gradient(160deg, var(--brand-ink) 0%, var(--brand-ink-2) 100%);
  color:var(--brand-on-ink);
  /* Was 76px 64px - reasonable when this only ever ran behind a hero photo (the
     .yangda-hero-has-photo mobile override zeroes it out anyway in that case), but
     with no photo currently configured this is the value actually governing the gap
     under the header on every width, and 76px read as too much there - reported
     directly on both desktop and mobile once the header-flush bug above was fixed.
     44px matches .yangda-hero-compact's own top value (the menu page's no-photo
     hero), so a photo-less home hero now reads the same amount of "hero padding" as
     every other no-photo hero on the site, not its own separate, taller value. */
  padding-block:44px 48px;
  overflow:hidden;
}
/* Mockup lays the hero out as a 1.15fr/0.85fr CSS grid (text column deliberately wider
   than the photo column) with a 56px gap — not an even 50/50 split. The markup keeps
   Bootstrap's .row/.col-lg-6 classes (so the existing g-4 gutter/stacking-on-mobile
   behavior below lg is untouched), and this override only replaces the column *ratio*
   at the same >=992px breakpoint .col-lg-6 already uses, neutralizing Bootstrap's own
   width/padding on the two columns so the grid-template-columns value actually controls
   their size. */
@media (min-width:992px){
  .yangda-hero .row{
    display:grid;
    grid-template-columns:1.15fr .85fr;
    gap:56px;
    /* Bootstrap's .row/.g-4 pulls itself left/right by -gutter to cancel its columns'
       own +gutter padding, so content lines up with the container's padded edge. Zeroing
       the columns' padding below (needed so grid-template-columns controls their size,
       not Bootstrap's flex-basis) without also zeroing this margin left the row's own
       negative margin uncompensated — content bled 28px past the container's left edge
       instead of sitting flush with it. Caught by comparing exact x-coordinates against
       the mockup via getBoundingClientRect(), not visible at a glance. */
    margin-left:0;
    margin-right:0;
  }
  .yangda-hero .row > .yangda-hero-head,
  .yangda-hero .row > .yangda-hero-photo{
    width:auto;
    max-width:none;
    padding-left:0;
    padding-right:0;
  }
  /* Mockup's `.hero-head{max-width:560px}` — without this the text column just takes
     the full 1.15fr grid track (~630px+ depending on viewport), letting more text fit
     per line than the mockup allows and changing exactly where the heading wraps. */
  .yangda-hero .row > .yangda-hero-head{
    max-width:560px;
  }
}
.yangda-eyebrow{
  display:block;
  /* em, not a fixed px value - the original 14px (then 20px) bump looked right
     in every desktop/emulated-mobile check, but stayed reported as broken on a
     real device even after ruling out every caching explanation and confirming
     it wasn't Firefox-specific (same result in Chrome, same device). The
     remaining, real-device-only explanation: a fixed px margin next to
     rem/clamp()-sized text doesn't scale with it under a larger OS/browser
     text-size setting - the heading grows, the gap next to it doesn't, so it
     visually shrinks relative to the text even though the pixel value never
     changed. em ties this margin to the eyebrow's own font-size instead, so it
     scales together with whatever is making the text itself bigger. */
  margin-bottom:1.67em;
  font-size:.75rem;
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--brand-on-ink-muted);
}
/* Matches the mockup's --fs-2xl (clamp(2.5rem,5vw + 1rem,4.25rem), weight 800) exactly —
   the previous clamp() topped out 16px smaller (3.25rem/52px vs 4.25rem/68px) and used a
   tight line-height (1.08) instead of the mockup's inherited global 1.55, which is most of
   why the live heading read noticeably smaller and more cramped side by side. */
.yangda-hero-head h1{
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-weight:800;
  font-size:clamp(2.5rem,5vw + 1rem,4.25rem);
  line-height:1.55;
  letter-spacing:-0.02em;
  color:var(--brand-on-ink);
  margin-bottom:20px;
}
.yangda-hero-lead{
  max-width:34rem;
  color:var(--brand-on-ink-muted);
  font-size:1.0625rem;
  margin-bottom:24px;
}
.yangda-hero-status-cta{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:26px;
  margin-bottom:24px;
}
.yangda-status-row{
  margin-bottom:0;
}
.yangda-hero-ctas{
  display:flex;
  gap:14px;
  flex-wrap:wrap;
  align-items:center;
}
/* Guest-only "Bereits ein Konto? Hier anmelden" next to the primary order CTA —
   floating as bare text right next to a solid btn-lg read as an unstyled afterthought
   (loose baseline-aligned text has nothing anchoring it next to a tall bordered
   button). Given the same bordered-pill treatment as the other secondary chips in
   this dark header/hero context (.yangda-status-chip, .yangda-account-pill, the
   lang-selector) — same --brand-border token, not a new one-off value. */
.yangda-hero-login{
  /* inline-block, not inline-flex: the string is one text node followed by an <a>
     (igniter.orange::default.text_logged_in) — flex would box each as a separate
     flex item and silently collapse the space between them (confirmed via
     getComputedStyle/innerHTML — the space is in the DOM, flex just drops it at
     the item boundary). inline-block keeps normal inline whitespace rules. */
  display:inline-block;
  padding:11px 18px;
  border:1px solid var(--brand-border);
  border-radius:999px;
  font-size:.875rem;
  line-height:1.4;
  color:var(--brand-on-ink-muted);
}
.yangda-hero-login a{
  color:var(--brand-on-ink);
  text-decoration:underline;
}
.yangda-hero-login a:hover{
  color:var(--brand-on-ink-muted);
}

/* ==========================================================================
   Site-wide announcement (ContentSettings::announcement_enabled/_text,
   admin-editable — "Zeigt eine Ankündigung..." in the Content settings form).
   Two treatments sharing the same setting: a persistent header bar on every
   page (includes/announcement-bar.blade.php, dark/on-ink context — same
   token family as the rest of the header) and a bigger card on the homepage
   only (_pages/home.blade.php, light page-body context — --stamp-tint/
   --border/--text tokens, same family .yangda-info-card already uses).
   Design renders tested first as static mockups in .theme-preview/ (variants
   A/B/C) before building; C (hero pill) wasn't picked, so no live CSS for it.
   ========================================================================== */
.yangda-announce-bar{
  background-color:var(--stamp);
  color:#fff;
}
.yangda-announce-bar-inner{
  display:flex;
  align-items:center;
  gap:10px;
  padding:10px 24px;
  font-size:.8125rem;
  font-weight:600;
}
.yangda-announce-bar-icon{
  display:inline-flex;
  flex-shrink:0;
  font-size:.875rem;
}
/* Ticker: two identical copies of the joined text sit side by side in a flex track
   (width:max-content, so the track is exactly as wide as its content demands, not
   constrained by the visible bar), animated left by exactly 50% of the track's own
   width — since both halves are identical, the moment the first copy has fully
   scrolled off, the second is sitting exactly where the first started, so the loop
   point is invisible. overflow:hidden on the outer element is what actually clips
   it to the bar's width; min-width:0 is needed for a flex child to be allowed to
   shrink below its content's width at all (the default is that flex items refuse
   to shrink past their content size, which would silently defeat overflow:hidden
   here). */
.yangda-announce-ticker{
  flex:1;
  min-width:0;
  overflow:hidden;
}
.yangda-announce-ticker-track{
  display:flex;
  width:max-content;
}
/* Only the scrolling variant gets the animation — the plain .yangda-announce-ticker-
   track (short text, fits without it) stays put, single copy, no wasted motion. */
.yangda-announce-ticker-track-scrolling{
  animation:yangda-announce-scroll 22s linear infinite;
}
.yangda-announce-ticker-track-scrolling:hover{
  animation-play-state:paused;
}
.yangda-announce-ticker-track span{
  white-space:nowrap;
  padding-right:64px;
}
@keyframes yangda-announce-scroll{
  from{ transform:translateX(0); }
  to{ transform:translateX(-50%); }
}
@media (prefers-reduced-motion: reduce){
  .yangda-announce-ticker-track-scrolling{ animation:none; }
}
.yangda-announce-bar-close{
  flex-shrink:0;
  background:none;
  border:none;
  color:#fff;
  opacity:.85;
  font-size:1.25rem;
  line-height:1;
  cursor:pointer;
  padding:0 0 0 12px;
}
.yangda-announce-bar-close:hover{
  opacity:1;
}
.yangda-announce-section{
  padding-top:32px;
}
.yangda-announce-section-inner{
  display:flex;
  flex-direction:column;
  gap:12px;
}
.yangda-announce-card{
  display:flex;
  align-items:flex-start;
  gap:16px;
  background-color:var(--stamp-tint);
  border:1px solid var(--border);
  border-radius:14px;
  padding:20px 24px;
}
.yangda-announce-card-icon{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:40px;
  height:40px;
  flex-shrink:0;
  border-radius:999px;
  background-color:var(--stamp);
  color:#fff;
  font-size:1rem;
}
.yangda-announce-card-body h3{
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-weight:700;
  font-size:1.0625rem;
  margin-bottom:4px;
  color:var(--text);
}
.yangda-announce-card-body p{
  margin-bottom:0;
  color:var(--text-muted);
  font-size:.9375rem;
}

/* ---------- flash-message toasts (livewire/utils/flash-message.blade.php, vendor) ----------
   Vendor's own toast container sits at `top-0 start-50 translate-middle-x` — dead
   center at the very top of the viewport, directly on top of the header/nav the
   instant one appears (e.g. the "Sie sind nun registriert..." message after
   registering). It's also plain stock Bootstrap: raw `.text-bg-success`/`-danger`/
   `-warning` (default green/red/amber, black text forced via !important) and square
   corners, unrelated to this theme's actual palette anywhere else on the site. This
   is a pure CSS reskin, not a markup rewrite — the vendor <script> that builds each
   toast (buildToast/buildMessage) is untouched, so autohide/dismiss/ARIA all keep
   working exactly as shipped; only the container's position and the resulting
   Bootstrap classes are restyled.
   Moved to bottom-right — nothing else on this site anchors there on desktop, so it
   can never collide with the header or the announcement bar. On a phone-width
   screen there's no such empty corner though — a floating card there sits on top of
   whatever content happens to be at that scroll position (confirmed via screenshot:
   it landed over the hero's lead paragraph and the "Geschlossen" status chip). Below
   the `sm` breakpoint this switches to a full-width bar pinned to the very bottom
   instead — same "snackbar" pattern most mobile apps use, which reads as an
   intentional part of the page's own bottom edge rather than a card floating over
   content. */
#toast-notification{
  top:auto !important;
  bottom:24px !important;
  left:auto !important;
  right:24px !important;
  transform:none !important;
}
.toast{
  --bs-toast-border-radius:12px;
  --bs-toast-box-shadow:0 12px 32px rgba(0,0,0,.18);
  --bs-toast-padding-x:16px;
  --bs-toast-padding-y:14px;
  font-family:"Plex Sans Var","IBM Plex Sans","Noto Sans SC","PingFang SC","Microsoft YaHei",-apple-system,"Segoe UI",sans-serif;
  font-size:.875rem;
  font-weight:600;
}
.toast .toast-body{
  display:flex;
  align-items:flex-start;
  gap:10px;
}
/* One Font Awesome glyph per level via ::before — cheaper than teaching the vendor
   JS a new element, and it's already loading the same all.min.css every other icon
   on this site uses. */
.toast .toast-body::before{
  font:900 1rem "Font Awesome 6 Free";
  line-height:1.4;
  flex-shrink:0;
}
.text-bg-success{ background-color:var(--jade-tint) !important; color:var(--jade) !important; }
.text-bg-success .toast-body::before{ content:"\f058"; }
.text-bg-danger{ background-color:var(--stamp-tint) !important; color:var(--stamp-deep) !important; }
.text-bg-danger .toast-body::before{ content:"\f06a"; }
.text-bg-warning{ background-color:var(--amber-tint) !important; color:var(--amber) !important; }
.text-bg-warning .toast-body::before{ content:"\f071"; }
.text-bg-info{ background-color:var(--surface-2) !important; color:var(--text) !important; }
.text-bg-info .toast-body::before{ content:"\f05a"; color:var(--text-muted); }
/* This has to come AFTER the unconditional `.toast{}`/`#toast-notification{}` rules
   above, not before — a media-query rule doesn't carry extra specificity, so with
   equal-specificity selectors it's pure source order that decides the winner. This
   block sat ABOVE the base rules in an earlier version, which meant the base rule's
   --bs-toast-border-radius:12px always came later in the cascade and silently won
   regardless of viewport width — confirmed by measuring the real (not synthetic)
   toast at 412px width and finding it 12px-rounded and ~340px wide, not the
   full-width/square-corner result this block asks for. 767.98px, not the narrower
   575.98px `sm` breakpoint used elsewhere in this file for small-scale card/table
   tweaks — this needs to match the header's own navbar-expand-md breakpoint instead,
   since that's what actually determines whether a visitor is looking at the mobile
   hamburger layout. */
@media (max-width:767.98px){
  #toast-notification{
    left:0 !important;
    right:0 !important;
    bottom:0 !important;
    padding:0 !important;
    /* Bootstrap's own .toast-container{width:max-content} (from the vendor markup's
       own "toast-container" class, still present alongside this ID) shrink-wraps
       the container to its content's natural width — the reason left:0/right:0
       alone didn't stretch it edge to edge even once the border-radius/ordering bug
       above was fixed. An ID selector beats a class one on specificity, so this
       wins without needing !important — same reasoning that let the position rules
       above override .toast-container's own `position:absolute`. */
    width:100%;
  }
  .toast{
    width:100%;
    max-width:100%;
    /* Bottom-sheet convention: round the edge that's actually free (top), not the
       edge sitting flush against the screen's own bottom (a radius there wouldn't
       even be visible). --bs-toast-border-radius alone can't express this — it's
       one value Bootstrap applies to all four corners — so this sets border-radius
       directly instead of going through that variable. */
    border-radius:16px 16px 0 0;
    --bs-toast-box-shadow:0 -8px 24px rgba(0,0,0,.22);
  }
  /* Menu page only, mobile only - raised directly ("only on the order page"). The
     add-to-cart toast is the one flash message a customer can trigger repeatedly
     while actively scrolling a long menu, low on the screen where the bottom
     snackbar above sits right at thumb height and easy to miss/cover; every other
     page's flash messages (login, address updates, etc.) keep the bottom snackbar
     as-is. Needs !important + source order after the unscoped rules above for the
     same reason those needed !important in the first place - competing against
     another !important rule, not just specificity, and body.yangda-menu-page
     #toast-notification/.toast are already higher-specificity than the bare
     selectors above regardless, but matching the project's own established
     convention of also keeping overrides after their base rule. */
  body.yangda-menu-page #toast-notification{
    top:0 !important;
    bottom:auto !important;
  }
  body.yangda-menu-page .toast{
    border-radius:0 0 16px 16px;
    --bs-toast-box-shadow:0 8px 24px rgba(0,0,0,.22);
  }
}

.yangda-status-chip{
  display:inline-flex;
  align-items:center;
  gap:8px;
  padding:7px 14px;
  border-radius:999px;
  font-size:.75rem;
  font-weight:600;
}
/* Mockup's open-state chip is tinted jade (background/border/text all derived from
   --brand-jade at low opacity), not a neutral chip with jade-colored text on an
   off-white-tinted background — same missing-token bug as --brand-stamp/--stamp. */
.yangda-status-chip.yangda-status-open{
  color:var(--brand-jade);
  background-color:rgba(111,175,148,.14);
  border:1px solid rgba(111,175,148,.35);
}
.yangda-status-chip.yangda-status-closed{
  color:var(--brand-on-ink-muted);
  background-color:rgba(242,241,236,.06);
  border:1px solid var(--brand-border);
}
.yangda-status-dot{
  width:7px;
  height:7px;
  border-radius:50%;
  background-color:currentColor;
  animation:yangda-status-pulse 2.4s ease-in-out infinite;
}
@keyframes yangda-status-pulse{
  0%,100%{ opacity:1; }
  50%{ opacity:.35; }
}
/* Mockup's hero photo is a small, centered, portrait-cropped frame (4:5, capped at
   400px wide, barely-rounded corners, a thin border, no drop-shadow) — not a photo
   stretched to fill its whole grid column with a heavy 18px radius and a large drop
   shadow. Two very different presentations of the same image; this was probably the
   single biggest visible difference in the hero once the layout/background/type were
   already matched. */
.yangda-hero-photo{
  aspect-ratio:4/5;
  max-width:400px;
  margin-inline:auto;
  border-radius:6px;
  overflow:hidden;
  border:1px solid var(--brand-border);
}
.yangda-hero-photo img{
  display:block;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 35%;
}
/* Mockup's own <900px breakpoint: the photo becomes a full-bleed banner above the
   text (order:-1) instead of a small framed portrait. Reusing 991px here to match the
   grid-ratio override's breakpoint above rather than introducing a third value. */
@media (max-width:991px){
  /* Mockup also overrides padding-block to `0 40px` at this breakpoint (mockup's own
     rule: `.hero .wrap{padding-block:0 40px}` under max-width:900px) — never carried
     over here. The base `.yangda-hero{padding-block:76px 64px}` rule above has no
     media guard, so it kept applying at mobile too, leaving a 76px dead gap of plain
     background between the header and the photo, which is order:-1'd to the very top
     of the section at this width and expects to sit flush against it, not floating
     56px+ below it.

     Scoped to .yangda-hero-has-photo (new modifier class, set in home.blade.php only
     when a hero photo is actually configured), not bare .yangda-hero - this was
     unconditional before, so with no hero photo set (true on this install right now:
     ContentSettings::heroImage() returns null), the eyebrow text lost its padding-top
     for a photo that was never there to sit flush against, leaving it flush against the
     header instead, with none of the breathing room a hero section normally has. The
     menu page's own hero never showed this (same underlying .yangda-hero, no photo
     option) only because .yangda-hero-compact{padding-block:44px 36px}, defined later
     in this file at the same specificity, happened to win that particular tie -
     coincidence, not a scoping fix, which is why home needed its own. */
  .yangda-hero-has-photo{
    padding-block:0 40px;
  }
  .yangda-hero .row{
    gap:32px;
  }
  .yangda-hero-photo{
    order:-1;
    max-width:none;
    /* This bleed offset was computed against the container's old 12px gutter, before
       the sixteenth pass of docs/theme/06 changed .container's padding to 28px each
       side — never updated at the time, so the photo stopped reaching the true edge
       and sat with a visible ~4px gap on each side instead. Recomputed and confirmed
       via getBoundingClientRect() (photo.x was 4px, not 0) rather than assumed. */
    width:calc(100% + 32px);
    margin-inline:-16px;
    /* .yangda-hero-photo is still a Bootstrap .col-lg-6 at this width (the >=992px
       grid override further up doesn't apply here), so it still carries its own
       12px-each-side column gutter as *padding*. The width/margin above bleed the
       BORDER box to the true screen edge, but padding eats back into the CONTENT
       box from the inside — so the outer box reached the edges while the <img>
       inside it (which fills the content box, not the border box) stopped 12px
       short on each side. Confirmed via computed style: photo padding was "0 12px",
       img width was 366px instead of the full 390px viewport. Zeroing it here is
       what actually lets the image itself reach the edges, not just its container. */
    padding-left:0;
    padding-right:0;
    aspect-ratio:16/11;
    border-radius:0;
    border-width:0 0 1px;
  }
  .yangda-hero-photo img{
    object-position:50% 40%;
  }
}

/* ---------- info strip (hours / delivery / location) ---------- */
.yangda-info-strip{
  padding-block:56px;
}
.yangda-section-heading{
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-weight:700;
  font-size:1.625rem;
  margin-bottom:26px;
}
.yangda-section-heading-row{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:16px;
  flex-wrap:wrap;
  margin-bottom:26px;
}
.yangda-card-grid{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:20px;
  margin-bottom:20px;
}
@media (max-width:767px){
  .yangda-card-grid{ grid-template-columns:1fr; }
}
.yangda-info-card-title{
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-size:1.0625rem;
  font-weight:700;
  margin-bottom:10px;
}
.yangda-info-card{
  background-color:var(--surface);
  border:1px solid var(--border);
  border-radius:14px;
  padding:20px;
}
.yangda-info-card-note{
  margin:12px 0 0;
  font-size:.8125rem;
  color:var(--text-muted);
}
.yangda-tabular{
  font-family:"Plex Mono",ui-monospace,monospace;
  font-variant-numeric:tabular-nums;
}
.yangda-hours-table{
  width:100%;
  border-collapse:collapse;
}
.yangda-hours-table td{
  padding:8px 0;
  border-bottom:1px solid var(--border);
  font-size:.875rem;
}
.yangda-hours-table tr:last-child td{ border-bottom:0; }
.yangda-hours-table td:last-child{ text-align:right; color:var(--text); }
.yangda-hours-table td:first-child{ color:var(--text-muted); white-space:nowrap; padding-right:12px; }
.yangda-hours-table tr.yangda-today td:first-child{ color:var(--text); font-weight:700; }
.yangda-today-tag{
  display:inline-block;
  margin-left:8px;
  padding:1px 8px;
  border-radius:999px;
  background-color:var(--jade-tint);
  color:var(--jade);
  font-size:.6875rem;
  font-weight:700;
  text-transform:uppercase;
}
.yangda-zone-table{
  width:100%;
  border-collapse:collapse;
  font-size:.875rem;
  /* Fixed, not auto (the browser default): with auto layout, a single
     unbreakable word sizes its whole column to fit on one line - German
     "MINDESTBESTELLWERT" has no spaces to wrap on, so it was forcing that
     column to ~133px out of a ~340px-wide mobile table, squeezing the
     postcode-list column down to ~72px and making it wrap onto many lines
     instead. Fixed layout + explicit widths below hands each column a
     reasonable share regardless of its longest word. */
  table-layout:fixed;
}
.yangda-zone-table th{
  text-align:left;
  padding-bottom:10px;
  border-bottom:1px solid var(--border);
  font-size:.6875rem;
  font-weight:600;
  letter-spacing:.04em;
  text-transform:uppercase;
  color:var(--text-muted);
  /* Real hyphenation (needs <html lang="de">, already set in _layouts/default.blade.php),
     not word-break - a first attempt with word-break:break-word split
     "MINDESTBESTELLWERT"/"LIEFERKOSTEN" at whatever character happened to hit the
     column edge, leaving a stray orphaned "N" alone on its own line for the latter.
     hyphens:auto breaks at real German syllable boundaries instead; overflow-wrap is
     the fallback for anything the hyphenation dictionary can't help with (a very long
     postcode run in the first column, for instance). */
  hyphens:auto;
  overflow-wrap:break-word;
}
.yangda-zone-table th:not(:first-child),
.yangda-zone-table td:not(:first-child){ text-align:right; }
.yangda-zone-table th:first-child,
.yangda-zone-table td:first-child{ width:38%; }
.yangda-zone-table th:nth-child(2),
.yangda-zone-table td:nth-child(2){ width:32%; }
.yangda-zone-table th:nth-child(3),
.yangda-zone-table td:nth-child(3){ width:30%; }
.yangda-zone-table td{
  padding:10px 0;
  border-bottom:1px solid var(--border);
  overflow-wrap:break-word;
}
.yangda-zone-table tr:last-child td{ border-bottom:0; }
.yangda-zone-plz{
  color:var(--text-muted);
  font-size:.6875rem;
  margin-top:2px;
}

.yangda-location-strip{
  display:flex;
  align-items:center;
  gap:24px;
  padding:24px;
  background-color:var(--surface);
  border:1px solid var(--border);
  border-radius:14px;
}
.yangda-location-photo{
  flex:0 0 auto;
  width:110px;
  height:110px;
  border-radius:50%;
  object-fit:cover;
}
.yangda-location-text h3{
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-size:1.0625rem;
  font-weight:700;
  margin-bottom:6px;
}
.yangda-location-text p{
  color:var(--text-muted);
  margin-bottom:10px;
}
.yangda-contact-list{
  list-style:none;
  margin:0;
  padding:0;
  display:flex;
  gap:20px;
  flex-wrap:wrap;
}
/* Same missing-reset gap as .yangda-menu-link above — the mockup's phone/email links
   in the location strip (`.contact-list a{color:var(--text)}`) rely on its global
   `a{text-decoration:none}` reset for the no-underline look; this theme has no such
   reset, so these fell back to Bootstrap's default underline. */
.yangda-contact-list a{
  text-decoration:none;
}
@media (max-width:575px){
  .yangda-location-strip{ flex-direction:column; text-align:center; }
  /* text-align:center above only affects normal block/inline content — .yangda-contact-list
     is itself a flex row, so its items ignored it and stayed flush-left under the now-
     centered heading/address, the actual "not aligned to center" bug. */
  .yangda-contact-list{ justify-content:center; }
}

/* ---------- featured dishes ("Aus der Karte") ---------- */
.yangda-featured{
  padding-block:56px;
}
/* Mockup has a global `a{text-decoration:none}` reset (every link is unstyled by
   default, styled classes opt back in) — this theme has no equivalent reset, so plain
   links fall back to Bootstrap's default underline unless a class explicitly turns it
   off. `.yangda-menu-link` never did. */
.yangda-menu-link{
  font-weight:600;
  white-space:nowrap;
  text-decoration:none;
}
/* Mockup's `.table-spread{aspect-ratio:16/7}` is a wide, tight banner crop (~492px tall
   at this container's width) — the previous `max-height:420px` here was an unmatched
   guess from earlier in the project, not derived from the mockup's actual CSS, and
   landed close but visibly off. Matching the real aspect-ratio (plus its <640px
   breakpoint swap to 4/3) is more correct than any fixed pixel height regardless of
   container width changes down the line. */
.yangda-table-spread{
  border-radius:14px;
  overflow:hidden;
  margin-bottom:24px;
  aspect-ratio:16/7;
}
.yangda-table-spread img{
  display:block;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:center 42%;
}
@media (max-width:640px){
  .yangda-table-spread{
    aspect-ratio:4/3;
  }
}
.yangda-dish-grid{
  display:grid;
  grid-template-columns:1fr 1fr 1fr;
  gap:20px;
}
@media (max-width:991px){
  .yangda-dish-grid{ grid-template-columns:1fr 1fr; }
}
@media (max-width:575px){
  .yangda-dish-grid{ grid-template-columns:1fr; }
  /* Title and price sharing one row (space-between) works fine at desktop card
     widths, but on a narrow phone the fixed-width, never-wrapping price
     (white-space:nowrap) eats into the title's share of the row — for a longer
     name like "Ente süß-sauer" next to a 180px photo, that left the title only
     enough room to wrap 3 lines deep instead of reading normally. Stacking price
     under title here gives the title the card's full width to wrap into, same
     as every other row in this theme already does when space is tight. */
  .yangda-dish-title-row{
    flex-direction:column;
    align-items:flex-start;
    gap:2px;
  }
}
.yangda-dish{
  display:flex;
  gap:16px;
  padding:18px;
  background-color:var(--surface);
  border:1px solid var(--border);
  border-radius:14px;
  color:var(--text);
}
.yangda-dish:hover{
  border-color:var(--stamp);
}
/* Large featured dish — first item in the set, matching the mockup's
   "one big dish + a grid of smaller ones" split. */
.yangda-dish-featured{
  margin-bottom:20px;
}
.yangda-dish-featured .yangda-dish-photo{
  width:180px;
  height:180px;
}
.yangda-dish-featured .yangda-dish-title{
  font-size:1.125rem;
}
.yangda-dish-photo{
  flex:0 0 auto;
  width:88px;
  height:88px;
  border-radius:10px;
  overflow:hidden;
}
.yangda-dish-photo img{
  width:100%;
  height:100%;
  object-fit:cover;
}
.yangda-dish-body{ flex:1 1 auto; min-width:0; }
.yangda-dish-no{
  display:block;
  font-size:.6875rem;
  color:var(--text-muted);
  margin-bottom:2px;
}
.yangda-dish-title-row{
  display:flex;
  justify-content:space-between;
  gap:12px;
  align-items:baseline;
}
.yangda-dish-title{
  font-weight:700;
}
.yangda-dish-price{
  white-space:nowrap;
  color:var(--stamp);
}
.yangda-dish-desc{
  margin:4px 0 0;
  font-size:.8125rem;
  color:var(--text-muted);
}

/* ---------- footer additions (brand block, hours recap, address) ---------- */
.yangda-footer-brand{
  display:flex;
  align-items:center;
  gap:10px;
  margin-bottom:12px;
}
.yangda-footer-brand .img-logo{
  height:32px;
  width:auto;
  border-radius:50%;
}
.yangda-footer-brand .yangda-brand-name{
  font-family:"Cabinet Grotesk","Helvetica Neue","Noto Sans SC","PingFang SC","Microsoft YaHei",Arial,sans-serif;
  font-weight:700;
  font-size:1.0625rem;
  color:var(--brand-on-ink);
}
.yangda-footer-tagline{
  color:var(--brand-on-ink-muted);
  font-size:.875rem;
  max-width:26rem;
  margin-bottom:16px;
}
.yangda-footer-hours{
  width:100%;
  border-collapse:collapse;
}
.yangda-footer-hours td{
  padding:3px 0;
  font-size:.8125rem;
  color:var(--brand-on-ink-muted);
}
/* Weekday cell has no fixed width, so once the time cell needs more room than this
   narrow footer column has to give (two ranges on one line, or zh_CN's larger root
   font-size), the table was squeezing the weekday cell below its content's natural
   width — for a 2-character CJK day name ("周一") that meant wrapping mid-word into
   two stacked lines instead of shrinking gracefully the way a 3-letter "Di." can.
   nowrap forces the table to give this cell whatever width it actually needs first. */
.yangda-footer-hours td:first-child{
  white-space:nowrap;
  padding-right:12px;
}
.yangda-footer-hours td:last-child{
  text-align:right;
  color:var(--brand-on-ink);
}
.yangda-footer-address{
  color:var(--brand-on-ink-muted);
  font-size:.875rem;
  margin-bottom:8px;
}

/* ---------- compact hero variant (menus page) ---------- */
/* "Compact" was a misnomer — the mockup's own `.page-header h1` (speisekarte.html)
   reuses the exact same --fs-2xl clamp/weight as the home hero's h1, not a smaller
   one. This previously used its own clamp(2rem,3.5vw,2.75rem)/weight:700, topping
   out at 44px vs the mockup's 68px — the actual, dominant reason this page's
   heading read smaller than the mockup side by side. */
.yangda-hero-compact{
  padding-block:44px 36px;
}
.yangda-hero-compact h1{
  font-family:"Cabinet Grotesk","Helvetica Neue",Arial,sans-serif;
  font-weight:800;
  font-size:clamp(2.5rem,5vw + 1rem,4.25rem);
  line-height:1.55;
  letter-spacing:-0.02em;
  color:var(--brand-on-ink);
  margin:0 0 10px;
}
.yangda-hero-compact .yangda-hero-lead{
  font-size:1.125rem;
  margin-bottom:0;
}

/* ---------- footer payment badges ---------- */
/* Was a bare-outline 6px-radius rectangle with uppercase text — the only footer
   chip using that radius/case combination (.yangda-account-pill, .yangda-status-
   chip and the lang-selector pill all use border-radius:999px and sentence case;
   the all-caps here made three genuinely long labels like "Barzahlung bei
   Lieferung" read as shouting for no reason). Matched to that existing pill
   family instead of inventing a fourth treatment, plus an icon (see
   footer.blade.php) so three long text-only labels aren't the only way to tell
   them apart at a glance. */
.yangda-pay-badges{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:8px;
}
/* Each SVG already draws its own white rounded-rect card (see the comment above this
   block's call site) — the border here is just a hairline to keep the white card from
   blending into the footer's own light-mode surface, same reasoning as brand-border
   everywhere else in the dark chrome. */
.yangda-pay-badges img{
  height:28px;
  width:auto;
  border-radius:5px;
  border:1px solid var(--brand-border);
}
/* Inherits .yangda-footer's own var(--brand-on-ink-muted) text color — Bootstrap's
   .text-muted is calibrated for light backgrounds and would be too low-contrast
   against the footer's dark ink surface. */
.yangda-pay-note{
  margin-top:8px;
  max-width:320px;
}

/* Favorite button (local.menufavorites extension) inside a menu row — the
   extension's own .btn-favorite is a bordered pill button (sized for
   standalone use); too heavy for a compact row where it's just a secondary
   affordance next to the title/price. Strip the box, keep just the star.
   box-shadow: Bootstrap's plain `.btn` base rule (not just the pill-button
   look already stripped above) carries its own default inset-highlight +
   drop-shadow — invisible at the old tiny inline icon size, but this button
   now fills a 44px-wide, full-row-height column, large enough for that
   shadow to render as a visible box outline colliding with the row divider
   lines above/below it. */
.yangda-menu-row-fav .btn-favorite{
  padding:0;
  border:0;
  background:none;
  box-shadow:none;
}
/* Re-adds the border this same selector just stripped above, mobile only —
   placed AFTER that rule specifically (same specificity, so source order
   decides) after an earlier version of this override, positioned earlier in
   the file, got silently cancelled back out to border:0 by this later
   unconditional rule despite its own @media condition matching. On mobile,
   .yangda-menu-row-fav's own full-height desktop column is gone (see its
   media query above) and height:100% collapses to just the icon's line
   height — a border there turns it back into a properly-sized, visibly
   button-shaped tap target instead of a star floating in empty space. */
@media (max-width:767.98px){
  .yangda-menu-row-fav .btn-favorite{
    min-height:44px;
    border:1px solid var(--border);
    border-radius:10px;
  }
}
