/* ==========================================================================
   AsianFresh — per-tenant theme. Served from THIS bundle's CmsStylesheets row
   via /cms/assets/theme.css. SELF-CONTAINED: it does not @import
   /assets/tokens.css (that is Asharaf's palette) and it does not depend on
   Tailwind. The FreshDays prototype used the Tailwind CDN; a CDN <script> is a
   runtime dependency + a CSP liability, so every utility the templates need is
   written out here as real CSS.

   Palette ported verbatim from tijaro-storefront/templates/FreshDays/index.html
   (the tailwind.config block on line 17) — do not "tidy" these hex values.
   ========================================================================== */

:root {
  /* Core surfaces / ink */
  --af-cream: #FFF9F0;
  --af-ink: #1B2B23;
  --af-mud: #6B6257;
  --af-chilli: #C1372B;
  --af-white: #ffffff;

  /* Kerala green ramp */
  --af-kg-50: #EFF8F2;
  --af-kg-100: #D8EFDF;
  --af-kg-200: #B2E0C2;
  --af-kg-600: #1B9448;
  --af-kg-700: #157A3B;
  --af-kg-800: #0F5C2C;
  --af-kg-900: #083D1D;

  /* Gold / chilli accent ramp (the prototype's "gold" scale is warm red) */
  --af-gold-100: #FBE3DF;
  --af-gold-200: #F3C0B7;
  --af-gold-400: #D93A2B;
  --af-gold-600: #A11F12;

  /* ── Bridge to the storefront's React design tokens ──────────────────────
     The React (non-CMS) routes — /checkout, /cart, /order/{token}, /account —
     paint entirely from the `--lm-*` custom properties declared in the app's
     own globals.css `:root`. This stylesheet loads AFTER that one, so the
     declarations below win the cascade and those routes inherit the AsianFresh
     palette instead of the platform default. Every value is an existing
     `--af-*` token: no new colours, and nothing here leaks outside this
     tenant's stylesheet.

     This is the ONLY thing the stepped checkout + order-confirmation surfaces
     need in order to be on-brand — they carry no tenant-specific CSS of their
     own (see the `tj-co-*` / `tj-oc-*` blocks in the storefront's globals.css,
     which are written against these tokens). */
  --lm-bg: var(--af-cream);
  --lm-bg-1: var(--af-kg-50);
  --lm-bg-2: var(--af-cream);
  --lm-surface: var(--af-white);
  --lm-surface-hi: var(--af-kg-50);
  --lm-line: var(--af-line);
  --lm-line-strong: var(--af-line-strong);

  --lm-ink: var(--af-ink);
  --lm-ink-dim: var(--af-mud);
  --lm-ink-faint: rgba(107, 98, 87, .72);

  /* Accent slot: the masthead green is the storefront's primary action colour,
     so buttons and selected states read as AsianFresh rather than saffron. */
  --lm-saffron: var(--af-kg-600);
  --lm-saffron-soft: var(--af-kg-700);
  --lm-ember: var(--af-gold-400);
  --lm-gold: var(--af-gold-400);
  --lm-leaf: var(--af-kg-700);
  --lm-cream: var(--af-cream);
  --lm-accent-tint: var(--af-kg-50);
  --lm-on-accent: #ffffff;
  --lm-paper: var(--af-white);
  --lm-paper-ink: var(--af-ink);

  /* The deep stop of the "Cardamom" gift-card gradient (#2108). The shared
     component used to hard-code `#5A7A55`, a shade of Asharaf's sage that the
     design system never gave a token. It now reads `var(--lm-leaf-deep,
     #5A7A55)`, so every tenant that leaves this undefined renders exactly as
     before and this tenant gets its own deepest green instead. */
  --lm-leaf-deep: var(--af-kg-900);

  --lm-radius: var(--af-radius);
  --lm-radius-sm: var(--af-radius-sm);
  --lm-radius-lg: var(--af-radius-lg);

  --lm-sans: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --lm-serif: 'Baloo Chettan 2', 'Manrope', system-ui, sans-serif;
  --lm-mono: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --af-sans: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --af-display: 'Baloo Chettan 2', 'Manrope', system-ui, sans-serif;

  --af-line: rgba(0, 0, 0, 0.06);
  --af-line-strong: rgba(0, 0, 0, 0.10);
  --af-radius: 16px;
  --af-radius-sm: 10px;
  --af-radius-lg: 22px;
  --af-shadow-card: 0 1px 2px rgba(27, 43, 35, .05);
  --af-shadow-lift: 0 10px 24px -12px rgba(11, 107, 79, .35);
  --af-shadow-pop: 0 12px 34px -14px rgba(8, 61, 29, .45);

  /* Header height — script.js keeps this in sync with the real element so
     sticky offsets never guess. */
  --af-header-h: 118px;

  /* The five banner gradients. Named exactly as the prototype's THEMES map so
     the CMS dropdown values map 1:1 to a class (see .af-theme-*). */
  --af-grad-backwater: linear-gradient(115deg, #083D1D 0%, #1B9448 55%, #2BB35C 100%);
  --af-grad-harbour: linear-gradient(115deg, #04353B 0%, #0A6070 60%, #0E8391 100%);
  --af-grad-kasavu: linear-gradient(115deg, #7A4E0B 0%, #C98A1E 55%, #E8B33C 100%);
  --af-grad-chilli: linear-gradient(115deg, #6E1A12 0%, #B42F22 55%, #D95A3F 100%);
  --af-grad-midnight: linear-gradient(115deg, #101815 0%, #26332B 60%, #3C4E41 100%);
}

/* ── reset ──────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--af-cream);
  color: var(--af-ink);
  font-family: var(--af-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: var(--af-kg-600); text-decoration: none; }
a:hover { color: var(--af-kg-800); }

/* `body` is load-bearing, not tidiness. On the React routes `app/layout.tsx`
   loads the Tailwind Play CDN `beforeInteractive`, and that script injects its
   preflight into a RUNTIME <style> which lands after every <link> — including
   this one. Preflight declares `h1..h6 { font-size: inherit; font-weight:
   inherit }` at (0,0,1), the same specificity as a bare `h1`, so on source
   order it won and this reset never applied to a React page: every heading
   without a class of its own fell back to the body's 400. The descendant
   selector makes it (0,0,2) and settles it without `!important`. The CMS pages
   do not load that CDN (this file writes out the utilities its templates need),
   so they rendered 800 all along and are unaffected either way. */
body h1, body h2, body h3, body h4, body h5 { margin: 0; font-family: var(--af-display); font-weight: 800; line-height: 1.15; }
p { margin: 0; }
img { max-width: 100%; display: block; }

button {
  font-family: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

input, textarea, select { font-family: inherit; font-size: inherit; color: inherit; }

/* Material Symbols Rounded — the icon font is loaded by the layout's <link>. */
.ms {
  font-family: 'Material Symbols Rounded';
  font-weight: 400;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  direction: ltr;
  font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 24;
  -webkit-font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  user-select: none;
}

/* Horizontal rails hide their scrollbar but stay scrollable / swipeable. */
.af-noscroll { scrollbar-width: none; -ms-overflow-style: none; }
.af-noscroll::-webkit-scrollbar { display: none; }

.af-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.af-hide { display: none !important; }

:focus-visible { outline: 2px solid var(--af-kg-600); outline-offset: 2px; }

/* ── shell ──────────────────────────────────────────────────────────────── */

.af-shell { min-height: 100vh; width: 100%; overflow-x: hidden; background: var(--af-cream); }

.af-main { width: 100%; padding: 12px 12px 112px; }

@media (min-width: 768px) {
  .af-main { padding: 20px 24px 64px; }
}

.af-section { margin-top: 20px; }

.af-panel {
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: var(--af-white);
  padding: 12px;
}

@media (min-width: 768px) {
  .af-panel { padding: 16px; }
}

.af-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.af-section-title { font-size: 17px; }

@media (min-width: 768px) {
  .af-section-title { font-size: 20px; }
}

.af-viewall {
  font-size: 11.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--af-kg-600);
  white-space: nowrap;
}

/* ── header ─────────────────────────────────────────────────────────────── */

.af-header {
  position: sticky;
  top: 0;
  z-index: 40;
  box-shadow: 0 2px 12px -6px rgba(15, 92, 44, .5);
}

.af-utility {
  display: none;
  background: var(--af-kg-900);
  padding: 7px 24px;
  font-size: 11px;
  font-weight: 500;
  color: var(--af-gold-200);
}

@media (min-width: 768px) { .af-utility { display: block; } }

.af-utility-inner { display: flex; width: 100%; align-items: center; gap: 24px; flex-wrap: wrap; }
.af-utility-item { display: flex; align-items: center; gap: 4px; }
.af-utility-item .ms { font-size: 13px; }
.af-utility-spacer { margin-left: auto; }
.af-utility a:hover, .af-utility button:hover { color: #fff; }
.af-utility a { color: inherit; }

.af-headbar { background: var(--af-kg-700); }

.af-headrow {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
}

@media (min-width: 768px) {
  .af-headrow { gap: 24px; padding: 12px 24px; }
}

.af-burger {
  display: grid;
  place-items: center;
  height: 36px;
  width: 36px;
  flex: 0 0 auto;
  border-radius: var(--af-radius-sm);
  color: rgba(255, 255, 255, .9);
}
.af-burger:hover { background: rgba(255, 255, 255, .1); }
.af-burger .ms { font-size: 22px; }

@media (min-width: 768px) { .af-burger { display: none; } }

.af-brand { display: flex; flex: 0 0 auto; align-items: center; }
.af-brand-plate {
  display: flex;
  align-items: center;
  border-radius: var(--af-radius-sm);
  background: #fff;
  padding: 4px 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .06);
}
.af-brand-logo { height: 32px; width: auto; }
@media (min-width: 768px) { .af-brand-logo { height: 40px; } }

.af-brand-word {
  font-family: var(--af-display);
  font-size: 20px;
  font-weight: 800;
  color: var(--af-kg-800);
  line-height: 1;
  white-space: nowrap;
}
.af-brand-word em { font-style: normal; color: var(--af-gold-400); }
@media (min-width: 768px) { .af-brand-word { font-size: 24px; } }

.af-search-wrap { display: none; min-width: 0; flex: 1; align-items: center; }
@media (min-width: 768px) { .af-search-wrap { display: flex; } }

.af-search {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  border-radius: 12px;
  background: #fff;
  padding: 9px 12px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .05);
}
.af-search:focus-within { box-shadow: inset 0 0 0 2px var(--af-gold-400); }
.af-search .ms { font-size: 20px; color: rgba(107, 98, 87, .7); }
.af-search input {
  width: 100%;
  border: 0;
  background: transparent;
  outline: none;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--af-ink);
}
.af-search input::placeholder { color: rgba(107, 98, 87, .6); }

.af-search-btn {
  display: none;
  flex: 0 0 auto;
  border-radius: var(--af-radius-sm);
  background: var(--af-kg-600);
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #fff;
}
.af-search-btn:hover { background: var(--af-kg-700); }
@media (min-width: 1024px) { .af-search-btn { display: block; } }

/* ── deliver-to: the control and the panel behind it ────────────────────────
   The template draws the control (index.html:54-57) and nothing behind it, so
   every value below is derived from THIS client's own vocabulary rather than
   borrowed: --af-radius / -sm / -lg for the corners, the kg ramp for the
   accent, --af-gold-* (this palette's warm red) for the refusal, the
   .af-label micro-caps for every heading, .af-kicker's pill metrics for the
   fee chips, and the sheet/scrim geometry of this bundle's own .af-list-side
   filter sheet. Nothing here reads or re-points an --lm-* token.

   BREAKPOINTS. The button is the pin alone from md and gains its two lines of
   text from lg (the template's own idiom — its Search button appears at lg,
   its account text at md). Below md the button is gone, as the template has
   it, and the drawer row is the way in; the wrap goes out of flow there so it
   costs the 390px masthead nothing. Its text is never `display: none`, only
   .af-sr-clipped, so the button keeps its accessible name at every width. */

.af-deliverto-wrap { position: relative; flex: 0 0 auto; }
/* Out of flow below md: the button is hidden there, and an empty flex item
   would still eat a 12px gap out of a row that has none to spare. The panel
   and scrim inside are position: fixed, so they are unaffected. */
@media (max-width: 767.98px) { .af-deliverto-wrap { position: absolute; } }

.af-deliverto {
  display: none;
  align-items: center;
  gap: 6px;
  border-radius: 12px;
  padding: 6px 10px;
  color: rgba(255, 255, 255, .95);
  text-align: left;
}
.af-deliverto:hover { background: rgba(255, 255, 255, .1); }
/* The global :focus-visible ring is kg-600 — invisible on the kg-700
   masthead. Same ring, this palette's light end. */
.af-deliverto:focus-visible { outline: 2px solid var(--af-gold-200); outline-offset: 2px; }
@media (min-width: 768px) { .af-deliverto { display: flex; } }
.af-deliverto .af-pin { font-size: 20px; color: var(--af-gold-400); }
.af-deliverto-txt { display: flex; flex-direction: column; line-height: 1.15; }
@media (max-width: 1023.98px) {
  .af-deliverto-txt {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}
.af-deliverto-k { font-size: 9.5px; text-transform: uppercase; letter-spacing: .12em; color: rgba(255, 255, 255, .6); }
.af-deliverto-v { font-size: 12.5px; font-weight: 700; }
.af-deliverto-caret { font-size: 16px; color: rgba(255, 255, 255, .7); transition: transform .18s ease; }
.af-deliverto[aria-expanded="true"] .af-deliverto-caret { transform: rotate(180deg); }

/* The header is a stacking context at z-index 40, which is also the bottom
   bar's. Lifting it while the sheet is open is what keeps the sheet (and its
   scrim) above the bottom bar and the floating basket without moving the panel
   out of the header, where the desktop popup has to be anchored. Still under
   the auth modal (65). */
.af-header.af-dt-open { z-index: 55; }

.af-dt-scrim {
  position: fixed;
  inset: 0;
  z-index: 51;
  background: rgba(27, 43, 35, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s ease, visibility .2s ease;
}
.af-dt-scrim.open { opacity: 1; visibility: visible; }
@media (min-width: 1024px) { .af-dt-scrim { display: none; } }

/* visibility (not display) so the closed panel is out of the tab order and the
   accessibility tree, and the open/close still animates.

   VISIBILITY IS NOT TRANSITIONED ON THE WAY IN, and that is load-bearing.
   Under `transition: … visibility .16s`, the computed value is still `hidden`
   on the frame the .open class lands — and an element that is computed-hidden
   silently REFUSES focus(), so the panel opened with focus stranded on the
   button and the postcode field unreachable without a Tab. Measured in the
   smoke run. `visibility 0s` flips it in the same style recalc; the closing
   rule delays the flip by the length of the fade so the panel still animates
   out instead of vanishing. */
.af-dt-panel {
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: #fff;
  padding: 16px;
  color: var(--af-ink);
  text-align: left;
  box-shadow: var(--af-shadow-pop);
  opacity: 0;
  visibility: hidden;
  transition: opacity .16s ease, transform .22s ease, visibility 0s linear .22s;
}
.af-dt-panel.open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity .16s ease, transform .22s ease, visibility 0s;
}

@media (min-width: 1024px) {
  .af-dt-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;                     /* anchored to the control's right edge: the
                                     row's free space is always to its left */
    z-index: 55;
    width: 360px;
    transform: translateY(-6px);
  }
}
@media (max-width: 1023.98px) {
  .af-dt-panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 52;                  /* over the bottom bar (40) and the FAB (45) */
    max-height: 86vh;
    overflow-y: auto;
    border-radius: var(--af-radius-lg) var(--af-radius-lg) 0 0;
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    transform: translateY(101%);
  }
}

.af-dt-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.af-dt-title { font-size: 12.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; }
.af-dt-x { display: grid; place-items: center; height: 36px; width: 36px; flex: 0 0 auto; border-radius: var(--af-radius-sm); color: var(--af-mud); }
.af-dt-x:hover { background: var(--af-kg-50); }
.af-dt-x .ms { font-size: 20px; }
.af-dt-hint { margin: 6px 0 12px; font-size: 12px; line-height: 1.6; color: var(--af-mud); }

.af-dt-inputrow { display: flex; align-items: center; gap: 8px; }
.af-dt-inputrow .af-input { flex: 1 1 auto; min-width: 0; text-transform: uppercase; }
.af-dt-inputrow .af-input::placeholder { text-transform: none; }
/* `.af-btn` is a full-width block by default; in this row it is a trailing
   action, so it sizes to its label.

   SCOPED ON PURPOSE. A bare `.af-dt-go` ties with `.af-btn` on specificity, and
   `.af-btn { width: 100% }` is declared ~1100 lines BELOW this block, so the
   later rule won: the button took the whole row (326px) and squeezed the input
   to 26px — the panel shipped with a field nobody could type a postcode into.
   Two classes outrank one whatever the source order, so this cannot regress by
   someone moving a block. */
.af-dt-inputrow .af-dt-go { width: auto; flex: 0 0 auto; padding: 11px 14px; }
.af-dt-inputrow .af-dt-go[disabled] { opacity: .6; cursor: not-allowed; }

/* "Use my current location". Built from THIS bundle's own row idiom — the same
   10px corner, hairline border, kg-50 plate and kg-600 icon as .af-dt-addr and
   .af-drawer-deliver — so it reads as a second way into the panel rather than a
   second primary action. Deliberately NOT .af-btn: Check is the panel's one
   filled button, and a second one beside it would compete with it.

   No `--lm-*` token appears here. Those are Asharaf's design language; this
   file bridges them at the top for the React routes only, and reaching for one
   to style an AsianFresh control would repaint another shop's shape. */
.af-dt-locate {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  border-radius: var(--af-radius-sm);
  border: 1px solid var(--af-line);
  background: var(--af-kg-50);
  padding: 10px 12px;
  text-align: left;
  font-size: 12.5px;
  font-weight: 800;
  color: var(--af-kg-700);
}
.af-dt-locate:hover { border-color: var(--af-kg-200); color: var(--af-kg-800); }
.af-dt-locate .ms { font-size: 18px; color: var(--af-kg-600); flex: 0 0 auto; }
.af-dt-locate[disabled] { opacity: .6; cursor: not-allowed; }

.af-dt-suggest {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  max-height: 188px;
  overflow-y: auto;
  border-radius: var(--af-radius-sm);
  border: 1px solid var(--af-line);
}
.af-dt-opt {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--af-ink);
}
.af-dt-opt:hover, .af-dt-opt.is-active { background: var(--af-kg-50); color: var(--af-kg-700); }

.af-dt-result {
  margin-top: 12px;
  border-radius: var(--af-radius-sm);
  padding: 12px;
  font-size: 12.5px;
  line-height: 1.55;
}
.af-dt-result.is-yes { border: 1px solid var(--af-kg-100); background: var(--af-kg-50); color: var(--af-kg-800); }
.af-dt-result.is-no { border: 1px solid var(--af-gold-200); background: var(--af-gold-100); color: var(--af-gold-600); }
.af-dt-result.is-info { border: 1px solid var(--af-line-strong); background: var(--af-cream); color: var(--af-mud); }
.af-dt-result-h { display: flex; align-items: flex-start; gap: 8px; font-weight: 800; }
.af-dt-result-h .ms { font-size: 18px; flex: 0 0 auto; }
.af-dt-zone { display: block; margin-top: 3px; padding-left: 26px; font-weight: 600; }
.af-dt-chips { margin-top: 8px; padding-left: 26px; display: flex; flex-wrap: wrap; gap: 6px; }
/* .af-kicker's metrics on a white plate, so the pills read inside the tinted
   result box. */
.af-dt-chip {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  border: 1px solid var(--af-kg-100);
  background: #fff;
  padding: 4px 10px;
  font-size: 10.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--af-kg-700);
}

.af-dt-areas {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 10px;
  font-size: 12px;
  font-weight: 800;
  color: var(--af-kg-700);
  text-decoration: underline;
}
.af-dt-areas .ms { font-size: 16px; }

.af-dt-saved { margin-top: 14px; border-top: 1px solid var(--af-line); padding-top: 12px; }
.af-dt-sub { margin-bottom: 8px; font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-mud); }
.af-dt-addr {
  display: flex;
  width: 100%;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 6px;
  border-radius: var(--af-radius-sm);
  border: 1px solid var(--af-line);
  background: #fff;
  padding: 10px 12px;
  text-align: left;
}
.af-dt-addr:hover { border-color: var(--af-kg-200); background: var(--af-kg-50); }
.af-dt-addr .ms { font-size: 18px; color: var(--af-kg-600); flex: 0 0 auto; }
.af-dt-addr-l { display: block; font-size: 12.5px; font-weight: 800; color: var(--af-ink); }
.af-dt-addr-s { display: block; margin-top: 2px; font-size: 11.5px; font-weight: 600; color: var(--af-mud); }

/* Drawer row — the way in below md, where the masthead button is gone. */
.af-drawer-deliver {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  border-radius: var(--af-radius-sm);
  border: 1px solid var(--af-line);
  background: var(--af-kg-50);
  padding: 10px 12px;
  text-align: left;
}
.af-drawer-deliver:hover { border-color: var(--af-kg-200); }
.af-drawer-deliver .ms { font-size: 19px; color: var(--af-kg-600); }
.af-drawer-deliver-k { display: block; font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-mud); }
.af-drawer-deliver-v { display: block; font-size: 12.5px; font-weight: 800; color: var(--af-ink); }
.af-drawer-deliver .af-drawer-deliver-caret { margin-left: auto; font-size: 18px; color: var(--af-mud); }

.af-headactions { margin-left: auto; display: flex; align-items: center; gap: 4px; }
@media (min-width: 768px) { .af-headactions { gap: 8px; } }

.af-iconbtn {
  position: relative;
  display: none;
  place-items: center;
  height: 40px;
  width: 40px;
  border-radius: 12px;
  color: rgba(255, 255, 255, .95);
}
.af-iconbtn:hover { background: rgba(255, 255, 255, .1); }
.af-iconbtn .ms { font-size: 21px; }
@media (min-width: 768px) { .af-iconbtn { display: grid; } }

.af-iconbtn-badge {
  position: absolute;
  right: 2px;
  top: 2px;
  display: grid;
  place-items: center;
  height: 16px;
  min-width: 16px;
  border-radius: 999px;
  background: var(--af-gold-400);
  padding: 0 4px;
  font-size: 9px;
  font-weight: 800;
  color: #fff;
}

.af-acct {
  display: flex;
  align-items: center;
  gap: 8px;
  border-radius: 12px;
  padding: 6px 8px;
  color: #fff;
}
.af-acct:hover { background: rgba(255, 255, 255, .1); }
.af-acct-badge {
  display: grid;
  place-items: center;
  height: 32px;
  width: 32px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: var(--af-gold-400);
  font-size: 12px;
  font-weight: 800;
  color: #fff;
}
.af-acct-txt { display: none; flex-direction: column; align-items: flex-start; line-height: 1.15; }
@media (min-width: 768px) { .af-acct-txt { display: flex; } }
.af-acct-k { font-size: 9.5px; text-transform: uppercase; letter-spacing: .12em; color: rgba(255, 255, 255, .6); }
.af-acct-v { font-size: 12.5px; font-weight: 700; }

.af-cartbtn {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  border-radius: 12px;
  background: var(--af-gold-400);
  padding: 8px 10px;
  color: #fff;
}
.af-cartbtn:hover { background: var(--af-gold-600); color: #fff; }
@media (min-width: 768px) { .af-cartbtn { padding: 8px 14px; } }
.af-cartbtn .ms { font-size: 21px; }
.af-cart-txt { display: none; flex-direction: column; align-items: flex-start; line-height: 1.15; }
@media (min-width: 768px) { .af-cart-txt { display: flex; } }
.af-cart-k { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; opacity: .7; }
.af-cart-v { font-size: 12.5px; font-weight: 800; }
.af-cart-dot {
  position: absolute;
  right: -4px;
  top: -4px;
  display: grid;
  place-items: center;
  height: 18px;
  min-width: 18px;
  border-radius: 999px;
  background: var(--af-chilli);
  padding: 0 4px;
  font-size: 10px;
  font-weight: 800;
  color: #fff;
}
@media (min-width: 768px) { .af-cart-dot { display: none; } }

.af-search-mob { padding: 0 12px 10px; }
@media (min-width: 768px) { .af-search-mob { display: none; } }
.af-search-mob .af-search { padding: 8px 12px; }

.af-headrule { height: 3px; width: 100%; background: var(--af-chilli); }

/* The category tab bar — the ONE category navigator, at every width. It is the
   bottom strip of the sticky header, so it stays reachable while the grid
   scrolls (see the .af-header rule further down for how that is done). */
.af-catnav { border-bottom: 1px solid var(--af-line); background: rgba(255, 255, 255, .95); backdrop-filter: blur(8px); }
.af-catnav-inner {
  position: relative;              /* offsetLeft reference for revealTab() */
  display: flex;
  width: 100%;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  scroll-padding: 0 12px;
  overscroll-behavior-x: contain;  /* swiping past the last tab must not drag the page */
  -webkit-overflow-scrolling: touch;
  padding: 0 8px;
}
@media (min-width: 768px) { .af-catnav-inner { padding: 0 24px; } }

.af-catnav-link {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  min-height: 40px;                /* tap target: on mobile these tabs are the switcher */
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  padding: 10px 12px;
  font-size: 12.5px;
  font-weight: 700;
  color: rgba(27, 43, 35, .8);
  transition: color .15s ease;
}
.af-catnav-link:hover { color: var(--af-kg-600); }
.af-catnav-link.active { border-bottom-color: var(--af-kg-600); color: var(--af-kg-700); }

/* Pinned-state affordance: the bar lifts off the grid it is floating over.
   script.js toggles the class from the bar's own position. */
.af-catnav.af-catnav-pinned {
  border-bottom-color: var(--af-line-strong);
  box-shadow: 0 10px 18px -16px rgba(8, 61, 29, .75);
}

/* ── hero banner ────────────────────────────────────────────────────────── */

.af-hero {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--af-radius);
  background: var(--af-kg-900);
}

@media (min-width: 640px) { .af-hero { aspect-ratio: 2.4 / 1; } }
@media (min-width: 1024px) { .af-hero { aspect-ratio: 1450 / 420; } }

.af-hero-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity .5s ease;
}
.af-hero-slide.active { opacity: 1; visibility: visible; }

.af-hero-media { position: absolute; inset: 0; }
.af-hero-media img { width: 100%; height: 100%; object-fit: cover; }

/* Scrim so the overlaid copy stays legible over any photo. The prototype
   omitted the text entirely; we render it, so the scrim is mandatory. */
.af-hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(4, 20, 12, .82) 0%, rgba(4, 20, 12, .55) 45%, rgba(4, 20, 12, .12) 100%);
}

.af-hero-copy {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  padding: 16px 56px 26px 18px;
  max-width: 680px;
  color: #fff;
  text-align: left;
}

@media (min-width: 768px) { .af-hero-copy { padding: 20px 72px 30px 40px; gap: 9px; } }

.af-hero-eyebrow {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  background: rgba(255, 255, 255, .18);
  padding: 4px 10px;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: #fff;
}

.af-hero-title {
  font-size: clamp(19px, 4.2vw, 40px);
  color: #fff;
  text-shadow: 0 2px 18px rgba(0, 0, 0, .35);
}

.af-hero-sub {
  font-size: clamp(11.5px, 1.5vw, 15px);
  font-weight: 500;
  line-height: 1.5;
  color: rgba(255, 255, 255, .88);
  max-width: 460px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.af-hero-cta {
  align-self: flex-start;
  margin-top: 4px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 12px;
  background: var(--af-gold-400);
  padding: 8px 16px;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #fff;
}
.af-hero-cta:hover { background: #fff; color: var(--af-gold-600); }

.af-hero-fine {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: rgba(255, 255, 255, .65);
}

.af-hero-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  height: 36px;
  width: 36px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .85);
  color: var(--af-kg-900);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
  z-index: 2;
}
.af-hero-arrow:hover { background: #fff; }
.af-hero-arrow .ms { font-size: 20px; }
.af-hero-prev { left: 12px; }
.af-hero-next { right: 12px; }

.af-hero-dots {
  position: absolute;
  bottom: 12px;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  z-index: 2;
}
.af-hero-dot {
  height: 6px;
  width: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .55);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
  transition: width .25s ease, background .25s ease;
}
.af-hero-dot.active { width: 24px; background: #fff; }

/* ── strip banners ──────────────────────────────────────────────────────── */

.af-strips { margin-top: 12px; display: grid; gap: 12px; }
@media (min-width: 640px) { .af-strips { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.af-strip {
  position: relative;
  display: block;
  aspect-ratio: 2.13 / 1;
  overflow: hidden;
  border-radius: var(--af-radius);
}
.af-strip img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.af-strip-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(4, 20, 12, .78) 0%, rgba(4, 20, 12, .35) 70%, rgba(4, 20, 12, .05) 100%);
}
.af-strip-copy {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 14px 16px;
  color: #fff;
  text-align: left;
}
.af-strip-badge {
  align-self: flex-start;
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--af-gold-200);
}
.af-strip-title { font-size: clamp(13px, 2vw, 17px); color: #fff; }
.af-strip-fine { font-size: 10.5px; font-weight: 600; color: rgba(255, 255, 255, .72); }

/* Gradient themes — value names match the CMS dropdown 1:1. */
.af-theme-backwater { background: var(--af-grad-backwater); }
.af-theme-harbour { background: var(--af-grad-harbour); }
.af-theme-kasavu { background: var(--af-grad-kasavu); }
.af-theme-chilli { background: var(--af-grad-chilli); }
.af-theme-midnight { background: var(--af-grad-midnight); }

/* ── category tiles ─────────────────────────────────────────────────────── */

.af-cattiles {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  margin: 0 -4px;
  padding: 0 4px;
}

@media (min-width: 768px) {
  .af-cattiles {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 8px;
    margin: 0;
    padding: 0;
  }
}

@media (min-width: 1024px) {
  .af-cattiles { grid-template-columns: repeat(12, minmax(0, 1fr)); }
}

.af-cattile {
  display: flex;
  width: 76px;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  border-radius: 12px;
  padding: 6px;
  color: var(--af-ink);
}
.af-cattile:hover { background: var(--af-kg-50); }
@media (min-width: 768px) { .af-cattile { width: auto; } }

.af-cattile-ico {
  display: grid;
  place-items: center;
  height: 56px;
  width: 56px;
  border-radius: 999px;
  background: var(--af-kg-50);
  box-shadow: inset 0 0 0 1px var(--af-kg-100);
  transition: background .15s ease, box-shadow .15s ease;
}
.af-cattile-ico .ms { font-size: 26px; color: var(--af-kg-700); transition: color .15s ease; }
.af-cattile:hover .af-cattile-ico { background: var(--af-kg-600); box-shadow: inset 0 0 0 1px var(--af-kg-600); }
.af-cattile:hover .af-cattile-ico .ms { color: #fff; }
@media (min-width: 768px) { .af-cattile-ico { height: 64px; width: 64px; } }

.af-cattile-label {
  text-align: center;
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1.2;
  color: rgba(27, 43, 35, .85);
}

/* ── product card ───────────────────────────────────────────────────────── */

.af-rail {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  margin: 0 -4px;
  padding: 0 4px 4px;
}

.af-rail-item { width: 152px; flex: 0 0 auto; }
@media (min-width: 640px) { .af-rail-item { width: 190px; } }

.af-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
@media (min-width: 640px) { .af-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .af-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* THE CARD, AND THE ONE OTHER SURFACE THAT DRAWS IT (#2131).
   ---------------------------------------------------------------------------
   Every rule from here to `.af-qty-n` carries a second selector: the class the
   shared React `<WishlistGrid variant="template">` emits for the same element.
   The account area's Wishlist tab draws THIS card — the prototype's wishlist
   tab is a verbatim copy of its shop-grid card (index.html:1097-1142 repeats
   :636) — and the two are rendered by different engines from different
   payloads, side by side in one shop.

   They are GROUPED rather than restated because restating them is what the
   client reported: the tile was written from the card as it stood, the card
   then gained an offer flash, a tag chip, a brand line, a rating row, a pack
   size and a was-price, and the tile kept the shape it was born with. One
   declaration block per element means the next change to this card cannot
   leave the saved tile behind — there is nothing to forget to copy.

   The `.tj-*` classes are the PLATFORM's, not this bundle's, and they appear
   in exactly one stylesheet: this one, served per slug. Every other tenant
   keeps `globals.css`'s neutral card.

   Only the elements that genuinely differ live apart, in the Wishlist section
   near the end of this file: the grid's own column counts, the remove control
   (a permanently-lit heart, not the catalogue's hover-in one), the media's
   link wrapper, and the glyphs — which are generated content here because a
   ligature emitted by a shared component paints as the raw word "star" for any
   tenant whose chrome loads no icon font. */
.af-card,
.tj-wish-tile {
  position: relative;
  display: flex;
  height: 100%;
  flex-direction: column;
  border-radius: var(--af-radius);
  border: 1px solid rgba(0, 0, 0, .06);
  background: #fff;
  padding: 8px;
  box-shadow: var(--af-shadow-card);
  transition: transform .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.af-card:hover,
.tj-wish-tile:hover {
  transform: translateY(-2px);
  border-color: rgba(27, 148, 72, .3);
  box-shadow: var(--af-shadow-lift);
}
.af-card.is-out,
.tj-wish-tile.is-off { opacity: .55; }

.af-card-media,
.tj-wish-media {
  position: relative;
  aspect-ratio: 1 / 1;
  width: 100%;
  overflow: hidden;
  border-radius: 12px;
  background: var(--af-cream);
}
.af-card-media img,
.tj-wish-media img { width: 100%; height: 100%; object-fit: cover; }

/* The shop's EMPTY-MEDIA treatment: what this design draws where a photo would
   be when the operator uploaded none. Shared with the saved tile (#2131) and
   with the order-history card's thumbnail strip (#2134) — the API returns
   `imageUrl: null` rather than inventing a placeholder, so every surface that
   shows a product needs this, and it must be the same square everywhere. Its
   34px initial is sized for a card's 1:1 media; the 56px strip tile restates
   only the size, down in the My orders block. */
.af-card-ph,
.tj-wish-ph,
.tj-acct-thumb-ph {
  display: grid;
  place-items: center;
  height: 100%;
  width: 100%;
  font-family: var(--af-display);
  font-size: 34px;
  font-weight: 800;
  color: var(--af-kg-200);
  background: var(--af-kg-50);
}

.af-card-tag,
.tj-wish-tag {
  pointer-events: none;
  position: absolute;
  right: 6px;
  top: 6px;
  border-radius: 6px;
  background: rgba(15, 92, 44, .9);
  padding: 3px 6px;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  line-height: 1;
  letter-spacing: .08em;
  color: var(--af-gold-200);
}

/* "N% off" — top-left, and only ever printed against a real was-price. */
.af-card-off,
.tj-wish-flash {
  pointer-events: none;
  position: absolute;
  left: 6px;
  top: 6px;
  border-radius: 6px;
  background: var(--af-chilli);
  padding: 3px 6px;
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
  color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
}

.af-wish {
  position: absolute;
  right: 12px;
  top: calc(50% - 8px);
  display: grid;
  place-items: center;
  height: 32px;
  width: 32px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .95);
  color: var(--af-mud);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .16);
  z-index: 2;
  opacity: 0;
  transition: opacity .15s ease, color .15s ease;
}
.af-card:hover .af-wish, .af-wish:focus-visible { opacity: 1; }
.af-wish:hover { color: var(--af-chilli); }
.af-wish .ms { font-size: 17px; font-variation-settings: 'FILL' 0, 'wght' 500; }
.af-wish.is-on { opacity: 1; color: var(--af-chilli); }
.af-wish.is-on .ms { font-variation-settings: 'FILL' 1, 'wght' 500; }

/* Touch devices have no hover — always show the heart. */
@media (hover: none) { .af-wish { opacity: 1; } }

.af-card-body,
.tj-wish-body { display: flex; flex: 1; flex-direction: column; padding: 10px 4px 4px; }

.af-card-brand,
.tj-wish-brand {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: rgba(107, 98, 87, .7);
  min-height: 1.2em;
}

.af-card-name,
.tj-wish-name {
  margin-top: 4px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.6em;
  text-align: left;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--af-ink);
}
a.af-card-name:hover,
a.tj-wish-name:hover { color: var(--af-kg-600); }

/* Fixed height whatever it carries: the template's meta row is what keeps the
   bottom halves of the cards on a line across the grid. The rating chip and its
   count only render when the product actually has both a score and a number of
   ratings; with either missing the row holds its height and shows the pack size
   alone. Nothing here is ever defaulted or computed. */
.af-card-meta,
.tj-wish-meta {
  margin-top: 6px;
  display: flex;
  height: 18px;
  align-items: center;
  gap: 6px;
  overflow: hidden;
  white-space: nowrap;
}

.af-card-rating,
.tj-wish-rating {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 2px;
  border-radius: 4px;
  background: var(--af-kg-600);
  padding: 2px 6px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.2;
  color: #fff;
}
/* The star. Markup on the CMS card, generated content on the shared one. */
.af-card-rating .ms,
.tj-wish-rating::before { font-size: 10px; }
.af-card-rc,
.tj-wish-rc { overflow: hidden; text-overflow: ellipsis; font-size: 10px; color: rgba(107, 98, 87, .7); }

.af-card-unit,
.tj-wish-unit { margin-left: auto; flex: 0 0 auto; font-size: 11px; font-weight: 500; color: var(--af-mud); }

.af-card-price-row,
.tj-wish-price-row { margin-top: 8px; display: flex; align-items: flex-end; gap: 6px; }
.af-card-price,
.tj-wish-price { font-size: 16px; font-weight: 800; line-height: 1; letter-spacing: -.01em; color: var(--af-ink); }
.af-card-mrp,
.tj-wish-mrp { font-size: 11px; font-weight: 500; line-height: 1; color: rgba(107, 98, 87, .6); text-decoration: line-through; }
.af-card-note,
.tj-wish-note { margin-top: 4px; min-height: 1.35em; font-size: 10.5px; font-weight: 600; color: var(--af-kg-600); }

.af-card-foot,
.tj-wish-foot { margin-top: auto; padding-top: 10px; }

.af-add,
.tj-wish-add {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: var(--af-radius-sm);
  border: 1px solid var(--af-kg-600);
  background: var(--af-kg-50);
  padding: 8px;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--af-kg-700);
  transition: background .15s ease, color .15s ease;
}
.af-add:hover,
.tj-wish-add:hover:not([disabled]) { background: var(--af-kg-600); color: #fff; }
/* The basket glyph. Markup on the CMS card, generated content on the shared one. */
.af-add .ms,
.tj-wish-add::before { font-size: 15px; }
.af-add[disabled],
.tj-wish-add[disabled] { opacity: .5; cursor: not-allowed; }

.af-soldout,
.tj-wish-off {
  display: block;
  width: 100%;
  border-radius: var(--af-radius-sm);
  background: var(--af-cream);
  padding: 8px;
  text-align: center;
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--af-mud);
}

/* Quantity stepper — swapped in by script.js on the CMS card, by the cart
   store's own subscription on the saved tile. Same control either way. */
.af-qty,
.tj-wish-qty {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-radius: var(--af-radius-sm);
  background: var(--af-kg-600);
  padding: 4px;
  color: #fff;
}
.af-qty button,
.tj-wish-qty-btn { display: grid; place-items: center; height: 28px; width: 28px; border-radius: 6px; color: #fff; }
.af-qty button:hover,
.tj-wish-qty-btn:hover:not([disabled]) { background: rgba(255, 255, 255, .15); }
.af-qty .ms,
.tj-wish-qty-btn::before { font-size: 17px; }
.af-qty-n,
.tj-wish-qty-n { font-size: 13px; font-weight: 800; }

/* ── deals band ─────────────────────────────────────────────────────────── */

.af-deals {
  margin-top: 20px;
  overflow: hidden;
  border-radius: var(--af-radius);
  background: var(--af-kg-800);
  padding: 12px;
}
@media (min-width: 768px) { .af-deals { padding: 20px; } }

.af-deals-head { margin-bottom: 12px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; }
.af-deals-title { font-size: 18px; color: #fff; }
@media (min-width: 768px) { .af-deals-title { font-size: 22px; } }

.af-countdown {
  display: flex;
  align-items: center;
  gap: 6px;
  border-radius: var(--af-radius-sm);
  background: var(--af-chilli);
  padding: 4px 8px;
  font-size: 11px;
  font-weight: 800;
  color: #fff;
}
.af-countdown .ms { font-size: 13px; }

.af-deals-viewall { margin-left: auto; font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-gold-200); }

/* ── today's catch + counter ────────────────────────────────────────────── */

.af-catchrow { margin-top: 20px; display: grid; gap: 12px; }
@media (min-width: 1024px) { .af-catchrow { grid-template-columns: 280px 1fr; } }

.af-catch {
  position: relative;
  overflow: hidden;
  border-radius: var(--af-radius);
  padding: 20px;
  background: var(--af-grad-harbour);
}
.af-catch-kicker {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .15);
  padding: 4px 8px;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: #fff;
}
.af-catch-title { margin-top: 12px; font-size: 24px; line-height: 1.15; color: #fff; }
.af-catch-body { margin-top: 8px; font-size: 12px; font-weight: 500; line-height: 1.6; color: rgba(255, 255, 255, .8); }
.af-catch-ticks { margin-top: 16px; display: grid; gap: 6px; font-size: 11.5px; font-weight: 600; color: rgba(255, 255, 255, .9); }
.af-catch-tick { display: flex; align-items: center; gap: 6px; }
.af-catch-tick .ms { font-size: 15px; color: var(--af-gold-200); }
.af-catch-cta {
  margin-top: 20px;
  display: block;
  width: 100%;
  border-radius: 12px;
  background: var(--af-gold-400);
  padding: 10px;
  text-align: center;
  font-size: 12.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #fff;
}
.af-catch-cta:hover { background: #fff; color: var(--af-gold-600); }

.af-counter-tabs { display: flex; gap: 4px; border-radius: var(--af-radius-sm); background: var(--af-cream); padding: 4px; }
.af-counter-tab {
  border-radius: 6px;
  padding: 4px 10px;
  font-size: 11.5px;
  font-weight: 700;
  color: rgba(27, 43, 35, .7);
}
.af-counter-tab:hover, .af-counter-tab.active { background: #fff; color: var(--af-kg-700); }

/* The "All fish" / "All meat" pair under the counter's panels. Was an inline
   style on the <p>; it is a rule now so the panel is styled from this sheet
   like everything else, and so a link can drop out without leaving a gap. */
.af-counter-links { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 16px; }

/* ── why-us + testimonials ──────────────────────────────────────────────── */

.af-why { margin-top: 20px; display: grid; gap: 12px; }
@media (min-width: 768px) { .af-why { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.af-why-item { border-radius: var(--af-radius); border: 1px solid var(--af-line); background: #fff; padding: 16px; }
.af-why-ico { display: grid; place-items: center; height: 40px; width: 40px; border-radius: 12px; background: var(--af-gold-100); }
.af-why-ico .ms { font-size: 21px; color: var(--af-gold-600); }
.af-why-title { margin-top: 12px; font-size: 13.5px; font-weight: 800; font-family: var(--af-sans); }
.af-why-body { margin-top: 4px; font-size: 11.5px; font-weight: 500; line-height: 1.6; color: var(--af-mud); }

.af-quotes { margin-top: 12px; display: flex; gap: 12px; overflow-x: auto; padding-bottom: 4px; }
.af-quote { width: 270px; flex: 0 0 auto; border-radius: 12px; background: var(--af-cream); padding: 16px; }
@media (min-width: 768px) { .af-quote { width: 330px; } }
.af-quote .af-quote-mark { font-size: 22px; color: var(--af-gold-400); }
.af-quote-text { margin-top: 4px; font-size: 12.5px; font-weight: 500; line-height: 1.6; color: rgba(27, 43, 35, .85); }
.af-quote-by { margin-top: 12px; display: flex; align-items: center; gap: 8px; }
.af-quote-in {
  display: grid;
  place-items: center;
  height: 32px;
  width: 32px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: var(--af-kg-600);
  font-size: 11px;
  font-weight: 800;
  color: #fff;
}
.af-quote-name { font-size: 12px; font-weight: 700; }
.af-quote-city { font-size: 10.5px; color: var(--af-mud); }

/* ── footer ─────────────────────────────────────────────────────────────── */

.af-footer {
  border-top: 1px solid var(--af-line);
  background: var(--af-kg-900);
  padding: 32px 16px 96px;
  color: rgba(255, 255, 255, .85);
}
@media (min-width: 768px) { .af-footer { padding: 32px 24px; } }

.af-footer-grid { display: grid; width: 100%; gap: 24px; }
@media (min-width: 768px) { .af-footer-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }

.af-footer-brand { font-family: var(--af-display); font-size: 22px; font-weight: 800; color: #fff; }
.af-footer-brand em { font-style: normal; color: var(--af-gold-400); }
.af-footer-blurb { margin-top: 8px; max-width: 300px; font-size: 12px; font-weight: 500; line-height: 1.6; color: rgba(255, 255, 255, .7); }

.af-footer-contacts { margin-top: 12px; display: flex; gap: 8px; }
.af-footer-contact {
  display: grid;
  place-items: center;
  height: 36px;
  width: 36px;
  border-radius: var(--af-radius-sm);
  background: rgba(255, 255, 255, .1);
  color: #fff;
}
.af-footer-contact:hover { background: rgba(255, 255, 255, .2); color: #fff; }
.af-footer-contact .ms { font-size: 18px; }

.af-footer-h { font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-gold-200); }
.af-footer-links { margin-top: 8px; display: grid; gap: 6px; font-size: 12px; font-weight: 500; }
.af-footer-links a { color: rgba(255, 255, 255, .75); }
.af-footer-links a:hover { color: #fff; }

.af-news { margin-top: 8px; font-size: 12px; font-weight: 500; color: rgba(255, 255, 255, .7); }
.af-news-form { margin-top: 8px; display: flex; gap: 8px; }
.af-news-form input {
  width: 100%;
  border: 0;
  border-radius: var(--af-radius-sm);
  background: rgba(255, 255, 255, .1);
  padding: 10px 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: #fff;
  outline: none;
}
.af-news-form input::placeholder { color: rgba(255, 255, 255, .4); }
.af-news-btn {
  flex: 0 0 auto;
  border-radius: var(--af-radius-sm);
  background: var(--af-gold-400);
  padding: 0 12px;
  font-size: 11.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #fff;
}
.af-news-btn:hover { background: var(--af-gold-600); }
.af-news-btn[disabled] { opacity: .6; cursor: progress; }
.af-news-msg { margin-top: 8px; font-size: 11.5px; font-weight: 600; color: var(--af-gold-200); }

.af-paybadges { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px; font-size: 10px; font-weight: 700; color: rgba(255, 255, 255, .6); }
.af-paybadge { border-radius: 4px; border: 1px solid rgba(255, 255, 255, .15); padding: 4px 8px; }

.af-footer-legal {
  margin-top: 24px;
  display: flex;
  width: 100%;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 16px;
  border-top: 1px solid rgba(255, 255, 255, .1);
  padding-top: 16px;
  font-size: 11px;
  font-weight: 500;
  color: rgba(255, 255, 255, .5);
}
.af-footer-legal a { color: rgba(255, 255, 255, .5); }
.af-footer-legal a:hover { color: #fff; }

/* ── mobile bottom nav ──────────────────────────────────────────────────── */

.af-botnav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  align-items: stretch;
  border-top: 1px solid var(--af-line-strong);
  background: rgba(255, 255, 255, .97);
  backdrop-filter: blur(8px);
  padding-bottom: env(safe-area-inset-bottom, 0);
}
@media (min-width: 768px) { .af-botnav { display: none; } }

.af-botnav-item {
  position: relative;
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 0;
  font-size: 9.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--af-mud);
}
.af-botnav-item.active { color: var(--af-kg-700); }
.af-botnav-item .ms { font-size: 21px; }
.af-botnav-badge {
  position: absolute;
  right: 16px;
  top: 4px;
  border-radius: 999px;
  background: var(--af-chilli);
  padding: 0 4px;
  font-size: 9px;
  font-weight: 800;
  color: #fff;
}

/* ── mobile drawer ──────────────────────────────────────────────────────── */

.af-drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 49;
  background: rgba(27, 43, 35, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s ease, visibility .2s ease;
}
.af-drawer-scrim.open { opacity: 1; visibility: visible; }
@media (min-width: 768px) { .af-drawer-scrim { display: none; } }

.af-drawer {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: 50;
  width: 290px;
  max-width: 86vw;
  overflow-y: auto;
  background: #fff;
  transform: translateX(-100%);
  transition: transform .22s ease;
}
.af-drawer.open { transform: translateX(0); }
@media (min-width: 768px) { .af-drawer { display: none; } }

.af-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: linear-gradient(120deg, #0F5C2C, #1B9448);
}
.af-drawer-brand { font-family: var(--af-display); font-size: 19px; font-weight: 800; color: #fff; }
.af-drawer-brand em { font-style: normal; color: var(--af-gold-400); }
.af-drawer-close { display: grid; place-items: center; height: 32px; width: 32px; border-radius: var(--af-radius-sm); color: #fff; }
.af-drawer-close:hover { background: rgba(255, 255, 255, .15); }

.af-drawer-body { padding: 12px; }
.af-drawer-acct {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 10px;
  border-radius: 12px;
  background: var(--af-cream);
  padding: 12px;
  text-align: left;
  color: var(--af-ink);
}
.af-drawer-label { margin-top: 12px; padding: 0 8px; font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-mud); }
.af-drawer-link {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 10px;
  border-radius: var(--af-radius-sm);
  padding: 10px 8px;
  text-align: left;
  font-size: 12.5px;
  font-weight: 700;
  color: rgba(27, 43, 35, .85);
}
.af-drawer-link:hover { background: var(--af-kg-50); color: var(--af-kg-700); }
.af-drawer-link .ms { font-size: 19px; color: var(--af-kg-600); }

/* There is no cart drawer in this theme. The basket screen is the React /cart
   route (the template's `isCart` page), and every basket control in the chrome
   links to it — so the only cart styling here is the header button (.af-cartbtn
   and .af-cart-k/-v/-dot, above), the bottom-bar badge and the floating basket.
   Do not reintroduce an .af-cart-drawer block: the importer never prunes, so an
   orphaned rule would outlive whatever markup justified it. */

.af-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  border-radius: 12px;
  padding: 12px;
  font-size: 12.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  text-align: center;
}
.af-btn-primary { background: var(--af-gold-400); color: #fff; }
.af-btn-primary:hover { background: var(--af-gold-600); color: #fff; }
.af-btn-ghost { background: #fff; color: var(--af-kg-700); box-shadow: inset 0 0 0 1px var(--af-kg-600); }
.af-btn-ghost:hover { background: var(--af-kg-50); color: var(--af-kg-700); }

/* ── modal (sign-in) ────────────────────────────────────────────────────── */

.af-modal {
  position: fixed;
  inset: 0;
  z-index: 65;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(27, 43, 35, .55);
  backdrop-filter: blur(4px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .18s ease, visibility .18s ease;
}
.af-modal.open { opacity: 1; visibility: visible; }

.af-modal-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  max-height: 90vh;
  overflow-y: auto;
  border-radius: var(--af-radius);
  background: #fff;
  padding: 26px;
  box-shadow: var(--af-shadow-pop);
}

.af-modal-close {
  position: absolute;
  top: 12px;
  right: 14px;
  display: grid;
  place-items: center;
  height: 32px;
  width: 32px;
  border-radius: var(--af-radius-sm);
  color: var(--af-mud);
}
.af-modal-close:hover { background: var(--af-cream); }

/* ── toast ──────────────────────────────────────────────────────────────── */

.af-toast {
  position: fixed;
  left: 50%;
  bottom: 88px;
  z-index: 70;
  transform: translate(-50%, 12px);
  border-radius: 999px;
  background: var(--af-ink);
  padding: 10px 18px;
  font-size: 12.5px;
  font-weight: 700;
  color: #fff;
  box-shadow: var(--af-shadow-pop);
  opacity: 0;
  visibility: hidden;
  transition: opacity .18s ease, transform .18s ease, visibility .18s ease;
}
.af-toast.open { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
@media (min-width: 768px) { .af-toast { bottom: 24px; } }

/* ── page (content) surfaces ────────────────────────────────────────────── */

.af-page { max-width: 980px; margin: 0 auto; }
.af-page-wide { max-width: 1240px; margin: 0 auto; }

.af-crumbs { display: flex; align-items: center; gap: 6px; padding: 8px 0; font-size: 11.5px; font-weight: 600; color: var(--af-mud); }
.af-crumbs .ms { font-size: 14px; }
.af-crumbs a:hover { color: var(--af-kg-600); }

.af-page-head { padding: 8px 0 20px; }
.af-kicker {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  background: var(--af-kg-50);
  padding: 5px 12px;
  font-size: 10.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--af-kg-700);
}
.af-page-title { margin-top: 12px; font-size: clamp(26px, 4vw, 40px); }
.af-page-intro { margin-top: 10px; max-width: 640px; font-size: 14.5px; line-height: 1.7; color: var(--af-mud); }

.af-prose { font-size: 14px; line-height: 1.75; color: var(--af-ink); }
.af-prose p { margin: 0 0 14px; }
.af-prose h2 { margin: 22px 0 10px; font-size: 20px; }
.af-prose h3 { margin: 18px 0 8px; font-size: 16px; }
.af-prose ul, .af-prose ol { margin: 0 0 14px; padding-left: 20px; }
.af-prose li { margin-bottom: 6px; }
.af-prose a { text-decoration: underline; }
.af-prose strong { font-weight: 700; }

.af-infogrid { display: grid; gap: 12px; }
@media (min-width: 768px) { .af-infogrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.af-updated { margin-top: 20px; font-size: 11.5px; font-weight: 600; color: var(--af-mud); }

/* FAQ — <details> keeps it keyboard-accessible with zero JS. */
.af-faq { border-radius: var(--af-radius); border: 1px solid var(--af-line); background: #fff; margin-bottom: 8px; }
.af-faq > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  cursor: pointer;
  padding: 14px 16px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--af-ink);
  list-style: none;
}
.af-faq > summary::-webkit-details-marker { display: none; }
.af-faq > summary::after {
  content: 'expand_more';
  font-family: 'Material Symbols Rounded';
  font-size: 20px;
  color: var(--af-kg-600);
  transition: transform .18s ease;
}
.af-faq[open] > summary::after { transform: rotate(180deg); }
.af-faq-body { padding: 0 16px 16px; font-size: 13px; line-height: 1.7; color: var(--af-mud); }

.af-linkcard {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: #fff;
  padding: 16px;
  color: var(--af-ink);
}
.af-linkcard:hover { border-color: var(--af-kg-200); box-shadow: var(--af-shadow-lift); color: var(--af-ink); }
.af-linkcard-ico { display: grid; place-items: center; height: 40px; width: 40px; flex: 0 0 auto; border-radius: 12px; background: var(--af-kg-50); }
.af-linkcard-ico .ms { font-size: 20px; color: var(--af-kg-700); }
.af-linkcard-title { font-size: 13.5px; font-weight: 800; }
.af-linkcard-body { margin-top: 3px; font-size: 12px; line-height: 1.6; color: var(--af-mud); }

/* ── forms (enquiry) ────────────────────────────────────────────────────── */

.af-form-grid { display: grid; gap: 14px; }
@media (min-width: 640px) { .af-form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.af-field-wide { grid-column: 1 / -1; }

.af-label { display: block; margin-bottom: 6px; font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-mud); }

.af-input {
  width: 100%;
  border-radius: var(--af-radius-sm);
  border: 1px solid var(--af-line-strong);
  background: #fff;
  padding: 11px 12px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--af-ink);
  outline: none;
}
.af-input:focus { border-color: var(--af-kg-600); box-shadow: 0 0 0 3px var(--af-kg-50); }
.af-input::placeholder { color: rgba(107, 98, 87, .55); }
.af-input[disabled] { background: var(--af-cream); cursor: not-allowed; }
textarea.af-input { resize: vertical; min-height: 120px; }

.af-error { margin-top: 12px; font-size: 13px; font-weight: 600; color: var(--af-chilli); }

.af-success { border-radius: var(--af-radius); border: 1px solid var(--af-kg-100); background: var(--af-kg-50); padding: 40px 24px; text-align: center; }
.af-success-ico {
  display: grid;
  place-items: center;
  height: 52px;
  width: 52px;
  margin: 0 auto 12px;
  border-radius: 999px;
  background: var(--af-kg-600);
  color: #fff;
}
.af-success-ico .ms { font-size: 26px; }

.af-contactcard { border-radius: var(--af-radius); border: 1px solid var(--af-line); background: #fff; padding: 20px; }
.af-contactcard + .af-contactcard { margin-top: 12px; }
.af-contact-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; font-size: 13.5px; }
.af-contact-row .ms { font-size: 19px; color: var(--af-kg-600); }
.af-hours-line { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; border-bottom: 1px solid var(--af-line); font-size: 13px; color: var(--af-mud); }
.af-hours-line:last-child { border-bottom: 0; }

/* ── menu / listing page ────────────────────────────────────────────────── */

.af-menu-head {
  border-radius: var(--af-radius);
  background: var(--af-grad-backwater);
  padding: 22px 20px;
  color: #fff;
}
.af-menu-title { font-size: clamp(24px, 4vw, 36px); color: #fff; }
.af-menu-intro { margin-top: 8px; max-width: 620px; font-size: 13.5px; line-height: 1.65; color: rgba(255, 255, 255, .82); }

.af-menu-search { margin-top: 14px; max-width: 460px; }
.af-menu-search .af-search { background: rgba(255, 255, 255, .96); }

/* The in-page pill rail that used to sit here (.af-catbar / .af-chip) is gone:
   it was a second copy of the header's category tabs, stacked directly under
   them, and the client flagged the duplication on the screenshots. The tab bar
   in the chrome is the only navigator now, and it works on mobile too. */

.af-cat-section { padding-top: 22px; scroll-margin-top: 190px; }
.af-cat-head { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.af-cat-title { font-size: clamp(19px, 3vw, 26px); }
.af-cat-rule { flex: 1; height: 1px; background: var(--af-line-strong); }
.af-cat-count { font-size: 11px; font-weight: 700; color: var(--af-mud); white-space: nowrap; }
.af-cat-desc { margin-bottom: 12px; font-size: 12.5px; color: var(--af-mud); }

.af-subcat { margin-top: 18px; }
.af-subcat-title { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; font-size: 15px; color: var(--af-kg-700); }

.af-empty { border-radius: var(--af-radius); border: 1px dashed var(--af-line-strong); background: #fff; padding: 48px 24px; text-align: center; }
.af-empty-title { font-size: 19px; }
.af-empty-body { margin-top: 8px; font-size: 13.5px; color: var(--af-mud); }

/* Search "no results" state is toggled by script.js on the listing page. */
.af-noresults { display: none; padding: 40px 12px; text-align: center; font-size: 14px; color: var(--af-mud); }
.af-noresults.show { display: block; }

/* ── pinned category tabs + floating basket ─────────────────────────────────
   Self-contained section: the sticky behaviour of the header (whose bottom
   strip is the category tab bar) and the floating basket, plus the two
   brand-logo sizers at the end.

   .af-shell OVERRIDE — READ BEFORE "TIDYING":
   `overflow-x: hidden` makes an element a scroll container in BOTH axes (the
   spec computes the other axis to `auto`), so every `position: sticky` inside
   .af-shell has been inert since this bundle was written — measured in
   Chromium, .af-header sat at top:-1400px after a 1400px scroll, and the
   catalogue's own category rail scrolled clean off with it. `overflow-x: clip`
   clips identically WITHOUT establishing a scroll container, so sticky works
   again. The horizontal-overflow guard is unchanged: body's own
   overflow-x:hidden still propagates to the viewport.
   ------------------------------------------------------------------------- */

.af-shell { overflow-x: clip; }

/* THE MASTHEAD PINS BY ITS BOTTOM STRIP, NOT WHOLE.
   The template pins the entire header (FreshDays index.html line 28) so its
   category tabs are always in reach. Doing that literally parks ~155px of
   chrome — search row, basket, delivery pill — at the top of a 390px screen.
   So the header pins at a NEGATIVE offset instead: it slides up by exactly its
   own height minus the strip we want to keep (the chilli rule + the tab bar),
   which leaves the tabs, and nothing else, under the browser chrome. Same
   reachable navigator, none of the cost.

   --af-header-top and --af-catnav-h are measured by script.js on load, resize
   and orientation change (and re-measured when React rebuilds the chrome). The
   0px fallback simply pins the whole header until that first frame lands —
   never a state where something is hidden. */
.af-header {
  position: sticky;
  top: var(--af-header-top, 0px);
}

/* Anchor targets clear the pinned tab bar instead of hiding under it — this is
   what the tabs and the header/drawer's /menu#cat-… links land on. */
[data-catalogue] .af-cat-section {
  scroll-margin-top: calc(var(--af-catnav-h, 44px) + 12px);
}

/* ── floating basket ────────────────────────────────────────────────────── */

.af-fab {
  position: fixed;
  right: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  z-index: 45;                     /* over .af-botnav (40), under every scrim (49+) */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  width: 56px;
  border-radius: 999px;
  background: var(--af-kg-600);
  color: #fff;
  box-shadow: 0 12px 26px -12px rgba(8, 61, 29, .7), 0 2px 6px rgba(8, 61, 29, .22);
  opacity: 0;
  visibility: hidden;              /* also keeps it out of the tab order while empty */
  transform: translateY(10px) scale(.92);
  transition: opacity .18s ease, transform .18s ease, visibility .18s ease, background .18s ease;
}

.af-fab:hover { background: var(--af-kg-700); color: #fff; }
.af-fab:active { transform: scale(.96); }
.af-fab .ms { font-size: 26px; }
.af-fab.af-fab-show { opacity: 1; visibility: visible; transform: none; }

/* Above the mobile bottom bar. --af-fab-lift is the measured height of
   .af-botnav, which already carries its own safe-area padding; the 58px
   fallback covers the frame before script.js measures. */
@media (max-width: 767.98px) {
  .af-fab { right: 14px; bottom: calc(var(--af-fab-lift, 58px) + 14px); }
}

.af-fab-badge {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 22px;
  height: 22px;
  border-radius: 999px;
  border: 2px solid var(--af-cream);
  background: var(--af-gold-400);
  padding: 0 5px;
  font-size: 11px;
  font-weight: 800;
  line-height: 18px;
  text-align: center;
  color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  .af-fab { transition: none; }
}

/* ── brand logo outside the masthead ────────────────────────────────────── */
/* Both reuse .af-brand-plate (the white pill) so a green mark reads against
   the kg-900 footer and the green drawer head; only the sizing is new. */

/* inline-flex, not the plate's own `flex`: as a block-level flex box the plate
   would stretch to the whole footer column and paint a full-width white bar. */
.af-footer-logo { display: inline-flex; padding: 6px 10px; }
.af-footer-logo img { height: 40px; width: auto; max-width: 190px; object-fit: contain; }
@media (min-width: 768px) { .af-footer-logo img { height: 46px; } }

.af-drawer-logo { display: inline-flex; padding: 4px 8px; }
.af-drawer-logo img { height: 30px; width: auto; max-width: 150px; object-fit: contain; }

/* ── auth modal: status note ────────────────────────────────────────────── */
/* Shown above the sign-in form when the modal has something to report — today
   that is the password-reset outcome ("Password changed…", "That link has
   expired…"). Green, because it is a status line, not an error: .af-error
   already owns the red. */
.af-auth-note {
  margin-bottom: 14px;
  border-radius: var(--af-radius-sm);
  border: 1px solid var(--af-kg-200);
  background: var(--af-kg-50);
  padding: 10px 12px;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--af-kg-800);
}

/* ── category listing screen (/menu) ────────────────────────────────────────
   Built to the FreshDays template's `isList` page: breadcrumb, 248px filter
   sidebar from lg, header row (title + count + sort pills), 2/3/4 grid.
   Everything here is prefixed .af-list-*. The category switcher is the header's
   own tab bar (.af-catnav-*, pinned) at every width — this screen renders no
   navigator of its own; the sidebar's Category list is a filter.
   ------------------------------------------------------------------------- */

.af-list-layout { margin-top: 12px; display: grid; gap: 16px; }
@media (min-width: 1024px) { .af-list-layout { grid-template-columns: 248px minmax(0, 1fr); align-items: start; } }

/* A grid item defaults to min-width:auto, so the results column was sized by
   its widest unbreakable child — the sort-pill row — and pushed the right-hand
   cards off a 390px screen (measured: 457px of column in a 366px track). The
   pill row is a horizontal scroller by design; this is what lets it be one. */
.af-list-main { min-width: 0; }

/* ── sidebar (desktop) / bottom sheet (mobile) ──────────────────────────── */

.af-list-side {
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: #fff;
  padding: 16px;
}
@media (min-width: 1024px) { .af-list-side { position: sticky; top: calc(var(--af-catnav-h, 44px) + 12px); } }

@media (max-width: 1023.98px) {
  .af-list-side {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 52;                  /* over the bottom bar (40) and the FAB (45) */
    max-height: 82vh;
    overflow-y: auto;
    border-radius: var(--af-radius-lg) var(--af-radius-lg) 0 0;
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    transform: translateY(101%);
    visibility: hidden;
    transition: transform .22s ease, visibility .22s ease;
  }
  .af-list-side.open { transform: none; visibility: visible; }
}

.af-list-scrim {
  position: fixed;
  inset: 0;
  z-index: 51;
  background: rgba(27, 43, 35, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s ease, visibility .2s ease;
}
.af-list-scrim.open { opacity: 1; visibility: visible; }
@media (min-width: 1024px) { .af-list-scrim { display: none; } }

.af-list-side-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.af-list-side-title { font-size: 12.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; }
.af-list-sheet-x { display: grid; place-items: center; height: 40px; width: 40px; border-radius: var(--af-radius-sm); color: var(--af-mud); }
.af-list-sheet-x:hover { background: var(--af-kg-50); }
@media (min-width: 1024px) { .af-list-sheet-x { display: none; } }

.af-list-block { margin-top: 16px; border-top: 1px solid var(--af-line); padding-top: 12px; }
.af-list-block-title { font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--af-mud); }

.af-list-catlist { margin-top: 8px; display: grid; gap: 2px; }
.af-list-catrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 40px;
  border-radius: var(--af-radius-sm);
  padding: 6px 8px;
  text-align: left;
  font-size: 12px;
  font-weight: 600;
  color: rgba(27, 43, 35, .8);
}
.af-list-catrow:hover { background: var(--af-kg-50); }
.af-list-catrow.active { background: var(--af-kg-50); color: var(--af-kg-700); font-weight: 800; }
.af-list-catcount { font-size: 10.5px; font-weight: 700; color: var(--af-mud); }

.af-list-range { margin-top: 12px; width: 100%; accent-color: var(--af-kg-600); }
.af-list-range-label { margin-top: 4px; font-size: 11.5px; font-weight: 700; color: var(--af-ink); }

.af-list-checks { margin-top: 8px; display: grid; gap: 2px; }
.af-list-check {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 4px 2px;
  font-size: 12px;
  font-weight: 500;
  color: rgba(27, 43, 35, .85);
  cursor: pointer;
}
.af-list-check input { height: 15px; width: 15px; accent-color: var(--af-kg-600); }

.af-list-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 40px;
  border-radius: var(--af-radius-sm);
  background: var(--af-cream);
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 700;
}
.af-list-toggle-state { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-mud); }
.af-list-toggle[aria-pressed="true"] { background: var(--af-kg-50); color: var(--af-kg-700); }
.af-list-toggle[aria-pressed="true"] .af-list-toggle-state { color: var(--af-kg-600); }

.af-list-sheet-done { margin-top: 16px; width: 100%; }
@media (min-width: 1024px) { .af-list-sheet-done { display: none; } }

/* ── header row: title, count, sort pills ───────────────────────────────── */

.af-list-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: #fff;
  padding: 10px 12px;
  scroll-margin-top: calc(var(--af-catnav-h, 44px) + 12px);
}
.af-list-title { font-size: 19px; }
@media (min-width: 768px) { .af-list-title { font-size: 22px; } }
.af-list-count { font-size: 11.5px; font-weight: 600; color: var(--af-mud); }

.af-list-tools { margin-left: auto; display: flex; align-items: center; gap: 6px; overflow-x: auto; max-width: 100%; }
.af-list-sort-label { display: none; flex: 0 0 auto; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--af-mud); }
@media (min-width: 768px) { .af-list-sort-label { display: block; } }

.af-list-sort {
  flex: 0 0 auto;
  min-height: 40px;
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
  color: rgba(27, 43, 35, .7);
}
.af-list-sort:hover { background: var(--af-kg-50); }
.af-list-sort.active { background: var(--af-kg-50); color: var(--af-kg-700); box-shadow: inset 0 0 0 1px var(--af-kg-200); }

.af-list-filter-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: var(--af-ink);
  padding: 6px 14px;
  font-size: 11.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #fff;
}
.af-list-filter-btn .ms { font-size: 16px; }
.af-list-filter-btn.has-filters { background: var(--af-kg-700); }
@media (min-width: 1024px) { .af-list-filter-btn { display: none; } }

.af-list-note { margin-top: 12px; font-size: 12.5px; line-height: 1.6; color: var(--af-mud); }

/* Only the selected category is on screen, so its section carries no heading
   of its own — the header row above is the H1. */
.af-list .af-cat-section { padding-top: 14px; }
.af-list .af-cat-section[hidden] { display: none; }

/* The template's list grid: 2 / sm 3 / xl 4 — 4-up only once the viewport can
   take it alongside the 248px sidebar. */
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .af-list-main .af-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Cookie-preferences reopen control in the legal row — the platform consent
   script binds via [data-cookie-preferences]; style it as a legal link. */
.af-footer-cookie-prefs { background: none; border: none; padding: 0; font: inherit; color: inherit; text-decoration: underline; cursor: pointer; }
.af-footer-cookie-prefs:hover { color: var(--af-gold-200, #F3C0B7); }

/* ── product page (/menu/{slug}) ────────────────────────────────────────────
   Built to the FreshDays template's `isProduct` screen (index.html lines
   522-684): gallery column, detail column, delivery/basket aside, then the
   "goes well with" rail underneath and a sticky CTA bar on mobile.

   THE MARKUP IS NOT OURS. /menu/{slug} is a React route; this bundle owns only
   the stylesheet it loads (STOREFRONT_ASSETS_FROM_CMS) and the wording it reads
   from the `tijaro-product-*` metas in site-layout.yml. So every class below is
   a contract: renaming one here silently unstyles the page. They are all
   prefixed .af-pdp-* and nothing outside this block uses them.

   The three-column grid is the template's own — minmax(0,440px) 1fr 312px —
   and collapses to one column under 1024px, where the aside falls below the
   detail column and the mobile CTA bar takes over from the gallery's buttons.
   ------------------------------------------------------------------------- */

.af-pdp {
  margin-top: 12px;
  display: grid;
  gap: 16px;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 1024px) {
  .af-pdp { grid-template-columns: minmax(0, 440px) minmax(0, 1fr) 312px; align-items: start; }
}

/* ── gallery column ─────────────────────────────────────────────────────── */

.af-pdp-gallery {
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: var(--af-white);
  padding: 12px;
}

.af-pdp-media {
  position: relative;
  aspect-ratio: 1 / 1;
  width: 100%;
  overflow: hidden;
  border-radius: 12px;
  background: var(--af-cream);
}
.af-pdp-media img { width: 100%; height: 100%; object-fit: cover; }

/* Same rule as the card badge: only ever printed against a real was-price. */
.af-pdp-off {
  pointer-events: none;
  position: absolute;
  left: 12px;
  top: 12px;
  border-radius: var(--af-radius-sm);
  background: var(--af-chilli);
  padding: 4px 8px;
  font-size: 12px;
  font-weight: 800;
  line-height: 1.1;
  color: #fff;
}

.af-pdp-wish {
  position: absolute;
  right: 12px;
  top: 12px;
  display: grid;
  place-items: center;
  height: 36px;
  width: 36px;
  border-radius: 999px;
  background: var(--af-white);
  color: var(--af-mud);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .14), inset 0 0 0 1px var(--af-line);
}
.af-pdp-wish:hover { color: var(--af-chilli); }
.af-pdp-wish .ms { font-size: 19px; font-variation-settings: 'FILL' 0, 'wght' 500; }
.af-pdp-wish.is-on { color: var(--af-chilli); }
.af-pdp-wish.is-on .ms { font-variation-settings: 'FILL' 1, 'wght' 500; }

.af-pdp-thumbs { margin-top: 10px; display: flex; gap: 8px; overflow-x: auto; }
/* This design's thumbnail SQUARE, drawn identically wherever it appears:
   `overflow-hidden rounded-lg bg-cream ring-1 ring-black/10` on the product
   gallery (index.html:537) and on the order-history card's strip (:1059). The
   two differ only in size — 64px there, 56px here — so the shape is shared and
   the My orders block restates the dimension alone. The strip's CONTAINER is
   not shared: the gallery's opens `mt-3`, while the history card spaces its own
   rows with a 12px grid gap and a margin there would double it. */
.af-pdp-thumb,
.tj-acct-thumb {
  position: relative;
  height: 64px;
  width: 64px;
  flex: 0 0 auto;
  overflow: hidden;
  border-radius: var(--af-radius-sm);
  background: var(--af-cream);
  box-shadow: inset 0 0 0 1px var(--af-line-strong);
}
.af-pdp-thumb img,
.tj-acct-thumb img { width: 100%; height: 100%; object-fit: cover; }
.af-pdp-thumb.active { box-shadow: inset 0 0 0 2px var(--af-kg-600); }

/* Desktop-only CTA pair under the gallery. Below md the sticky bar owns it. */
.af-pdp-cta-row { display: none; }
@media (min-width: 768px) { .af-pdp-cta-row { margin-top: 12px; display: flex; gap: 8px; } }
.af-pdp-cta-row .af-pdp-add,
.af-pdp-cta-row .af-pdp-buy { flex: 1 1 0; margin-top: 0; }

/* ── detail column ──────────────────────────────────────────────────────── */

/* min-width:0 for the same reason as .af-list-main: a grid item is sized by its
   widest unbreakable child by default, and this column holds a long product
   name, a spec table and a chip row. Without it one long word pushes the aside
   off the screen. */
.af-pdp-main { min-width: 0; }

.af-pdp-brand { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .16em; color: var(--af-mud); }
.af-pdp-title { margin-top: 4px; font-family: var(--af-display); font-size: 23px; font-weight: 800; line-height: 1.15; }
@media (min-width: 768px) { .af-pdp-title { font-size: 28px; } }

.af-pdp-meta { margin-top: 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* Rendered only when the page has a rating to show. The platform has no
   reviews today — the class exists so nothing has to be restyled when it does,
   not as an invitation to print a number nobody left. */
.af-pdp-rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border-radius: 6px;
  background: var(--af-kg-600);
  padding: 3px 8px;
  font-size: 11.5px;
  font-weight: 800;
  color: #fff;
}
.af-pdp-rating .ms { font-size: 12px; }
/* The count beside the chip — "412 ratings". Muted, so the green chip carries
   the eye and the number stays supporting text (template line 551). */
.af-pdp-ratings { font-size: 11.5px; font-weight: 600; color: var(--af-mud); }
.af-pdp-stock { font-size: 11.5px; font-weight: 700; color: var(--af-kg-600); }

.af-pdp-price-row { margin-top: 12px; display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; }
.af-pdp-price { font-family: var(--af-display); font-size: 30px; font-weight: 800; line-height: 1; }
.af-pdp-mrp { font-size: 15px; font-weight: 600; color: rgba(107, 98, 87, .7); text-decoration: line-through; }
.af-pdp-save { font-size: 13px; font-weight: 800; color: var(--af-chilli); }
.af-pdp-tax { margin-left: 4px; font-size: 11px; font-weight: 600; color: var(--af-mud); }

.af-pdp-group { margin-top: 16px; }
.af-pdp-group-title { font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--af-mud); }
.af-pdp-chips { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 8px; }
.af-pdp-chip {
  border-radius: 12px;
  border: 1px solid var(--af-line-strong);
  background: var(--af-white);
  padding: 8px 12px;
  text-align: left;
  font-size: 12px;
  font-weight: 700;
  color: rgba(27, 43, 35, .8);
  transition: border-color .15s ease, background .15s ease, color .15s ease;
}
.af-pdp-chip:hover { border-color: var(--af-kg-600); }
.af-pdp-chip.active { border-color: var(--af-kg-600); background: var(--af-kg-50); color: var(--af-kg-700); }
/* Out-of-stock pack size / cut: still visible (it tells the customer the
   option exists) but not selectable. */
.af-pdp-chip.is-disabled {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
  text-decoration: line-through;
}
.af-pdp-chip-price { display: block; font-size: 11px; font-weight: 600; color: var(--af-mud); }
.af-pdp-group-note { margin-top: 8px; display: flex; align-items: flex-start; gap: 6px; font-size: 11.5px; font-weight: 600; line-height: 1.5; color: var(--af-kg-600); }
.af-pdp-group-note .ms { font-size: 15px; flex: 0 0 auto; }

.af-pdp-offers {
  margin-top: 16px;
  border-radius: var(--af-radius);
  border: 1px dashed var(--af-gold-400);
  background: rgba(251, 227, 223, .5);
  padding: 12px;
}
.af-pdp-offers-title { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 800; }
.af-pdp-offers-title .ms { font-size: 17px; color: var(--af-gold-600); }
.af-pdp-offer { margin-top: 6px; display: flex; align-items: flex-start; gap: 6px; font-size: 11.5px; font-weight: 500; line-height: 1.6; color: rgba(27, 43, 35, .85); }
.af-pdp-offer .ms { font-size: 14px; flex: 0 0 auto; color: var(--af-kg-600); }

.af-pdp-cards { margin-top: 16px; display: grid; gap: 12px; }
@media (min-width: 640px) { .af-pdp-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.af-pdp-card {
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: var(--af-white);
  padding: 14px;
}
.af-pdp-card-title { font-size: 12.5px; font-weight: 800; }
.af-pdp-desc { margin-top: 6px; font-size: 12px; font-weight: 500; line-height: 1.7; color: var(--af-mud); }
.af-pdp-specs { margin-top: 6px; display: grid; gap: 4px; }
.af-pdp-spec {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 1px solid var(--af-line);
  padding: 4px 0;
  font-size: 11.5px;
  font-weight: 500;
}
.af-pdp-spec > :first-child { color: var(--af-mud); }
.af-pdp-spec > :last-child { text-align: right; font-weight: 700; color: var(--af-ink); }

/* ── delivery / basket aside ────────────────────────────────────────────── */

.af-pdp-side {
  align-self: start;
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: var(--af-white);
  padding: 16px;
}
@media (min-width: 1024px) { .af-pdp-side { position: sticky; top: calc(var(--af-catnav-h, 44px) + 12px); } }

.af-pdp-side-title { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--af-mud); }
.af-pdp-pin {
  margin-top: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  border-radius: 12px;
  background: var(--af-cream);
  padding: 8px 12px;
}
.af-pdp-pin .ms { font-size: 18px; color: var(--af-kg-600); }
.af-pdp-pin input,
.af-pdp-pin > span:not(.ms) { width: 100%; border: 0; background: transparent; font-size: 12.5px; font-weight: 700; color: var(--af-ink); outline: none; }
.af-pdp-pin button,
.af-pdp-pin a { flex: 0 0 auto; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--af-kg-600); }

.af-pdp-side-list { margin-top: 12px; display: grid; gap: 8px; font-size: 12px; font-weight: 600; }
.af-pdp-side-item { display: flex; align-items: flex-start; gap: 8px; line-height: 1.45; }
.af-pdp-side-item .ms { font-size: 16px; flex: 0 0 auto; color: var(--af-kg-600); }

.af-pdp-qty {
  margin-top: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border-radius: 12px;
  background: var(--af-kg-800);
  padding: 12px;
}
.af-pdp-qty-label { display: flex; flex-direction: column; line-height: 1.2; }
.af-pdp-qty-label > :first-child { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; color: var(--af-gold-200); }
.af-pdp-qty-label > :last-child { font-size: 12px; font-weight: 600; color: rgba(255, 255, 255, .8); }
.af-pdp-qty-ctl { display: flex; align-items: center; gap: 4px; border-radius: var(--af-radius-sm); background: rgba(255, 255, 255, .1); padding: 4px; }
.af-pdp-qty-ctl button { display: grid; place-items: center; height: 28px; width: 28px; border-radius: 6px; color: #fff; }
.af-pdp-qty-ctl button:hover { background: rgba(255, 255, 255, .2); }
.af-pdp-qty-ctl .ms { font-size: 17px; }
.af-pdp-qty-ctl > span { min-width: 24px; text-align: center; font-size: 13px; font-weight: 800; color: #fff; }

/* The two commit buttons. Base = the aside's pair (filled green "Add to
   basket", the red "Buy now" the client sees in the screenshot); the gallery
   row and the aside then swap which of the two is outlined, exactly as the
   template does. */
.af-pdp-add,
.af-pdp-buy {
  margin-top: 12px;
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-radius: 12px;
  border: 2px solid transparent;
  padding: 12px;
  font-size: 13px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .08em;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.af-pdp-add { background: var(--af-kg-600); color: #fff; }
.af-pdp-add:hover { background: var(--af-kg-700); color: #fff; }
.af-pdp-buy { background: var(--af-gold-400); color: #fff; }
.af-pdp-buy:hover { background: var(--af-gold-600); color: #fff; }

/* Under the gallery: green outline + red fill. */
.af-pdp-cta-row .af-pdp-add { border-color: var(--af-kg-600); background: var(--af-white); color: var(--af-kg-700); }
.af-pdp-cta-row .af-pdp-add:hover { background: var(--af-kg-50); color: var(--af-kg-700); }

/* In the aside: green fill + red outline. */
.af-pdp-side .af-pdp-buy { margin-top: 8px; border-color: var(--af-gold-400); background: transparent; color: var(--af-gold-600); }
.af-pdp-side .af-pdp-buy:hover { background: var(--af-gold-100); color: var(--af-gold-600); }

.af-pdp-add[disabled], .af-pdp-buy[disabled] { opacity: .5; cursor: not-allowed; }

/* ── "goes well with" rail ──────────────────────────────────────────────── */

.af-pdp-rel {
  margin-top: 20px;
  border-radius: var(--af-radius);
  border: 1px solid var(--af-line);
  background: var(--af-white);
  padding: 12px;
}
@media (min-width: 768px) { .af-pdp-rel { padding: 16px; } }
.af-pdp-rel-title { margin-bottom: 12px; font-family: var(--af-display); font-size: 17px; font-weight: 800; }
@media (min-width: 768px) { .af-pdp-rel-title { font-size: 19px; } }
.af-pdp-rel-track {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  padding-bottom: 4px;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
/* The rail carries the same .af-card as the grid; only its width is fixed. */
.af-pdp-rel-track > * { width: 152px; flex: 0 0 auto; }
@media (min-width: 640px) { .af-pdp-rel-track > * { width: 190px; } }

/* ── mobile commit bar ──────────────────────────────────────────────────── */
/* Sits above the bottom nav — --af-fab-lift is that bar's measured height, so
   this never lands on top of it (or floats over nothing when it is hidden). */
.af-pdp-sticky {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--af-fab-lift, 58px);
  z-index: 46;                     /* over .af-botnav (40) and the FAB (45), under every scrim (49+) */
  display: flex;
  gap: 8px;
  border-top: 1px solid var(--af-line);
  background: rgba(255, 255, 255, .96);
  backdrop-filter: blur(8px);
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
}
.af-pdp-sticky .af-pdp-add,
.af-pdp-sticky .af-pdp-buy { flex: 1 1 0; margin-top: 0; padding: 11px; font-size: 12.5px; }
.af-pdp-sticky .af-pdp-add { border-color: var(--af-kg-600); background: var(--af-white); color: var(--af-kg-700); }
@media (min-width: 768px) { .af-pdp-sticky { display: none; } }

/* The floating basket occupies this exact band on mobile. On a product page the
   commit bar IS the basket affordance, so the FAB stands down rather than
   sitting on top of "Buy now". The z-index above keeps the bar in front even in
   a browser without :has(). */
@media (max-width: 767.98px) {
  body:has(.af-pdp-sticky) .af-fab { display: none; }
}

/* ==========================================================================
   React control layer — the `lm-*` primitives, in FreshDays' shapes  (#2103)
   --------------------------------------------------------------------------
   The React routes (/checkout, /checkout/success, /gifts, /enquiry, the
   upsell sheet and the voucher / gift-card / loyalty code fields) draw their
   buttons, fields, labels and chips from the `lm-*` classes in the storefront's
   shared `app/globals.css`. Those classes are ASHARAF'S KITCHEN'S design
   language, not a neutral default: `.lm-btn` hard-codes `border-radius: 999px`,
   `font-weight: 500` and sentence case, and reads no token for any of them.

   That is why the 27 `--lm-*` re-points above were never enough. A custom
   property can only change a value a rule already reads; it cannot change one
   the rule hard-codes. The result was FreshDays colours on Asharaf's shapes —
   a green pill in medium weight — which the client has now rejected three
   times. See docs/pilots/asianfresh/DESIGN_LANGUAGE_AUDIT.md for the ranked
   evidence.

   This block overrides the RULES. It wins because this stylesheet is the LAST
   <link> in <head> (after /_next/static/css/…, after /assets/boot.css) and both
   sides are author-origin at equal specificity — verified in a browser against
   both the dev server and the production standalone build, and against a real
   `.lm-btn` collision, not just by reading the markup.

   ISOLATION: this file is served only to this tenant, from
   /cms/assets/theme.css → /api/public/storefront/asianfresh/assets/theme.css.
   `globals.css` is untouched, so Asharaf's, Breizh, Kismath and Tijaro render
   byte-identically to before.

   PROVENANCE: every value below is either a rule already in this file — the
   `af-*` layer that draws the tenant's own CMS pages and product page — or a
   measurement taken from the client's prototype, and each rule says which.
   Nothing is borrowed from another client. Three values are neither, and are
   named where they appear: the buttons' HORIZONTAL padding (the `af-*` buttons
   are all full-width and state none, so it comes from the prototype's `px-5`),
   `.lm-btn-sm`/`-lg` padding (raised as the font size drops, so the tap target
   does not shrink), and the fields' `line-height` (a measured fix, not a style
   choice). The point is that a shopper crossing from /menu to /checkout does
   not change shops.

   DELIBERATELY NOT DECLARED:
     • `.lm-btn { min-height }` — globals.css bumps it to 48px (40px for -sm)
       below 760px and that rule is not `!important`, so declaring it here would
       silently shrink every button on a phone.
     • `font-size` on the fields below 760px — globals.css pins 16px there with
       `!important` to stop iOS zooming the viewport on focus. Out-importanting
       an accessibility guard to win 2.5px is the wrong trade.
     • `.lm-card`, `.lm-mono`, `.lm-divider`, `.lm-spinner`, `.lm-card-hover`,
       and every `.lm-btn-*` colour variant — all already resolve to this
       tenant's tokens (`.lm-card` is 16px/white/hairline, i.e. the prototype's
       `rounded-2xl border-black/5 bg-white`). Re-stating them would ship CSS
       that changes nothing.
     • `.lm-chip-ember`, `.lm-bottom-nav`, `.lm-scroll`, `.lm-section` — no call
       site reaches an Asian Fresh shopper. Recorded in the audit instead.
   ========================================================================== */

/* Buttons — `.af-btn`'s shape. The prototype draws `rounded-xl` 87 times
   against `rounded-full` 28 (badges, avatars, dots), `font-extrabold` 206
   times and `uppercase` 137 times; its canonical CTA, counted across all 133
   of its buttons, is `rounded-xl font-extrabold uppercase tracking-wider
   text-[12.5px]`. Horizontal padding is that CTA's `px-5` (20px); vertical is
   the `py-3` (12px) its full-width CTAs use rather than the `py-2.5` of its
   inline ones, because 12px is what holds the control at 44.8px — above the
   44px minimum target on desktop, against 49px before. The ≤760px guard in
   globals.css still takes it to 48px on a phone. */
.lm-btn {
  border-radius: 12px;
  padding: 12px 20px;
  font-size: 12.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
}
/* `.af-news-btn` — the tenant's own compact action, 11.5px/800/.06em on
   `var(--af-radius-sm)`. Padding is raised as the font size drops so the target
   holds: 39.3px, against 39.5px before, and the ≤760px guard still gives it
   40px on a phone. */
.lm-btn-sm { border-radius: var(--af-radius-sm); padding: 10px 14px; font-size: 11.5px; }
/* `.af-pdp-buy` — the tenant's own large commit button (13px, .08em). This is
   the one control that gets materially shorter, 56.5px → 49.5px, which is the
   prototype's own `py-3.5 text-[13px]` and still well clear of 44px. */
.lm-btn-lg { padding: 14px 26px; font-size: 13px; letter-spacing: .08em; }

/* Fields — `.af-input`, which this tenant already ships on its enquiry page.
   The prototype's field is `rounded-xl border border-black/10 bg-white px-3.5
   py-3 text-[13px] font-semibold` (8 of its 8 text fields). `border-radius` is
   NOT restated: `.lm-input` reads `var(--lm-radius-sm)`, which this file has
   already pointed at `--af-radius-sm`, so it is the tenant's own 10px. */
.lm-input,
.lm-select,
.lm-textarea {
  border-color: var(--af-line-strong);
  background: var(--af-white);
  padding: 11px 12px;
  font-size: 13.5px;
  font-weight: 500;
  /* The UA gives <select> `line-height: normal`, so its height is decided by
     whatever font is loaded AT THAT MOMENT: measured at 41px mid-FOUT and 44px
     once Manrope arrives, while the <input> beside it sat at 44.3px throughout.
     Stating the ratio makes the row one height and takes the tap target off the
     font-loading race. */
  line-height: 1.5;
}
/* The prototype's `focus:ring-2 focus:ring-kg-100`, which the shared CSS omits
   entirely. Its companion `focus:border-kg-600` is NOT declared here, and that
   is deliberate: no `border-color` on `.lm-input:focus` reaches the element.
   Measured in Chromium on /gifts and /enquiry, against a real page input and a
   synthetic one, with the value as a var, as a literal, as four longhands and
   as the full `border` shorthand — every form is inert, because
   `globals.css`'s `.lm-input { border: 1px solid var(--lm-line) }` puts a
   `var()` in a border SHORTHAND and its pending-substitution longhands outlive
   the later declaration. The platform's own
   `.lm-input:focus { border-color: var(--lm-saffron) }` is inert for the same
   reason and always has been (verified with this block removed), so this is a
   defect in the shared layer to raise there, not one to paper over from a
   tenant stylesheet with `!important`. Focus stays clearly indicated: this ring
   plus the tenant's own `:focus-visible { outline: 2px solid var(--af-kg-600) }`
   above, measured at rgb(27,148,72) 2px. */
.lm-input:focus,
.lm-select:focus,
.lm-textarea:focus {
  box-shadow: 0 0 0 3px var(--af-kg-50);
}
/* `.af-input::placeholder` is `rgba(107, 98, 87, .55)` and is NOT adopted here.
   Against the white fill this rule introduces, that composites to ≈2.2:1, where
   the shared `--lm-ink-faint` placeholder it would replace reaches ≈3.2:1.
   Matching the CMS form exactly would mean making a contrast-sensitive value
   worse to win a difference nobody can see; the tenant's own placeholder is the
   one to raise, not this one to lower. */

/* Field captions — `.af-label`. Already uppercase in globals.css; what was
   wrong is 10px/400/.16em in a 72%-alpha ink that composites to 3.2:1 on cream
   and fails AA. Solid `--af-mud` is 5.7:1, and is what the prototype uses. */
.lm-label {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: .06em;
  color: var(--af-mud);
}

/* Section eyebrows — `.af-pdp-brand`, this tenant's own 11px caps rule. Size,
   case and colour are already right; the weight and tracking were Asharaf's. */
.lm-eyebrow { font-weight: 800; letter-spacing: .16em; }

/* Page kickers — despite the name, `.lm-num` is the caps line above every
   `.lm-serif` page title ("Checkout", "Gift vouchers", "Order · A1B2C3").
   `.af-card-brand` is the same object on the tenant's own cards. */
.lm-num { font-weight: 700; text-transform: uppercase; letter-spacing: .12em; }

/* Chips. The pill IS this tenant's chip shape — `.af-kicker` and
   `.af-list-filter-btn` are both `999px`, and the prototype's badges are all
   `rounded-full` — so radius, case and tracking stay. Only the weight moves. */
.lm-chip { font-weight: 800; }
/* These two carried Asharaf's saffron and sage as raw RGB, which no token
   re-point can reach. `-accent` is the dietary "info" badge (GF / DF) and
   `-leaf` the positive one (veg / vegan / savings), so they take the tenant's
   warm and green ramps: 6.3:1 and 5.0:1 on their own washes. */
.lm-chip-accent {
  background: var(--af-gold-100);
  border-color: var(--af-gold-200);
  color: var(--af-gold-600);
}
.lm-chip-leaf {
  background: var(--af-kg-50);
  border-color: var(--af-kg-200);
  color: var(--af-kg-700);
}

/* Display type. `.lm-serif` drags every React page title back to 400. All 31
   `font-display` usages in the prototype are `font-extrabold`, and none of the
   23 without a tracking class is tightened.

   Two corrections to what this comment used to claim (#2106). It said 400 was a
   weight "Baloo Chettan 2 does not ship, so the browser synthesises it" — it is
   not synthesised; the face ships 500-800 and 400 resolves to the real 500 face,
   measured identical at 60px. And it said this rule was needed because
   `.lm-serif` "outranks this file's own h1..h5 reset": that reset was not being
   outranked, it was not applying at all (the Tailwind preflight beat it on
   source order — see the note on the reset itself). The rule is still right; the
   reasoning printed here was not. */
/* `!important` on the tracking only (#2107). Three React heroes set
   `letterSpacing` INLINE and an inline declaration outranks every normal one:
   `/gifts` and `/enquiry` at -0.025em, `/checkout/success` at -0.02em, measured
   as -2.56px, -2px and -1.28px. Deleting those inline values is NOT the fix —
   it would drop all four tenants onto `globals.css`'s
   `.lm-serif { letter-spacing: -0.01em }`, changing what Asharaf's, Breizh and
   Kismath render, which is the one thing this work may not do. Overriding from
   here changes nothing for them: they never load this file, and their inline
   values stay exactly where they are.

   Scoped to the FACE rather than to `h1` on purpose — the invariant is "this
   tenant does not tighten its display face", and the child `<span>`s inherit it
   without needing rules of their own. The weight beside it needs no
   `!important`; nothing declares that inline. */
.lm-serif { font-weight: 800; letter-spacing: normal !important; }

/* Native checkboxes and radios. The prototype puts `accent-kg-600` on all
   seven of its own; neither stylesheet set `accent-color`, so the consent and
   preference controls were rendering in the browser's default blue. */
input[type="checkbox"],
input[type="radio"] { accent-color: var(--af-kg-600); }

/* ── Controls the shared checkout draws with INLINE styles  (#2104) ────────
   Everything above reaches its target by overriding a RULE. The two controls
   below cannot: `apps/storefront/src/app/checkout/checkout-fields.tsx` puts
   their design decisions in `style={{…}}` attributes, and an inline
   declaration outranks every normal declaration in every stylesheet. The only
   author-level mechanism that beats it is `!important`, so the two properties
   that are genuinely inline carry it and nothing else does.

   This is a WORKAROUND, not the fix. The durable fix is to lift those inline
   styles into class rules in the shared `globals.css` — a verbatim move that
   would leave all four tenants rendering identically and would let every
   tenant theme these controls without `!important`. That edits a file this
   tenant's work is not allowed to touch, so it is raised for the shared layer
   rather than done here. Until then the tests in
   `src/app/__tests__/asianfresh-control-layer.test.ts` and
   `tests/e2e/asianfresh-control-layer.spec.ts` pin the COMPUTED result, so if
   the component's inline styles change and these overrides start half-applying
   it fails loudly instead of rotting quietly. */

/* The Pickup / Delivery switch override that lived here (#2104) is GONE with
   the control it re-typed (#2111). The client's checkout has no collection
   route — step 1 is "Where should we deliver?" over the address list — and he
   has confirmed the shop takes no pickup at the moment, so the stepped layout
   no longer renders `FulfillmentSection` at all. `.lm-fulfill-row` is
   unreachable for this tenant, and a rule for it would be dead CSS with two
   `!important`s attached. The single-column layout every other tenant uses
   still draws the switch, styled by the shared `globals.css` as it always
   was. */

/* Consent and preference rows. The prototype's checkbox rows are
   `text-[12px] font-medium`; only the WEIGHT is adopted. Its 12px is measured
   off catalogue filters ("In stock only", "Halal certified"), not off a
   marketing-consent control, and shrinking legally load-bearing consent text to
   match a filter chip is not a design decision worth copying — so the 15px
   stays and no `!important` is needed for the one property that moves. The tick
   itself is already the tenant's green via the `accent-color` rule above.

   `:not([class])` is load-bearing, not decoration. `:has()` takes the
   specificity of its argument, so an unqualified `label:has(> input[…])` would
   be (0,1,2) — MORE specific than `.af-list-check`, this file's own CMS filter
   row on /menu. That row already declares `font-weight: 500`, so the override
   would be invisible today and would silently win the day someone changed it.
   Restricting the selector to labels carrying no class at all targets exactly
   the controls that have no other hook — which is the whole reason this rule
   exists — and cannot collide with a classed control now or later. */
label:not([class]):has(> input[type="checkbox"]) { font-weight: 500; }

/* ── The display face, everywhere the shared CSS uses it  (#2106) ──────────
   17 rules in `app/globals.css` set `font-family: var(--lm-serif)` and declare
   NO `font-weight`, so every one of them inherits the body's 400. That is the
   basket title, the checkout title, its card titles and THE CHECKOUT TOTAL,
   every account title, the FreshCoins balance and the order-confirmation
   heading — 13 elements across 8 surfaces, all in the tenant's display face at
   a weight the prototype never uses. The prototype is `font-extrabold` in all
   31 places it uses that face.

   The heading reset above cannot cover these. Half of them are `<p>` and
   `<span>`, not headings, and the ones that ARE headings carry a class, which
   outranks any element selector. Each rule needs the weight stated.

   For the record, and correcting the note on the `.lm-serif` rule above: 400 is NOT a
   synthesised face. Baloo Chettan 2 ships 500-800, and a browser asked for 400
   picks the nearest real face — measured at 60px, `w400` and `w500` render
   identically (307.27px for the same string) while 600/700/800 step up
   (312.19 / 318.16 / 323.28). So the defect was never a fake weight; it was the
   right face at the wrong end of its range.

   These live here rather than in `globals.css` because they are a statement
   about THIS tenant's design language, and a tenant stylesheet needs no
   cross-tenant proof: Asharaf's, Breizh and Kismath never load this file. */
.tj-cart-title,
.tj-cart-empty-title,
.tj-cart-thumb-ph,
.tj-cart-tile-ph,
.tj-cart-total,
.tj-co-title,
.tj-co-card-title,
.tj-co-total-v,
.tj-oc-title,
.tj-acct-title,
.tj-acct-balance-v,
.tj-acct-stat-v,
.tj-acct-hero-v,
.tj-acct-order-total,
.tj-wish-ph { font-weight: 800; }

/* ── The basket's coupon field  (#2109, reversed) ──────────────────────────
   `.tj-cart-input` is the prototype's coupon box, and the prototype fills it
   `bg-cream`:

     class="w-full rounded-xl bg-cream px-3 py-2.5 text-[12.5px] font-bold
            uppercase tracking-wider ring-1 ring-black/5 …"   (index.html:778)

   #2109 originally forced it white, reasoning that cream on a cream page
   "reads as no field at all". That reasoning was mine, not the client's, and
   they have rejected it: their prototype is the spec. The field sits inside the
   WHITE summary rail, not on the cream page, so cream is the inset the design
   intends and the contrast argument did not apply in the first place.

   The `field-not-white` detector in the design sweep inverts with this: cream
   is now the correct fill for this one control, and the sweep records it as the
   prototype's own exception rather than as a finding. */
.tj-cart-input { background: var(--af-cream); }

/* ── Delivery-notes disclosure on step 1  (#2111) ──────────────────────────
   Approved placement (#2111). Closed by
   default so step 1 renders as the template draws it — a heading, the address
   cards, "Add a new address" and "Deliver here" — with one quiet line beneath.
   Typed as the tenant's own micro-caps so it reads as part of the card rather
   than as a bolted-on control. */
.tj-co-notes { margin-top: 10px; }
.tj-co-notes > summary {
  cursor: pointer;
  list-style: none;
  padding: 6px 0;
  font-size: 11.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--af-kg-700);
}
.tj-co-notes > summary::-webkit-details-marker { display: none; }
.tj-co-notes > summary::before { content: "+ "; }
.tj-co-notes[open] > summary::before { content: "− "; }

/* ── Selected vs unselected choice cards  (#2111 follow-up) ────────────────
   The prototype draws every selectable list — addresses, slots, payment rows —
   with the SAME two-state pair (index.html:1577):

     const pill  = "border-kg-600 bg-kg-50 text-kg-700";   // selected
     const plain = "border-black/10 bg-white";             // unselected

   The shared `globals.css` collapses that pair for this tenant. Unselected
   reads `background: var(--lm-bg-1)` and selected `var(--lm-accent-tint)`, and
   this tenant maps BOTH to `--af-kg-50` — so every card, chosen or not, came
   out the same pale green and only the border moved. On the address list that
   is the one thing the row exists to say: which address the groceries are
   going to.

   Unselected therefore goes white on a hairline, selected keeps the green wash
   and takes the green border and ink the pill specifies. Slots and payment
   rows use the same class and get the same pair, which is exactly how the
   prototype treats them. */
.tj-co-choice {
  background: var(--af-white);
  border-color: var(--af-line-strong);
}
.tj-co-choice.is-active {
  background: var(--af-kg-50);
  border-color: var(--af-kg-600);
  color: var(--af-kg-700);
}

/* The label chip beside the name. `--lm-surface-hi` is `--af-kg-50` for this
   tenant, which is now precisely the SELECTED card's fill — the tag would have
   disappeared into the card it sits on. The prototype fills it `bg-cream`
   against both states. */
.tj-co-tag { background: var(--af-cream); }

/* The payment row's right-hand perk (#2111 follow-up). `globals.css` paints it
   `--lm-saffron`, which is this tenant's ACCENT — the same green as the
   selected card it sits inside, so the one thing on the row meant to catch the
   eye blended into the row. The prototype paints it `text-chilli`
   (index.html:857), a deliberate contrast against the green list. */
.tj-co-perk { color: var(--af-chilli); }

/* ==========================================================================
   /account/register — the prototype's centred card  (#2113)
   --------------------------------------------------------------------------
   The shared split auth layout (apps/storefront/src/components/auth/
   auth-split-layout.tsx) draws a brand panel beside the form card and ships
   its own `<style>` block. That shape is right for THIS tenant's sign-in —
   the client signed it off from the first screenshot — but its register
   screen (templates/FreshDays/index.html:961) is a different composition
   entirely: no panel, one card centred at `max-w-[720px]`.

   The app renders that on a `tijaro-auth-register-layout: centered`
   declaration in this bundle's site-layout, which adds `.tja-solo` to the
   wrapper and omits the panel. It declares NO `.tja-solo` rules of its own,
   deliberately: they would be bytes served to every tenant on that surface
   for a shape only this one renders. So the rules live here, in the
   stylesheet that loads for this tenant alone, and every other storefront's
   register page stays byte-identical.

   SPECIFICITY, not order. The app's block is a <style> in <body>; this file
   is a <link> in <head>, so at equal specificity the app wins on document
   order — the opposite of the `.lm-*` collision above. Every rule below is
   therefore two classes deep (`.tja-solo .tja-x`, 0-2-0) against the app's
   one (0-1-0), which wins irrespective of order and needs no !important.
   The app's `@media (min-width: 768px)` rules are also 0-1-0, so each
   breakpoint override below is restated at the same two-class depth.
   ========================================================================== */

/* `mx-auto max-w-[720px]` — and one column, so the card is not left sitting
   in the left half of the panel grid it no longer has a panel for. */
.tja-solo .tja-split {
  max-width: 720px;
  grid-template-columns: minmax(0, 1fr);
}

/* `p-6 md:p-8`. The card's `rounded-2xl border-black/5 bg-white` already comes
   from the app's `.tja-split`, which is exactly the prototype's card. */
.tja-solo .tja-card { padding: 24px; }
@media (min-width: 768px) {
  .tja-solo .tja-card { padding: 32px; }
}

/* `h-[3px] w-24 rounded` on #C1372B. The panel's rule stays gold (the
   `tijaro-auth-accent` declaration) because the SIGN-IN page draws it and that
   page is signed off; the register card's own rule is chilli, as drawn. */
.tja-solo .tja-rule {
  width: 96px;
  background: var(--af-chilli);
}

/* `font-display text-[24px] font-extrabold` — the app's is 22px. */
.tja-solo .tja-title { font-size: 24px; }

/* `mt-5` between the sub-line and the field grid. */
.tja-solo .tja-form { margin-top: 20px; }

/* `sm:w-auto sm:px-10` — full width on a phone, hugging its label above 640px.
   `justify-self` is the operative property, NOT width: `.tja-form` is a grid,
   so a grid item stretches to its track and `width: auto` alone still renders
   full-bleed (measured — it came out 654px). The prototype's hover is a flat
   kg-700 rather than the app's brightness filter. */
@media (min-width: 640px) {
  .tja-solo .tja-submit {
    width: auto;
    justify-self: start;
    padding-left: 40px;
    padding-right: 40px;
  }
}
.tja-solo .tja-submit:hover:not(:disabled) {
  background: var(--af-kg-700);
  filter: none;
}

/* `rounded-xl bg-gold-100/60 p-3 text-[11.5px] font-bold text-gold-600` — a
   solid pink panel, NOT the app's dashed accent outline. This tenant's "gold"
   ramp is a warm red, so gold-100 is the pink the client's screenshot shows
   and gold-600 the dark red on it. */
.tja-solo .tja-gift {
  border: none;
  border-radius: 12px;
  /* --af-gold-100 (#FBE3DF) at 60%, written out rather than mixed. `color-mix`
     computes to `color(srgb …)` in Chromium, so the rendered value stops being
     comparable to the `rgba()` the prototype states and the spec asserts —
     measured before pinning it. No @supports fallback is needed either. */
  background: rgba(251, 227, 223, 0.6);
  padding: 12px;
  font-weight: 700;
  color: var(--af-gold-600);
}

/* The postcode field's serviceability answer. The app ships no rule for
   `.tja-hint` at all — it rides `.tja-note`'s muted small print everywhere
   else — so this is purely the tenant's colouring of a yes and a no. */
.tja-solo .tja-hint { font-weight: 700; }
.tja-solo .tja-hint[data-state="serves"] { color: var(--af-kg-700); }
.tja-solo .tja-hint[data-state="unserved"] { color: var(--af-gold-600); }

/* ==========================================================================
   /account/* — the client's own account screen  (templates/FreshDays/index.html:990-1046)
   --------------------------------------------------------------------------
   The tabbed account area is shared by every tenant that declares
   `tijaro-account-layout: template`. Its structure lives in
   `apps/storefront/src/components/account/account-shell.tsx` and
   `src/app/account/profile/page.tsx`; its SHAPE is this client's, and this is
   the only stylesheet it may live in — `/cms/assets/theme.css` is served per
   slug, so every rule below reaches Asian Fresh and nobody else.

   Two things this block does that a token re-point cannot:

     1. `globals.css` draws the account area in Asharaf's proportions —
        24px display balance, 10.5px micro-caps card titles, a 46px avatar,
        `--lm-radius-sm` (10px) tabs. Those are values the rules HARD-CODE, so
        pointing `--lm-*` at the Kerala ramp only recoloured them.
     2. Every icon on this surface is drawn from HERE, as generated content on
        a `data-*` hook the shared component provides. They are deliberately
        NOT markup: a Material Symbols ligature emitted by a shared component
        paints as the raw word "person" for any tenant whose chrome loads no
        icon font, and the shared layer cannot know which tenants do. This
        bundle's site-layout loads the face (line 223), so the glyphs resolve
        here and nowhere else.

   PROVENANCE: every value is measured off the prototype's own classes, named
   per rule. Three deviations are marked `DEVIATION:` where they occur — a tap
   target, a focus outline and a step label — each with the reason.
   ========================================================================== */

/* FULL-BLEED, like every other page in this shop.

   The prototype's page shell is `<main class="w-full px-3 md:px-6">`
   (index.html:104) — no max-width anywhere, so the account screen runs to the
   viewport edge less 24px, and at 1900px its three stat tiles are wide.
   `globals.css` caps `.tj-acct-inner` at 1120px, which is a fine neutral
   default but not this client's: it left the profile stopping short while
   `/cart` (`.tj-cart-inner { width: 100% }`) already ran full width, so two
   pages of the same shop disagreed. Measured at 1600px viewport: cart inner
   x=24 w=1552, account inner 1120 — this closes that gap.

   The cap stays in `globals.css` for any future tenant that wants it. */
.tj-acct-inner { max-width: none; }
.tj-acct { padding-left: 24px; padding-right: 24px; }
@media (max-width: 767.98px) {
  /* The prototype narrows to `px-3` below `md`. */
  .tj-acct { padding-left: 12px; padding-right: 12px; }
}

/* The icon face, for the generated content below. Mirrors `.ms` above, which
   is the same font this bundle's own CMS pages draw with — ligature
   substitution applies to generated content exactly as it does to a text node. */
.tj-acct-tab::before,
.tj-acct-tab::after,
.tj-acct-signout::before,
.tj-acct-stat::before,
.tj-acct-track-step::before {
  font-family: 'Material Symbols Rounded';
  font-weight: 400;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  direction: ltr;
  font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 24;
  -webkit-font-feature-settings: 'liga';
  font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  user-select: none;
}

/* ── sidebar ─────────────────────────────────────────────────────────────── */

/* `h-12 w-12` — globals.css draws 46px. */
.tj-acct-avatar { width: 48px; height: 48px; }

/* The FreshCoins card. `flex items-center justify-between rounded-xl p-3` on
   `linear-gradient(120deg,#0F5C2C 0%,#1B9448 100%)` — kg-800 into kg-600.
   globals.css draws a 10px-radius GRID running the accent into its soft stop,
   which for this tenant is kg-600 → kg-700: the same gradient, backwards, in
   the wrong shape, with no room for the Redeem control beside it. */
.tj-acct-balance {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px;
  border-radius: 12px;
  background: linear-gradient(120deg, var(--af-kg-800) 0%, var(--af-kg-600) 100%);
}
/* `flex flex-col leading-tight` — the label and the balance as one column so
   the Redeem control can sit opposite them. */
.tj-acct-balance-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
/* `text-[10px] font-extrabold uppercase tracking-widest text-gold-200`. The
   colour is stated rather than left as globals.css's 85%-opacity white: this
   tenant's kasavu-gold is what the prototype puts on the green. */
.tj-acct-balance-k {
  font-weight: 800;
  letter-spacing: .1em;
  color: var(--af-gold-200);
  opacity: 1;
}
/* `font-display text-[20px] font-extrabold` — globals.css draws 24px. The
   weight already comes from the display-face block above. */
.tj-acct-balance-v { font-size: 20px; }
/* `rounded-lg bg-gold-400 px-2.5 py-1.5 text-[10.5px] font-extrabold uppercase
   tracking-wider text-white`. The shared layer declares NOTHING for this class
   — it is a control only this template draws — so every property is here.
   DEVIATION: vertical padding is 8px rather than the prototype's `py-1.5`
   (6px), which holds the tap target at 32px inside a 12px-padded card instead
   of 28px. The same trade the `.lm-btn` block above records. */
.tj-acct-redeem {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  border-radius: 8px;
  background: var(--af-gold-400);
  padding: 8px 10px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--af-white);
  text-decoration: none;
}
.tj-acct-redeem:hover { background: var(--af-gold-600); }

/* Tab rows: `flex items-center gap-2.5 rounded-xl px-3 py-2.5 text-[12.5px]
   font-bold text-ink/80`. globals.css draws 10px radius, 8px/12px padding and
   the muted ink it uses for secondary text. */
.tj-acct-tab {
  gap: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  color: rgba(27, 43, 35, .8);
}
/* `bg-kg-50 text-kg-700` — globals.css tints the background correctly for this
   tenant already, but paints the label with the accent (kg-600). */
.tj-acct-tab.is-active { color: var(--af-kg-700); }
/* `<span class="ms text-[19px]">{{ t.i }}</span>`, one glyph per row, exactly
   as the prototype's `tabs` table names them (index.html:1590). `marketing`
   has no row in that table — see the note on the Preferences tab in
   `account-shell.tsx` — so it takes Material Symbols' own settings glyph from
   the same family the rest of this design is drawn in. */
.tj-acct-tab::before { font-size: 19px; }
.tj-acct-tab[data-tab="profile"]::before { content: "person"; }
.tj-acct-tab[data-tab="orders"]::before { content: "receipt_long"; }
.tj-acct-tab[data-tab="addresses"]::before { content: "location_on"; }
.tj-acct-tab[data-tab="wishlist"]::before { content: "favorite"; }
.tj-acct-tab[data-tab="loyalty"]::before { content: "savings"; }
.tj-acct-tab[data-tab="marketing"]::before { content: "tune"; }
.tj-acct-tab[data-tab="help"]::before { content: "support_agent"; }
/* `<span class="ms ml-auto text-[16px] opacity-40">chevron_right</span>` — the
   same affordance on every row, so it is one rule rather than seven. */
.tj-acct-tab::after {
  content: "chevron_right";
  margin-left: auto;
  font-size: 16px;
  opacity: .4;
}

/* `flex w-full items-center justify-center gap-1.5 rounded-xl border
   border-black/10 py-2.5 text-[12px] font-extrabold uppercase tracking-wider
   text-mud` with a `logout` glyph at 17px. */
.tj-acct-signout {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-radius: 12px;
  font-size: 12px;
  letter-spacing: .06em;
}
.tj-acct-signout::before { content: "logout"; font-size: 17px; }
/* `hover:border-chilli hover:text-chilli`. globals.css moves the LABEL only,
   and to `--lm-ember`, which this tenant maps to the warm red of its gold
   ramp rather than to its chilli. Same selector as the shared rule, so this
   wins on document order without `!important`. */
.tj-acct-signout:hover:not(:disabled) {
  border-color: var(--af-chilli);
  color: var(--af-chilli);
}

/* ── body cards ──────────────────────────────────────────────────────────── */

/* `text-[12.5px] font-extrabold uppercase tracking-wider text-mud` — the card
   kicker. globals.css draws a 10.5px mono micro-cap, which is Asharaf's. */
.tj-acct-card-title {
  font-family: var(--af-sans);
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: .06em;
}

/* `flex items-center justify-between` — the title row that carries a link. */
.tj-acct-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
/* `text-[11.5px] font-extrabold uppercase tracking-wider text-kg-600`. */
.tj-acct-card-link {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--af-kg-600);
  text-decoration: none;
  white-space: nowrap;
}

/* Stat tiles: `rounded-2xl border border-black/5 bg-white p-4`, an icon in
   kg-600 above a 22px display value above an 11.5px uppercase key. The card
   itself already resolves to that through this tenant's tokens; the padding,
   the icon and the two type sizes do not.

   The glyphs are the prototype's own, tile for tile (index.html:1843-1847):
   `receipt_long` over the order count, `savings` over the money saved,
   `set_meal` over the collected orders. The pickup tile renders only while
   this tenant's `features.pickup` switch is on, and the savings tile only
   while the stats endpoint answers — the `auto-fit` grid closes the gap when
   either is absent, so a shorter row is a layout the design already handles
   rather than one to patch around.

   There is deliberately NO rewards-balance tile: the sidebar card prints that
   number a few inches up the same screen. */
.tj-acct-stat { padding: 16px; }
.tj-acct-stat::before { font-size: 20px; color: var(--af-kg-600); margin-bottom: 4px; }
.tj-acct-stat[data-stat="orders"]::before { content: "receipt_long"; }
.tj-acct-stat[data-stat="savings"]::before { content: "savings"; }
.tj-acct-stat[data-stat="pickup"]::before { content: "set_meal"; }
/* `font-display text-[22px] font-extrabold leading-none` — globals.css: 24px. */
.tj-acct-stat-v { font-size: 22px; }
/* `text-[11.5px] font-bold uppercase tracking-wider` — globals.css: 11px/800/.08em. */
.tj-acct-stat-k { font-size: 11.5px; font-weight: 700; letter-spacing: .06em; }

/* ── profile details, as a real form ─────────────────────────────────────── */

/* `mt-3 grid gap-3 sm:grid-cols-2`. The shared layer draws none of these
   classes — the form is a control this template asks for and the platform's
   account pages never had — so each property is stated here in full. */
.tj-acct-form { display: grid; gap: 12px; }
.tj-acct-fields { display: grid; gap: 12px; }
@media (min-width: 640px) {
  .tj-acct-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* `grid gap-1.5` with `text-[11px] font-extrabold uppercase tracking-wider
   text-mud` on the caption. */
.tj-acct-field { display: grid; gap: 6px; min-width: 0; }
.tj-acct-field-k {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--af-mud);
}

/* `rounded-xl border border-black/10 px-3.5 py-2.5 text-[13px] font-semibold`.
   The border is written as LONGHANDS on purpose: a `border` shorthand carrying
   a `var()` leaves pending-substitution longhands that outlive a later
   `border-color`, which is exactly why `.lm-input:focus`'s border change has
   always been inert (see the note on that rule above). Stated this way, the
   focus rule below actually applies. */
.tj-acct-input {
  width: 100%;
  border-width: 1px;
  border-style: solid;
  border-color: var(--af-line-strong);
  border-radius: 12px;
  background: var(--af-white);
  padding: 10px 14px;
  font-family: var(--af-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--af-ink);
  line-height: 1.5;
}
/* `focus:border-kg-600`. DEVIATION: the prototype also sets `outline-none`,
   which is not adopted — this bundle's `:focus-visible { outline: 2px solid
   var(--af-kg-600) }` is the keyboard-focus indicator and removing it to match
   a class name would be shipping an accessibility regression to win nothing
   visible. */
.tj-acct-input:focus { border-color: var(--af-kg-600); }
/* The email field. It is rendered because the template draws it, and locked
   because `PUT /api/customer/me` cannot change it — so it must not LOOK like a
   field that accepts what somebody types. The prototype has no such state, so
   this is derived from its own tokens: the cream inset it uses for the
   basket's coupon box, in the muted ink it uses for secondary text. */
.tj-acct-input.is-locked {
  background: var(--af-cream);
  color: var(--af-mud);
  cursor: default;
}
.tj-acct-field-note {
  font-size: 11px;
  font-weight: 600;
  color: var(--af-mud);
  line-height: 1.4;
}
.tj-acct-field-error {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--af-chilli);
  line-height: 1.4;
}

/* `mt-3 rounded-xl bg-kg-600 px-6 py-2.5 text-[12.5px] font-extrabold
   uppercase tracking-wider text-white`. DEVIATION: vertical padding is 12px,
   not the prototype's `py-2.5` (10px) — 12px is what holds this control above
   the 44px minimum target, the same trade recorded on `.lm-btn` above. */
.tj-acct-save {
  justify-self: start;
  /* Measured, not assumed: 12px of padding around a 12.5px label lands the
     control at 41px in Chromium. The shared layer has no rule to inherit a
     minimum from — unlike `.lm-btn`, which globals.css guards below 760px —
     so the floor is stated here. */
  min-height: 44px;
  border: none;
  border-radius: 12px;
  background: var(--af-kg-600);
  padding: 12px 24px;
  font-family: var(--af-sans);
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--af-white);
  cursor: pointer;
}
.tj-acct-save:hover:not(:disabled) { background: var(--af-kg-700); }
.tj-acct-save:disabled { opacity: .6; cursor: progress; }
@media (max-width: 639.98px) {
  /* Full width on a phone, as every other commit control in this design is. */
  .tj-acct-save { justify-self: stretch; width: 100%; }
}

.tj-acct-form-status { margin: 0; font-size: 12px; font-weight: 700; color: var(--af-kg-700); }
.tj-acct-form-status.is-error { color: var(--af-chilli); }

/* ── recent-order tracker ────────────────────────────────────────────────── */

/* `mt-3 grid gap-3 sm:grid-cols-4`, each step `rounded-xl border p-3`. */
.tj-acct-track { display: grid; gap: 12px; }
@media (min-width: 640px) {
  .tj-acct-track { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.tj-acct-track-step {
  display: grid;
  gap: 4px;
  justify-items: start;
  border-width: 1px;
  border-style: solid;
  border-radius: 12px;
  padding: 12px;
}
/* `<span class="ms text-[18px]">`, one glyph per stage, named exactly as the
   prototype's own tracker names them (index.html:1810-1814). */
.tj-acct-track-step::before { font-size: 18px; }
.tj-acct-track-step[data-stage="placed"]::before { content: "check_circle"; }
.tj-acct-track-step[data-stage="packed"]::before { content: "inventory_2"; }
.tj-acct-track-step[data-stage="transit"]::before { content: "local_shipping"; }
.tj-acct-track-step[data-stage="delivered"]::before { content: "home"; }
/* The prototype's three states, verbatim: done `border-kg-600 bg-kg-50
   text-kg-700`, in flight `border-gold-400 bg-gold-100 text-gold-600`, ahead
   `border-black/10 text-mud`. */
.tj-acct-track-step[data-state="done"] {
  border-color: var(--af-kg-600);
  background: var(--af-kg-50);
  color: var(--af-kg-700);
}
.tj-acct-track-step[data-state="now"] {
  border-color: var(--af-gold-400);
  background: var(--af-gold-100);
  color: var(--af-gold-600);
}
.tj-acct-track-step[data-state="todo"] {
  border-color: var(--af-line-strong);
  color: var(--af-mud);
}
/* `mt-1 text-[11.5px] font-extrabold` over `text-[10.5px] font-semibold
   opacity-70`. The timestamp appears on the placed step only — it is the one
   time `CustomerOrderSummaryDto` carries. */
.tj-acct-track-label { font-size: 11.5px; font-weight: 800; line-height: 1.3; }
.tj-acct-track-when { font-size: 10.5px; font-weight: 600; opacity: .7; line-height: 1.3; }

/* ==========================================================================
   ACCOUNT — the other four tabs  (#2126)
   ==========================================================================
   The block above dressed `/account/profile`. The client then looked at the
   rest of the profile view and rejected it: My orders, Addresses, Wishlist and
   FreshCoins were still wearing the platform's neutral shapes — Asharaf's pill
   chips, its mono micro-caps, its card proportions — under this tenant's
   colours. Same rule as before, and the same reason it lives HERE: every
   selector below reaches Asian Fresh and nobody else, because
   `/cms/assets/theme.css` is served per slug.

   PROVENANCE: every value is measured off the prototype's own Tailwind classes
   (`tijaro-storefront/templates/FreshDays/index.html:1048-1160`), named per
   rule. Deviations are marked `DEVIATION:` where they occur, with the reason.

   WHAT IS DELIBERATELY NOT DRAWN, because the platform has no data for it:
     · the address card's phone row — `CustomerDeliveryAddress` has no
       per-address number, and the ACCOUNT's is a different fact.
   It is an ABSENT element rather than an approximated one, so nothing here
   styles a placeholder into looking like data.

   The delivered order's "Rate items" primary used to sit on that list —
   customer reviews were approved and unbuilt, and a button that no-ops is worse
   than an absent one. As of #2138 the endpoint exists and the control is real,
   so it is drawn in full below (`.tj-rate*`). The prototype never draws a star
   control, a comment box or a nested form, so every value in that block is
   derived from THIS client's own tokens and named per rule — none of it is
   another client's shape repainted.

   The order card's thumbnail strip, its "6 items · Fish, rice, veg" caption and
   its Reorder / Invoice actions used to head that list — `CustomerOrderSummaryDto`
   carried `itemCount` and no lines, and neither action had an endpoint. As of
   #2134/#2135/#2136 all of it is real: the row carries `items`, `totalQuantity`
   and `categoryNames`, and both actions have routes. The card is drawn in full
   below, including what the API can honestly answer with nothing — an empty
   category list prints the count alone, and a null photo gets this shop's own
   empty-media square rather than an invented placeholder.

   The wishlist card's rating chip, pack size, was-price, offer flash, brand
   and note used to be on that list — `WishlistItem` carried none of them. As
   of #2130 it carries all six, so the saved tile is the shop card in full and
   its rules live with the card, up at `.af-card`. See the Wishlist section
   below for what is left over.
   ========================================================================== */

/* The icon face for this block's generated content — same declaration the
   profile block above makes, extended to the hooks these four tabs add. */
.tj-addr-card::before,
.tj-addr-new::before,
.tj-rate-star-g::before,
.tj-wish-remove::before,
.tj-wish-rating::before,
.tj-wish-add::before,
.tj-wish-qty-btn::before {
  font-family: 'Material Symbols Rounded';
  font-weight: 400;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  direction: ltr;
  font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 24;
  -webkit-font-feature-settings: 'liga';
  font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  user-select: none;
}

/* ── My orders ───────────────────────────────────────────────────────────── */

/* Card rows sit `mt-3` apart (12px) on every card this design draws — the
   order card, the profile-details card and the recent-order card
   (index.html:1023, 1036, 1057). globals.css draws 10px for all of them.

   The rewards ledger is the one exception: its rows open `mt-2`
   (index.html:1155), 8px. Selected on the ledger it CONTAINS rather than on a
   class added to the markup for this tenant's benefit — `:has()` is already how
   four other rules in this stylesheet pick a card out by its content. */
.tj-acct-card { gap: 12px; }
.tj-acct-card:has(.tj-ledger) { gap: 8px; }

/* `rounded-lg bg-cream px-2 py-1 text-[11px] font-extrabold tracking-wider`.
   globals.css draws a 6px radius at weight 700 on `--lm-bg-1`, which this
   tenant maps to the pale GREEN of its ramp — the prototype's chip is cream. */
.tj-acct-ref {
  border-radius: 8px;
  background: var(--af-cream);
  padding: 4px 8px;
  font-weight: 800;
  letter-spacing: .05em;
}

/* The status chip. `rounded-lg px-2 py-1 text-[10.5px] font-extrabold uppercase
   tracking-wider` plus a tone.

   globals.css draws `border-radius: 999px` with a 1px border — Asharaf's pill,
   which is exactly the shape this design does not use. The tones come off the
   `data-tone` hook the page emits, which is read from the SAME status table the
   tracker below uses, so a chip and a tracker can never disagree about an
   order. */
.tj-acct-pill {
  border: none;
  border-radius: 8px;
  padding: 4px 8px;
  letter-spacing: .05em;
}
/* `bg-kg-50 text-kg-700` — the prototype's delivered order (index.html:1454). */
.tj-acct-pill[data-tone="done"] { background: var(--af-kg-50); color: var(--af-kg-700); }
/* `bg-gold-100 text-gold-600` — the prototype's out-for-delivery order (:1453). */
.tj-acct-pill[data-tone="live"] { background: var(--af-gold-100); color: var(--af-gold-600); }
/* The prototype draws no cancelled order, so this is derived from its own
   tokens rather than borrowed: the chilli it uses for a destructive action, on
   a tint of itself. Never the gold treatment — a cancelled order is not in
   flight — and never the green one. */
.tj-acct-pill[data-tone="cancelled"] {
  background: rgba(193, 55, 43, .1);
  color: var(--af-chilli);
}
/* A status this build does not recognise makes no claim at all. */
.tj-acct-pill[data-tone="unknown"] { background: var(--af-cream); color: var(--af-mud); }

/* `ml-auto font-display text-[17px] font-extrabold` — globals.css draws 19px.
   The weight and the face come from the display block above. */
.tj-acct-order-total { font-size: 17px; }

/* `text-[11.5px] font-semibold text-mud` — the caption beside the thumbnail
   strip (index.html:1062): "6 items · Fish, rice, veg". globals.css draws
   12.5px at the inherited weight.

   `self-center pl-1` in the prototype — the 4px inset separates it from the
   last square, so it applies only when there IS a square before it. An order
   read from an API build with no line data renders the caption alone, flush. */
.tj-acct-order-meta { font-size: 11.5px; font-weight: 600; }
.tj-acct-thumbs + .tj-acct-order-meta { padding-left: 4px; }

/* The strip itself: `mt-3 flex gap-2 overflow-x-auto noscroll` (index.html:1057).
   `noscroll` is the prototype's own scrollbar-hiding pair, declared at :22 — a
   visible scrollbar under four 56px squares is chrome the design never draws.

   The 12px top gap comes from `.tj-acct-card { gap: 12px }` above, which is why
   the shared `.af-pdp-thumb*` container rule is not reused here. */
.tj-acct-order-items { gap: 8px; }
.tj-acct-thumbs { scrollbar-width: none; }
.tj-acct-thumbs::-webkit-scrollbar { display: none; }

/* `h-14 w-14 rounded-lg` — what differs from the gallery square this shares its
   shape with. TWO CLASSES ON PURPOSE: a bare `.tj-acct-thumb` ties with the
   shared block on specificity and would be decided by source order, which is
   the exact trap `.af-dt-go` fell into further up this file.

   DEVIATION, stated rather than hidden: the gallery square rounds to
   `--af-radius-sm` (10px, this bundle's small-radius token) where the prototype
   writes `rounded-lg` for BOTH strips. 8px is the prototype's value and it is
   what the ref chip and the status pill in this very card already use, so a
   10px square beside them would be visibly odd. The gallery's 10px is
   pre-existing and is not this slice's to move. */
.tj-acct-thumbs .tj-acct-thumb { height: 56px; width: 56px; border-radius: 8px; }
/* The empty-media tile is the shop's own (grouped onto `.af-card-ph`); only its
   initial is re-sized, because 34px is drawn for a card's 1:1 media. */
.tj-acct-thumbs .tj-acct-thumb-ph { font-size: 20px; }
/* "+7". The prototype's own data never overflows four, so this is derived from
   its tokens rather than copied: the strip's cream square, the caption's muted
   ink, at the caption's weight. */
.tj-acct-thumbs .tj-acct-thumb.is-more {
  display: grid;
  place-items: center;
  font-size: 12px;
  font-weight: 800;
  color: var(--af-mud);
}

/* Order actions. Primary `rounded-xl bg-kg-600 px-4 py-2 text-[11.5px]
   font-extrabold uppercase tracking-wider text-white`; secondary the same in
   `border border-black/10 text-ink/80`, hovering to kg-600 — except the help
   action, which the prototype hovers to chilli.

   globals.css draws a 10px radius and paints the secondary in full-strength
   ink. DEVIATION: `min-height: 40px` is left as globals.css sets it rather than
   reduced to the prototype's ~33px — the same tap-target trade recorded on
   `.lm-btn` and `.tj-acct-save` above. */
.tj-acct-action {
  border-radius: 12px;
  letter-spacing: .05em;
  color: rgba(27, 43, 35, .8);
}
.tj-acct-action:hover { border-color: var(--af-kg-600); }
.tj-acct-action.is-primary { color: var(--af-white); }
.tj-acct-action.is-help:hover { border-color: var(--af-chilli); color: var(--af-chilli); }
/* Reorder is the row's only BUTTON. The prototype draws it identically to the
   two links beside it (index.html:1066), and globals.css already strips the
   UA's button chrome — so all that is left is the border this design gives a
   secondary action, which a button does not inherit from `.tj-acct-action`'s
   own `border` shorthand any differently to an anchor. Stated for the disabled
   state only, which the prototype has no equivalent for and which is derived
   from its own tokens: the same muting `.tj-acct-save:disabled` uses. */
button.tj-acct-action:disabled { border-color: var(--af-line); color: var(--af-mud); }

/* ── What Reorder reports  (#2135) ─────────────────────────────────────────
   The prototype draws no such panel — its Reorder button is inert — so every
   value here comes off ITS OWN tokens rather than from another client's
   surface. The card it sits in is `rounded-2xl border border-black/5 bg-white`;
   this is the same idea one level in, on the cream this design uses for a
   nested surface (the ref chip, the thumbnail square).

   Nothing is toned as an error unless the shop actually could not sell it: a
   reorder that adds nothing because everything was already in the basket is a
   SUCCESS, and the endpoint is idempotent precisely so a second tap is
   harmless. Only `Unavailable` and `NeedsConfiguration` take the chilli. */
.tj-acct-reorder {
  border-color: rgba(0, 0, 0, .06);
  border-radius: 12px;
  background: var(--af-cream);
}
.tj-acct-reorder[data-state="error"] { border-color: var(--af-chilli); }
.tj-acct-reorder-head { font-size: 12.5px; font-weight: 800; }
.tj-acct-reorder-line { font-size: 11.5px; }
.tj-acct-reorder-name { font-weight: 800; }
.tj-acct-reorder-note { color: var(--af-mud); }
.tj-acct-reorder-line[data-outcome="Unavailable"] .tj-acct-reorder-note,
.tj-acct-reorder-line[data-outcome="NeedsConfiguration"] .tj-acct-reorder-note {
  color: var(--af-chilli);
}
/* `text-[11.5px] font-extrabold uppercase tracking-wider text-kg-600` — the
   prototype's own inline text action (its "All orders", index.html:1034). */
.tj-acct-reorder-link,
.tj-acct-reorder-cart {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--af-kg-600);
}
.tj-acct-reorder-link:hover,
.tj-acct-reorder-cart:hover { color: var(--af-kg-700); }

/* ── Rating a delivered order's items  (#2138) ─────────────────────────────
   The prototype's delivered rows lead with "Rate items" (index.html:1455) and
   draw NOTHING behind it — no star control, no comment box, no nested form. So
   there is no class here to "match colours with": every value below is derived
   from this client's own tokens and its own drawn surfaces, and each rule says
   which one.

   THE SHARED LAYER DRAWS NONE OF THESE CLASSES, on purpose. `globals.css`
   carries a neutral baseline for the platform's own account classes, written in
   `--lm-*` — which is Asharaf's Kitchen's design language: pill radii, mono
   micro-caps, its card proportions. Writing one for a control that client never
   asked for, only to override it here, is the "repaint another shop's shapes"
   trap this file exists to avoid. So the properties are stated here in full, the
   same way `.tj-acct-form` and its fields are above, and the same way for the
   same reason. `/cms/assets/theme.css` is served per slug, so every selector
   below reaches Asian Fresh and nobody else — and the tabbed account layout it
   sits on is opted into by `tijaro-account-layout: template`, which today is
   this tenant alone.

   WHAT IS REUSED RATHER THAN RESTATED, because this design already draws it:
     · the thumbnail SQUARE — `.tj-acct-thumb`, shared with the gallery and the
       order card's own strip; only the size differs;
     · the comment box — `.tj-acct-field` / `.tj-acct-field-k` /
       `.tj-acct-input`, the profile form's own controls, already measured off
       `index.html:1024-1027`. A `<textarea>` carrying the input's class picks up
       every one of those rules, so only the two properties a multi-line box
       needs are added;
     · the commit control — `.tj-acct-save`, the prototype's green
       `rounded-xl bg-kg-600` button;
     · the "Rated" tag — `.tj-acct-pill[data-tone="done"]`, i.e. `bg-kg-50
       text-kg-700`, the very treatment the prototype gives a delivered order
       (:1454). A second chip shape would be this design disagreeing with itself.
   ------------------------------------------------------------------------- */

/* The panel. Same idea as the reorder report above: a nested surface on the
   cream this design uses one level inside a white card (the ref chip, the
   thumbnail square), at the 12px radius its inner panels take. */
.tj-rate {
  display: grid;
  gap: 12px;
  min-width: 0;
  border: 1px solid var(--af-line);
  border-radius: 12px;
  background: var(--af-cream);
  padding: 12px;
}

/* `text-[12.5px] font-extrabold` — the panel title, as `.tj-acct-reorder-head`
   and the prototype's own `.af-pdp-card-title` (:1050 family) are. */
.tj-rate-head { margin: 0; font-size: 12.5px; font-weight: 800; line-height: 1.45; }
/* `text-[11.5px] font-semibold text-mud` — this design's caption, the same one
   the order card's item line uses. */
.tj-rate-sub { margin: 0; font-size: 11.5px; font-weight: 600; line-height: 1.5; color: var(--af-mud); }

.tj-rate-items { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }

/* One line's row. The prototype's card is `rounded-2xl border border-black/5
   bg-white p-4`; this is that card one level in — white on the panel's cream, at
   the inner 12px radius and the 12px padding the rest of this surface uses.
   The glyph-beside-content grid is the address card's own layout idea
   (`.tj-addr-card` below), which is where this design puts a square next to a
   stack of text. */
.tj-rate-item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 10px;
  row-gap: 8px;
  align-items: start;
  border: 1px solid var(--af-line);
  border-radius: 12px;
  background: var(--af-white);
  padding: 12px;
}

/* The shared thumbnail square at this row's size. TWO CLASSES, for the reason
   the strip's own rule states: a bare `.tj-acct-thumb` ties with the shared
   `.af-pdp-thumb` block on specificity and would be decided by source order.
   44px rather than the strip's 56px — the row is a form, not a gallery, and the
   square has to sit level with the product name above the stars. */
.tj-rate-item .tj-acct-thumb { height: 44px; width: 44px; border-radius: 8px; }
/* The empty-media tile's initial, scaled to the smaller square (the strip draws
   20px at 56px, the card 34px at 1:1). */
.tj-rate-item .tj-acct-thumb-ph { font-size: 17px; }

.tj-rate-body { display: grid; gap: 8px; justify-items: start; min-width: 0; }
/* `text-[12.5px] font-extrabold` — this design's card title. */
.tj-rate-name { font-size: 12.5px; font-weight: 800; line-height: 1.35; overflow-wrap: anywhere; }

/* The star row. A `fieldset` carries a UA border, padding, margin AND a
   `min-inline-size: min-content` that stops it shrinking inside a grid column —
   all four have to go or the row cannot narrow on a phone. */
.tj-rate-stars {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  margin: 0;
  padding: 0;
  border: 0;
  min-inline-size: 0;
}
/* The group's accessible name. Hidden, not removed — it is what a screen reader
   announces before the five radios, and the product name on screen is a
   separate element that a mouse user reads instead. */
.tj-rate-legend {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* One star. 32px is the square this design gives a small icon control — the
   size of the wishlist tile's own remove button (`.tj-wish-remove`) and one step
   up from the 28px stepper it draws inside a button — at the 8px radius the
   chips and squares in this card already use. Five of them make a 170px row that
   still fits the narrowest phone column this grid produces. */
.tj-rate-star {
  position: relative;
  display: grid;
  place-items: center;
  height: 32px;
  width: 32px;
  border-radius: 8px;
  cursor: pointer;
  /* An unchosen star. Derived, not borrowed: the muted ink this design writes
     secondary text in, at the strength `.af-pdp-mrp` uses for a struck price. */
  color: rgba(107, 98, 87, .45);
}
.tj-rate-star-g::before { content: "star"; font-size: 21px; }
/* A chosen star is kg-600 — the colour this shop already draws a RATING in
   (its rating chip is `bg-kg-600` with a white star, index.html:550), so the
   stars and the chip cannot disagree about what a rating looks like. */
.tj-rate-star[data-on="1"] { color: var(--af-kg-600); }
/* Hover is the pale green this design tints a hovered control with. */
.tj-rate-star:hover { background: var(--af-kg-50); }

/* The radio itself covers its star and is invisible — but it is still the thing
   that is focused, checked and arrow-keyed, which is the whole point of using
   one. `opacity: 0` hides its own focus ring along with it, so the ring is drawn
   on the label instead; `:has()` is already how four other rules in this file
   pick a parent out by its content. */
.tj-rate-radio {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.tj-rate-star:has(.tj-rate-radio:focus-visible) {
  outline: 2px solid var(--af-kg-600);
  outline-offset: 2px;
}
/* Each star's own label ("3 stars"). Hidden, not removed: it is the radio's
   accessible name, and the glyph beside it is generated content on an
   `aria-hidden` element precisely so it cannot join that name. */
.tj-rate-star-t {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The comment box. `.tj-acct-input` already gives it the prototype's field
   shape; a multi-line box needs a floor and a grab handle, and the prose weight
   this design writes a paragraph in (`.tj-addr-line`, `font-medium
   leading-relaxed`) rather than a single-line field's semibold. */
.tj-rate-note { min-height: 62px; resize: vertical; font-weight: 500; line-height: 1.6; }
/* The field fills its row. `.tj-rate-body`'s `justify-items: start` — which is
   what keeps the name, the tag and the star row at their own widths — sets the
   default `justify-self` of every child, and would otherwise shrink the comment
   box to its caption's width. */
.tj-rate-body > .tj-acct-field { justify-self: stretch; }

/* Save beside Close — the same row the address form's own actions use. */
.tj-rate-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }

/* ── The receipt  (#2136) ──────────────────────────────────────────────────
   `/account/orders/{id}/invoice`. The prototype draws no receipt either, so the
   sheet is assembled from the surfaces it DOES draw: the account card
   (`rounded-2xl border border-black/5 bg-white p-4`), its 12px row rhythm, the
   micro-caps eyebrow its panels use, and the display face for the closing
   total. Not one value is borrowed from another client's document. */
/* `.tj-inv-solo` is deliberately NOT styled here: it is the wrapper for a
   tenant that never opted into the tabbed account layout, and this tenant has.
   A rule for it would be dead CSS in this file. */
.tj-inv { border-color: rgba(0, 0, 0, .06); gap: 12px; }
.tj-inv-ref {
  font-family: var(--af-sans);
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: .05em;
  color: var(--af-mud);
}
.tj-inv-party-name { font-family: var(--af-display); font-weight: 800; }
.tj-inv-party-line { color: var(--af-mud); font-weight: 500; }
.tj-inv-meta-k,
.tj-inv-lines th {
  font-family: var(--af-sans);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .08em;
  color: var(--af-mud);
}
.tj-inv-meta-v { font-weight: 700; }
.tj-inv-line-name { font-weight: 800; }
.tj-inv-line-mods,
.tj-inv-line-note { color: var(--af-mud); font-weight: 500; }
.tj-inv-payment-when { color: var(--af-mud); }
.tj-inv-payment-v { font-weight: 800; }
.tj-inv-balance { color: var(--af-chilli); font-weight: 800; }

/* What the browser actually prints. The shop's chrome is CMS-rendered for this
   tenant, so only this stylesheet knows the names to hide — globals.css can
   drop the account rail and the on-screen buttons and no more. */
@media print {
  /* Every chrome element `site-layout.yml` actually renders — verified against
     the bundle rather than guessed, because a print rule naming a class nobody
     emits is dead CSS that reads as a decision. */
  .af-header,
  .af-catnav,
  .af-drawer,
  .af-drawer-scrim,
  .af-botnav,
  .af-fab,
  .af-footer { display: none !important; }
  .tj-inv { background: #fff; }
}

/* ── Addresses ───────────────────────────────────────────────────────────── */

/* `grid gap-3 sm:grid-cols-2` — a fixed TWO-up, not the auto-fill globals.css
   uses. At 1900px full-bleed, auto-fill packs six narrow cards across a row;
   the design puts two wide ones there. */
.tj-addr-grid { gap: 12px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 640px) {
  .tj-addr-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* `<span class="ms text-[19px] text-kg-600">{{ a.icon }}</span>`, first in the
   head row. Generated content on the `data-label` hook the address book emits —
   the shopper's OWN label, normalised — so the platform never decides what an
   address "is" and this file maps only the labels this shop's customers use.
   Anything else falls to the default pin. */
.tj-addr-card::before {
  content: "location_on";
  font-size: 19px;
  color: var(--af-kg-600);
  grid-row: 1;
  align-self: center;
}
.tj-addr-card[data-label="home"]::before { content: "home"; }
.tj-addr-card[data-label="work"]::before,
.tj-addr-card[data-label="office"]::before { content: "business"; }

/* The glyph is a grid child of the card, so the head row sits beside it. */
.tj-addr-card {
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 8px;
  align-items: start;
}
.tj-addr-card > * { grid-column: 2; }
.tj-addr-head { grid-row: 1; }

/* `rounded bg-cream px-1.5 py-[2px] text-[9.5px] font-extrabold uppercase
   tracking-wider text-mud` — globals.css tints it with `--lm-bg-1`, which is
   this tenant's pale green. */
.tj-addr-tag { background: var(--af-cream); letter-spacing: .05em; }

/* `mt-2 text-[12px] font-medium leading-relaxed text-mud`. */
.tj-addr-line { font-weight: 500; }

/* `rounded-lg border border-black/10 px-3 py-1.5 text-[11px] font-extrabold
   uppercase tracking-wider`, delete hovering to chilli. globals.css draws a
   10px radius. DEVIATION: the 40px minimum height stays, as above. */
.tj-addr-action { border-radius: 8px; letter-spacing: .05em; }
.tj-addr-action:hover:not(:disabled) { border-color: var(--af-kg-600); }
.tj-addr-action.is-danger:hover:not(:disabled),
.tj-addr-action.is-armed {
  border-color: var(--af-chilli);
  color: var(--af-chilli);
}

/* `grid min-h-[150px] place-items-center rounded-2xl border border-dashed
   border-kg-600 bg-white text-kg-600 hover:bg-kg-50`, with an `add_location_alt`
   glyph at 26px over a 12px uppercase label. Everything but the glyph already
   resolves through this tenant's tokens. */
.tj-addr-new { gap: 4px; align-content: center; }
.tj-addr-new::before { content: "add_location_alt"; font-size: 26px; }
.tj-addr-new-label { letter-spacing: .05em; }

/* The add / edit form is the profile form's own controls (`.tj-acct-field`,
   `.tj-acct-input`, `.tj-acct-save`), already drawn from this prototype's
   tokens in the block above — so the only rule it needs is the row that puts
   Save beside Cancel. */
.tj-addr-form-actions { gap: 10px; }

/* ── Wishlist ────────────────────────────────────────────────────────────── */

/* The prototype's wishlist tab draws the SHOP GRID's own product card, verbatim
   (index.html:1097-1142 is a copy of the card at :636). Since #2131 the card's
   rules are SHARED rather than restated — every `.af-card*` block up at line
   1029 carries the saved tile's class beside it — so the two cards cannot drift
   apart again.

   What is left here is only what genuinely differs:

     1. the grid's own column counts (the prototype's wishlist tab is
        `grid-cols-2 sm:grid-cols-3 xl:grid-cols-4`, not the catalogue's
        2/3/4 at 640/1024);
     2. the neutral values `globals.css` sets that the shared block does not
        restate, and that would otherwise show through — a `gap` between flex
        children the card spaces with margins, a 40px minimum on two controls
        the card draws at 36px, and the mono face on the sold-out badge;
     3. the media's link wrapper, which the CMS card has no equivalent for (its
        photo is not a link — the name is);
     4. the GLYPHS. The shared component emits no icon-font ligature, because
        one paints as the raw word "star" for any tenant whose chrome loads no
        icon font. This bundle loads the face, so they are drawn here as
        generated content — the same posture every other icon on this surface
        takes;
     5. the remove control, which is not the catalogue's heart: on a page of
        saved items it is permanently lit and it removes rather than saves. */
.tj-wish-grid {
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 640px) { .tj-wish-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .tj-wish-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* (2) globals.css lays the tile and its body out as GRIDS with a gap; the card
   is a flex column whose children carry their own `margin-top`. Without these
   two the saved tile would run 6px taller between every element than the card
   beside it. */
.tj-wish-tile { gap: 0; }
.tj-wish-body { gap: 0; }

/* (2) globals.css gives both controls a 40px minimum. Measured in Chromium at
   1600px, the card's own Add is 35px and its stepper 36px; a 40px tile control
   beside them is the difference the client would see first. The account area's
   40px tap-target floor is a rule for ROWS of small text actions, and this is
   neither.

   `line-height` is the same story one layer down, and it is why the Add came
   out 36px against the card's 35px. The React routes load the Tailwind Play
   CDN, whose preflight declares `button { font: inherit }` — so the button
   inherits the body's 1.5 and gives its 12px label an 18px line. The CMS pages
   load no such sheet, so the same button keeps the UA's `normal` (≈16.9px).
   Stating `normal` here settles it on the CMS card's value without touching
   the CMS card, which is on the other side of a shared declaration. */
.tj-wish-add { min-height: 0; line-height: normal; }
.tj-wish-qty { min-height: 0; }

/* (2) globals.css centres the media's contents with a grid; the card's is a
   plain block whose image fills it. Identical on screen — the link wrapper is
   100% × 100% either way — but stated so a future diff of the two cards has
   nothing in it. */
.tj-wish-media { display: block; }

/* (2) `.af-soldout` inherits the body's sans; globals.css names the mono face
   explicitly, so it has to be named back. */
.tj-wish-off { font-family: var(--af-sans); }

/* (3) The photo is a link on this card and not on the CMS one, so the anchor
   has to fill the media box rather than shrink to the image. */
.tj-wish-media-link { display: block; width: 100%; height: 100%; }

/* (4) The three glyphs, positioned exactly where the CMS card's markup puts
   them: the star inside the rating chip, the trolley before the Add label, and
   the stepper's own minus and plus. The stepper's Unicode signs are hidden
   rather than removed — the component emits them so a tenant with no icon font
   still gets a usable control. */
.tj-wish-rating::before { content: "star"; }
.tj-wish-add::before { content: "add_shopping_cart"; }
.tj-wish-qty-btn[data-step="dec"]::before { content: "remove"; }
.tj-wish-qty-btn[data-step="inc"]::before { content: "add"; }
.tj-wish-qty-sign { display: none; }

/* (5) `.af-wish.is-on` — the filled heart over the photo. The shared component
   draws a text button; the label is hidden (it stays the control's accessible
   name) and the glyph is generated here.

   Unlike the catalogue's heart this one never fades out on a non-hovering
   card: on a page of saved items it IS the remove control, and a control that
   only appears on hover is no control at all on a touch screen. */
.tj-wish-remove {
  position: absolute;
  right: 12px;
  top: calc(50% - 8px);
  z-index: 2;
  display: grid;
  place-items: center;
  height: 32px;
  width: 32px;
  min-height: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, .95);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .16);
  color: var(--af-chilli);
}
.tj-wish-remove::before { content: "favorite"; font-size: 17px; }
.tj-wish-remove:hover:not(:disabled) { color: var(--af-gold-600); }
/* Hidden, not removed: the button's accessible name is still "Remove". */
.tj-wish-remove-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── FreshCoins ──────────────────────────────────────────────────────────── */

/* `rounded-2xl p-5` on `linear-gradient(120deg,#0F5C2C 0%,#1B9448 60%,#2BB35C
   100%)`. globals.css runs the accent into its soft stop, which for this tenant
   is kg-600 → kg-700: two stops, the wrong way round, and none of them the
   light green this panel ends on.

   The final stop has no `--af-*` token of its own — it is the same shade the
   `--af-grad-backwater` banner gradient ends on, written literally there too
   rather than named. Stated here for the same reason: the prototype's palette
   is ported verbatim and these hex values are not to be "tidied". */
.tj-acct-hero {
  background: linear-gradient(120deg, var(--af-kg-800) 0%, var(--af-kg-600) 60%, #2BB35C 100%);
}
/* `text-[11px] font-extrabold uppercase tracking-[0.18em] text-gold-200` —
   globals.css draws a 10.5px mono micro-cap at 85% white. */
.tj-acct-hero-k {
  font-family: var(--af-sans);
  font-size: 11px;
  font-weight: 800;
  color: var(--af-gold-200);
  opacity: 1;
}
/* `font-display text-[38px] font-extrabold leading-none text-white` —
   globals.css clamps from 30px, so this panel shrank on a phone while the
   prototype holds 38px. The weight comes from the display block above. */
.tj-acct-hero-v { font-size: 38px; }
/* `text-[12px] font-semibold text-white/80` — globals.css: 12.5px at 85%. */
.tj-acct-hero-sub { font-size: 12px; color: rgba(255, 255, 255, .8); opacity: 1; }

/* The ledger. `mt-2 grid gap-1`, each row `flex items-center justify-between
   border-b border-black/5 py-2 text-[12.5px] font-semibold`, the description
   carrying its date inline in `ml-2 text-[11px] font-medium text-mud`, and the
   amount `font-extrabold` — kg-600 for a credit, chilli for a debit. */
.tj-ledger { gap: 4px; }
/* The prototype rules EVERY row, including the last. */
.tj-ledger-row:last-child { border-bottom: 1px solid var(--af-line); }
/* globals.css mutes a debit to secondary ink; this design gives it the shop's
   chilli, which is what tells a spend from an earn at a glance. */
.tj-ledger-row[data-kind="debit"] .tj-ledger-v { color: var(--af-chilli); }
/* The pager is the platform's own control — the prototype's history card shows
   four rows and never pages — so it is typed as this design's small action
   rather than left in Asharaf's mono. */
.tj-ledger-page { font-family: var(--af-sans); letter-spacing: .05em; }

/* ==========================================================================
   How a line travels  (#2174)
   --------------------------------------------------------------------------
   "Posted UK-wide" / "Van delivery only", on the product page's meta row and
   on a catalogue card. The prototype draws NO such control — it has no notion
   of postage — so there is no shape here to copy and none is borrowed: every
   value below is this client's own token, on this client's own stylesheet,
   which no other tenant loads.

   What it IS derived from is the prototype's own badge vocabulary. Its cards
   carry exactly two small status pills and both share one shape — a `rounded-md`
   micro-caps chip at 9-10px, bold, wide-tracked (ProductCard.dc.html):

     off:  bg-chilli … text-[10px] font-extrabold leading-none text-white
     tag:  bg-kg-800/90 … text-[9px] font-bold uppercase tracking-wider text-gold-200

   So this badge is that same chip, in the two ramps the design already uses to
   mean "yes, this is easy" and "careful, this one has conditions": the green
   `kg` ramp for a parcel and the gold/chilli `gold` ramp for van-only. Both are
   the tenant's own washes with the tenant's own ink — `af-kg-700` on `af-kg-50`
   and `af-gold-600` on `af-gold-100` — the same pairs `.lm-chip-leaf` and
   `.lm-chip-accent` already take on this storefront, so a shopper reads one
   badge language across the page rather than two.

   `--af-radius-sm` is 10px, which is the `rounded-md`/`rounded-lg` step this
   design uses for a small inset — deliberately NOT `999px`, which is Asharaf's
   pill and is not what this prototype's card badges are.
   ========================================================================== */
.af-ship {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 4px;
  border: 1px solid transparent;
  border-radius: var(--af-radius-sm);
  padding: 3px 8px;
  font-family: var(--af-sans);
  font-size: 10px;
  font-weight: 800;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: .08em;
  white-space: nowrap;
}

/* Goes by carrier — the shop's own green, its "this is straightforward" ramp. */
.af-ship[data-ship="postal"] {
  background: var(--af-kg-50);
  border-color: var(--af-kg-200);
  color: var(--af-kg-700);
}

/* Needs a van — the gold ramp, this design's "there is a condition on this"
   colour. Deliberately not `--af-chilli`, which the cards reserve for a price
   cut; a fresh-fish counter is not an error state. */
.af-ship[data-ship="van"] {
  background: var(--af-gold-100);
  border-color: var(--af-gold-200);
  color: var(--af-gold-600);
}

/* On a card the badge is a line of its own between the note and the Add
   button, so it takes the card's own left edge and the note's rhythm rather
   than the meta row's inline gap. */
.af-card .af-ship,
.tj-wish-body .af-ship {
  align-self: flex-start;
  margin-top: 6px;
  font-size: 9px;
  padding: 2px 6px;
}
