/**
 * Mini App shared CSS, injected via inject_shared_css() in lambda_http.py.
 * Placeholder in each HTML page's <style> block: /*{{MA_UI_CSS}}* /
 *
 * Spinner accent: override :root { --ma-accent: ... } per page if needed.
 * KPI size:       override :root { --kpi-size: ... } per page if needed.
 */

/* The cascade layers, declared once (FE-7): four names, lowest to
   highest. Nothing moves into a layer yet on purpose: one misordered
   layer silently changes which rule wins on 97 pages at once, so a
   component's rules move in only when it is edited anyway. This
   statement alone changes no pixel. */
@layer tokens, atoms, components, pages;

/* ─────────────────────────────────────────────────────────────
   Design tokens, warm-earth palette (handoff: 2026-05 staff redesign)
   Phase 1 of UI revamp. Tokens shipped as `--ma-*` so existing pages
   keep working with `--tg-theme-*`; new chrome opts in via `--ma-*`.
   Plus Jakarta Sans + Space Grotesk are intentional, do not swap.
   ───────────────────────────────────────────────────────────── */
/* The font stylesheet used to be @import-ed here. An @import cannot be
   requested until the stylesheet holding it has arrived and parsed, a
   quarter of a megabyte later, and the woff2 files are one hop after
   that again. It is now a <link> at the top of <head>, put there for
   every page by inject_shared_css() in shared/lambda_http.py, so it
   downloads alongside the page instead of behind it. Change the family
   list there, not here. */

/* ─────────────────────────────────────────────────────────────
   hidden means hidden, app-wide, once.

   The browser's own `[hidden] { display: none }` lives in the UA
   stylesheet, so ANY author rule that sets `display` beats it. Nearly
   every atom in this file sets one (`.ma-takeaway` is `display: flex`),
   which meant `el.hidden = true` quietly did nothing and the element
   sat there, usually empty, which is how an insight strip with no
   words ended up hanging at the top of Sales.

   Nine copies of this exact patch had already been written by hand -
   five scoped to one class in this file, four more pasted into page
   <style> blocks, and each new component had to rediscover the trap.
   One rule instead: `hidden` is the app's way to hide a thing, it is
   what `MAInsight` and `MAFreshness` both set, and it now works on
   every element without a page or a component opting in.
   ───────────────────────────────────────────────────────────── */
[hidden] { display: none !important; }

:root {
  /* Default tap-active accent, pages override per-Mini-App as needed.
     Used by atoms that historically read Telegram's button colour;
     defaults to brand brick so users with off-brand Telegram themes
     still see the Mini App's intended look. */
  --ma-accent: var(--ma-brick);
  --yoco: rgb(0, 153, 224);
  --uber: #06C167;
  --kpi-size: 28px;

  /* ── Warm-earth palette (light) ──────────────────────────── */
  --ma-cream: #F7F4ED;
  --ma-surface: #FFFDF8;
  --ma-elevated: #FFFDF8;     /* hero / dark-on-dark in dark mode; same as surface in light */
  --ma-white: #FFFFFF;
  --ma-ink: #1F2D2A;
  --ma-ink-2: #4A5550;
  --ma-ink-3: #8A8077;
  --ma-border: #E6DFD2;
  --ma-border-strong: #D4CABE;
  --ma-brick: #A8331F;
  --ma-brick-soft: #F6E3DB;
  --ma-brick-ink: #6E1E0F;
  /* Brick has two jobs and one hue cannot do both. As TEXT or an icon on
     the page it has to clear the ground, which in dark mode means going
     light (#E55B3D on #171C1A is 4.9:1). As a FILL under a white label it
     has to clear white, which means going dark, and #E55B3D under #fff
     is 3.6:1, under the floor for a 13px bold tab. So:
       --ma-brick        brick as a mark: text, icon, border, tint
       --ma-brick-solid  brick as a surface carrying a WHITE label
     In light mode they are the same colour; in dark mode the solid one
     stays deep enough for #fff to read (5.1:1). */
  --ma-brick-solid: #A8331F;
  --ma-gold: #E8B33E;
  --ma-gold-soft: #FBE9B8;
  --ma-gold-ink: #7A5610;
  --ma-forest: #2E5E3E;
  --ma-forest-soft: #DBE8DC;
  --ma-forest-ink: #1F4A2E;
  --ma-zuzo: #8B7BE8;
  --ma-zuzo-soft: #E5E1F8;
  --ma-zuzo-ink: #3D3287;
  /* The three states a ring on a photograph can mean (RCP-5, MAPhotoZoom).
     Colour is state, never confidence: grey = read off the paper, green =
     a person checked it, red = it blocks the line. These mirror the
     palette's neutral / forest / brick because a ring sits on a slip's
     photograph rather than on the page, and is the one place that may
     need its own value later. */
  --ma-ring-read: #8A8077;
  --ma-ring-checked: #2E5E3E;
  --ma-ring-blocks: #A8331F;

  /* ── Typography ──────────────────────────────────────────── */
  --ma-font-body: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
  --ma-font-num:  'Space Grotesk', 'Plus Jakarta Sans', system-ui, sans-serif;
  /* Display type (FE-13): the ONE host-conditional token, read only by
     rules at 28px or above. Defaults to the numeric pair, so a host that
     does not rebind it is untouched; only html.host-app rebinds it, to
     Fraunces. Below 28px every figure stays --ma-font-num on every host.
     test_font_scale.py holds the 28px line. */
  --ma-font-display: var(--ma-font-num);
  /* A kitchen tape code, and anything else that must not be read as a
     word. Space Grotesk is a display face, not a monospace one. */
  --ma-font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  /* ── Spacing scale ───────────────────────────────────────── */
  --ma-sp-1: 6px;
  --ma-sp-2: 8px;
  --ma-sp-3: 10px;
  --ma-sp-4: 12px;
  --ma-sp-5: 14px;
  --ma-sp-6: 18px;
  --ma-sp-7: 22px;
  --ma-sp-8: 28px;

  /* ── Radius scale ────────────────────────────────────────── */
  --ma-radius-xs: 6px;
  --ma-radius-sm: 8px;
  --ma-radius-md: 10px;
  --ma-radius-lg: 12px;
  --ma-radius-xl: 14px;
  --ma-radius-2xl: 16px;
  --ma-radius-pill: 999px;

  /* ── Width tiers (FE-7) ──────────────────────────────────────
     Four, up only: 0-699 phone (the contract), >=700 wide, >=1100 desk,
     >=1440 cap. 1100 matches a 1280x800 laptop after browser chrome and
     business_house_page.html's existing split. A custom property cannot
     be read inside @media, so this is the documented constant and the
     JS MABreakpoints the mirror; test_breakpoints.py fences literals. */
  --ma-bp-wide: 700px;
  --ma-bp-desk: 1100px;
  --ma-bp-cap: 1440px;

  /* ── Motion, the one signature ───────────────────────────────
     Modelled on Sales › Items (PR #138), the page operators called
     easy, smooth and snappy. Every duration, curve and press
     amplitude in the app comes from these tokens, a page that
     types its own `250ms cubic-bezier(...)` has re-forked the
     signature. Motion exists to show WHAT CHANGED, never to
     decorate: count a number up, grow a bar, slide a row to its new
     rank. All of it is switched off by the global
     `prefers-reduced-motion` block at the foot of this file.
     Reference: docs/REFERENCE-design-system.md § Motion. */
  --ma-ease: cubic-bezier(0.22, 1, 0.36, 1);  /* ease-out, no overshoot */
  --ma-dur-press: 80ms;    /* tap acknowledgement */
  --ma-dur-swap: 180ms;    /* tab / panel / sheet swap */
  --ma-dur-count: 250ms;   /* number count-up */
  --ma-dur-flip: 250ms;    /* list re-sort */
  --ma-dur-grow: 300ms;    /* bar / track growth */
  --ma-pulse-skel: 1.4s;   /* skeleton breathing */
  --ma-pulse-stale: 1.2s;  /* stale-while-revalidate shimmer */
  --ma-pulse-live: 1.6s;   /* the LIVE dot's breath */
  /* Press amplitude, in three steps. Small controls need a deeper
     squeeze than a full-width card to read as the same gesture. */
  --ma-press-sm: 0.94;     /* icon buttons, day cells, nav arrows */
  --ma-press: 0.97;        /* the default, buttons, chips, pills */
  --ma-press-lg: 0.99;     /* cards, full-width rows */
  --ma-press-fade: 0.82;   /* rows in a scrolling list, fade, don't squeeze */
  /* Skeleton fill. `--ma-border` reads as a grey placeholder bar on
     both the page background and a card, in both themes. */
  --ma-skel-bg: var(--ma-border);
  /* Bottom sheets never take the whole screen: they stop below
     Telegram's control strip and leave a strip of page visible, so
     there is always somewhere to tap out (and something to swipe
     against). dvh, not vh, the viewport shrinks when a keyboard opens. */
  --ma-sheet-max-h: calc(100dvh - var(--ma-top-inset) - var(--ma-sp-8));

  /* ── Shell insets (Telegram full-screen, Bot API 8.0) ─────────
     Written by MAShell from Telegram.WebApp.safeAreaInset (notch /
     status bar / home indicator) and contentSafeAreaInset (Telegram's
     floating collapse + menu controls). All 0 in the classic layout,
     so nothing below moves unless the app really is full-screen. */
  --ma-inset-top: 0px;
  --ma-inset-bottom: 0px;
  --ma-inset-left: 0px;
  --ma-inset-right: 0px;
  --ma-content-inset-top: 0px;
  /* Width kept clear at each end of any row that shares Telegram's
     control strip in full-screen: Telegram floats a "< Back" / "X Close"
     pill on the LEFT and the collapse + menu pill on the RIGHT. Measured
     on Android (2026-09-02): the left pill ends at ~80px and the right
     starts at ~305px of a 390px screen, so with --ma-sp-5 of row padding
     these clear both with a few px to spare, including the wider "Close"
     label at a section root. These two are the tuning knobs if a client
     places its controls differently; nothing else needs to change. */
  --ma-tg-back-w: 72px;
  --ma-tg-controls-w: 78px;
  /* Where our first sticky row may start. A page whose first row is
     COMPACT (a title + gear, or the merged chip + gear) shares
     Telegram's control strip and only clears the status bar, the row
     reserves --ma-tg-back-w / --ma-tg-controls-w at its ends so it sits
     in the gap between Telegram's two floating pills. Anything
     full-width (the tab strip) sits below the strip entirely. */
  --ma-top-inset: calc(var(--ma-inset-top) + var(--ma-content-inset-top));
  /* Measured height of the merged filter row, the shell's one sticky
     row, so a page-level sticky control (Sales' dish search) can pin
     exactly under it. */
  --ma-shell-bar-h: 0px;
  /* Measured by MAShell at runtime. The title row and the tabs are flow
     content now, so nothing pins under them; kept for pages that read
     them. */
  --ma-shell-header-h: 52px;
  --ma-shell-tabs-h: 44px;
  /* Shell collapse progress, 0 at the top → 1 after 48 px, written by
     MAShell once per frame while it changes. Drives the facet chip
     cross-fade where the engine has no CSS scroll timeline. */
  --ma-scroll-p: 0;
  /* How far the large facet chip overflows the chip row at rest
     (large − slim, measured by MAShell). The row reserves it as margin. */
  --ma-shell-chip-overflow: 0px;
}
/* A compact first row (classic title row, or the merged chip row) may
   share Telegram's strip, it clears only the status bar. */
html[data-shell-strip] { --ma-top-inset: var(--ma-inset-top); }

/* Dark mode, charcoal palette per the dark_mode handoff (May 2026).
   Three rules:
     1. Charcoal, not brown. #171C1A, neutral near-black with a tiny
        green-ink tint. NEVER sepia/warm-brown bg in dark mode.
     2. Brighten the warm primary. --ma-brick = #E55B3D (vs light #A8331F)
        so it pops against charcoal without going neon.
     3. Borders are always visible, every card needs a 1px --ma-border
        hairline. Slight bg/surface contrast is not enough in dark mode.

   --ma-cream / --ma-surface / --ma-white / --ma-elevated swap their
   semantic role:
     light    dark    used for
     cream    bg      page background
     surface  surface card backgrounds
     white    surface "white" cards (no separate token; consume --ma-surface)
     elevated elevated hero / dark-on-dark cards
*/

@media (prefers-color-scheme: dark) {
  :root {
    --ma-cream: #171C1A;
    --ma-surface: #212725;
    --ma-elevated: #2A332F;
    --ma-white: #212725;        /* legacy alias, same as surface in dark */
    --ma-ink: #ECEFEC;
    --ma-ink-2: #A8B0AC;
    --ma-ink-3: #6E7975;
    --ma-border: #2E3833;
    --ma-border-strong: #3D4843;
    --ma-brick: #E55B3D;
    --ma-brick-soft: #3D1B12;
    --ma-brick-ink: #FFCBB6;
    --ma-brick-solid: #C0442A;
    --ma-gold: #F1C351;
    --ma-gold-soft: #3A2C12;
    --ma-gold-ink: #FBE9B8;
    --ma-forest: #6BC68F;
    --ma-forest-soft: #1B3527;
    --ma-forest-ink: #C9E8CF;
    --ma-zuzo: #A89AFB;
    --ma-zuzo-soft: #27224A;
    --ma-zuzo-ink: #E4DEFB;
    --ma-ring-read: #A8B0AC;
    --ma-ring-checked: #6BC68F;
    --ma-ring-blocks: #E55B3D;
  }
}

/* Explicit override, Settings → Appearance picker writes data-theme
   on <html>. Mirrors the @media block above so the override wins
   regardless of OS preference. */
[data-theme="dark"] {
  --ma-cream: #171C1A;
  --ma-surface: #212725;
  --ma-elevated: #2A332F;
  --ma-white: #212725;
  --ma-ink: #ECEFEC;
  --ma-ink-2: #A8B0AC;
  --ma-ink-3: #6E7975;
  --ma-border: #2E3833;
  --ma-border-strong: #3D4843;
  --ma-brick: #E55B3D;
  --ma-brick-soft: #3D1B12;
  --ma-brick-ink: #FFCBB6;
  --ma-brick-solid: #C0442A;
  --ma-gold: #F1C351;
  --ma-gold-soft: #3A2C12;
  --ma-gold-ink: #FBE9B8;
  --ma-forest: #6BC68F;
  --ma-forest-soft: #1B3527;
  --ma-forest-ink: #C9E8CF;
  --ma-zuzo: #A89AFB;
  --ma-zuzo-soft: #27224A;
  --ma-zuzo-ink: #E4DEFB;
  --ma-ring-read: #A8B0AC;
  --ma-ring-checked: #6BC68F;
  --ma-ring-blocks: #E55B3D;
}

[data-theme="light"] {
  --ma-cream: #F7F4ED;
  --ma-surface: #FFFDF8;
  --ma-elevated: #FFFDF8;
  --ma-white: #FFFFFF;
  --ma-ink: #1F2D2A;
  --ma-ink-2: #4A5550;
  --ma-ink-3: #8A8077;
  --ma-border: #E6DFD2;
  --ma-border-strong: #D4CABE;
  --ma-brick: #A8331F;
  --ma-brick-soft: #F6E3DB;
  --ma-brick-ink: #6E1E0F;
  --ma-brick-solid: #A8331F;
  --ma-gold: #E8B33E;
  --ma-gold-soft: #FBE9B8;
  --ma-gold-ink: #7A5610;
  --ma-forest: #2E5E3E;
  --ma-forest-soft: #DBE8DC;
  --ma-forest-ink: #1F4A2E;
  --ma-zuzo: #8B7BE8;
  --ma-zuzo-soft: #E5E1F8;
  --ma-zuzo-ink: #3D3287;
  --ma-ring-read: #8A8077;
  --ma-ring-checked: #2E5E3E;
  --ma-ring-blocks: #A8331F;
}

/* Apply body font globally, every existing component uses
   `font-family: inherit`, so this cascades automatically. Pages with
   their own `body { font-family: ... }` are overridden because
   /*{{MA_UI_CSS}}* / is injected after the page's inline <style>. */
body {
  font-family: var(--ma-font-body);
}

/* Numeric helper, opt-in via class. Money totals, KPI numbers, hours,
   anything where digit alignment matters. */
.ma-num {
  font-family: var(--ma-font-num);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* ═════════════════════════════════════════════════════════════
   Motion + loading atoms, the one signature
   ─────────────────────────────────────────────────────────────
   The loading rule, in one line: a READ shows a skeleton that
   mirrors the layout it is about to become; a WRITE shows the
   `maBusy()` overlay because the operator must not touch anything
   until it lands. A spinner over content that is already on
   screen is always wrong, that is what `.ma-stale` is for.

     cache miss   →  .ma-skel blocks in the final layout's shape
     cache hit    →  paint immediately, no loading state at all
     revalidating →  .ma-stale on the container, .ma-shimmer on the
                     two or three numbers that may move
     writing      →  maBusy(+1) … maBusy(-1)

   JS side: MAMotion (skel / countUp / flip / stale / reduced).
   ═════════════════════════════════════════════════════════════ */
@keyframes ma-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

/* Skeleton block. Size it inline (`style="height:14px;width:40%"`)
   or with a page class, the shape is the page's job, the look and
   the breathing are not. Build the markup with `maSkel()`. */
.ma-skel {
  background: var(--ma-skel-bg);
  border-radius: var(--ma-radius-sm);
  animation: ma-pulse var(--ma-pulse-skel) ease-in-out infinite;
}
/* Outlined variant, an empty card standing in for a card. */
.ma-skel--card {
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
}
/* A line of text. Give it a width; the height is a text line. */
.ma-skel--text { height: 14px; border-radius: var(--ma-radius-xs); }
.ma-skel--circle { border-radius: 50%; }
/* For a <span> standing in for a word inside a line of text. */
.ma-skel--inline { display: inline-block; vertical-align: middle; }

/* Stale-while-revalidate. Put `.ma-stale` on the container while a
   cached payload is being refreshed and `.ma-shimmer` on the two or
   three numbers that might move. Everything else stays solid and
   stays tappable, the operator can keep working while we refresh. */
.ma-stale .ma-shimmer {
  animation: ma-pulse var(--ma-pulse-stale) ease-in-out infinite;
}

/* The line a dead connection leaves: "as of 14:20 · not updated"
   (MALastGood, FE-22). It sits at the top of the body, in the flow, so
   it never covers a number the operator is reading. Muted surface, not
   an alarm: nothing is wrong with the numbers, they are just not today's.
   Zero height and no border when it has nothing to say, so a page that
   never shows it is byte-for-byte its old layout. */
.ma-last-good {
  margin: 0;
  padding: 7px 16px;
  font-size: 12px;
  line-height: 1.35;
  text-align: center;
  color: var(--ma-ink-2);
  background: var(--ma-surface);
  border-bottom: 1px solid var(--ma-border);
}

/* The signal dropped and the shell says so once (MANeedsSignal, FE-24).
   Same muted surface as .ma-last-good. Hidden when it has nothing to say. */
.ma-needs-signal-line {
  margin: 0;
  padding: 7px 16px;
  font-size: 12px;
  line-height: 1.35;
  text-align: center;
  color: var(--ma-ink-2);
  background: var(--ma-surface);
  border-bottom: 1px solid var(--ma-border);
}

/* A control that needs a live read, greyed while the signal is gone
   (MANeedsSignal, FE-24): the label sits beneath it so the operator is
   told why rather than left to tap it and watch it fail. */
.ma-needs-signal {
  opacity: .55;
  pointer-events: none;
}
button.ma-needs-signal[disabled] { cursor: default; }
.ma-needs-signal::after {
  content: 'Needs signal';
  display: block;
  margin-top: 4px;
  font: 600 11px var(--ma-font-body);
  color: var(--ma-ink-3);
  text-align: center;
  opacity: 1;
  pointer-events: none;
}

/* The same line when a tap re-reads (LAST_GOOD_TAP, OPX-16): still a
   muted note, but a tappable one. The hint is in the words, not this
   rule; this only makes the whole strip a large, honest tap target and
   shows the press, so the operator can tell it is a control. */
.ma-last-good--tap {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-last-good--tap:active {
  background: var(--ma-border);
}
.ma-last-good--tap:focus-visible {
  outline: 2px solid var(--ma-accent, currentColor);
  outline-offset: -2px;
}

/* Live indicator, the small dot that breathes beside a LIVE / TODAY
   label. Two pages had their own; this is the one. Add --brick for a
   dot that should read urgent rather than in-progress. */
.ma-live-dot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--ma-gold);
  animation: ma-pulse var(--ma-pulse-live) ease-in-out infinite;
}
.ma-live-dot--brick { background: var(--ma-brick); }

/* Bars and tracks grow to their new size instead of jumping.
   Height/width are the two exceptions to the transform-only rule:
   they are cheap on a small box and there is no other way to draw a
   bar growing. Never put this on a long list. */
.ma-grow {
  transition: height var(--ma-dur-grow) var(--ma-ease),
              width  var(--ma-dur-grow) var(--ma-ease);
}

/* Panel / tab swap, 180ms opacity, no movement. */
.ma-swap-in { animation: ma-swap-in var(--ma-dur-swap) ease-out; }
@keyframes ma-swap-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Press acknowledgement. Every tappable thing gets one of these
   four so a tap feels the same everywhere in the app. */
.ma-press,
.ma-press-sm,
.ma-press-lg,
.ma-press-fade {
  transition: transform var(--ma-dur-press) ease, opacity var(--ma-dur-press) ease;
}
.ma-press:active    { transform: scale(var(--ma-press)); }
.ma-press-sm:active { transform: scale(var(--ma-press-sm)); }
.ma-press-lg:active { transform: scale(var(--ma-press-lg)); }
/* Fade instead of squeeze, for rows inside a scrolling list, where a
   scale would drag the neighbours' baselines with it. */
.ma-press-fade:active { opacity: var(--ma-press-fade); }

/* ---- Loading overlay ---- */
@keyframes ma-spin {
  to { transform: rotate(360deg); }
}

.ma-overlay {
  position: fixed;
  inset: 0;
  /* Above bottom-sheets (9999) so the loading spinner is visible
     when the user taps a tile inside the More sheet or any other
     modal. Bumped on 2026-05-19, was previously hidden behind
     sheets, making tile taps feel dead. */
  z-index: 10002;
  background: rgba(0, 0, 0, 0.42);
  display: none;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.ma-overlay.ma-overlay--visible { display: flex; }

.ma-spinner {
  width: 44px;
  height: 44px;
  border: 3px solid rgba(255, 255, 255, 0.22);
  border-top-color: var(--ma-accent);
  border-radius: 50%;
  animation: ma-spin 0.72s linear infinite;
}

/* Inline spinner, for a button, chip or row that is waiting on a slow
   fetch that must NOT block the rest of the page (so `maBusy` and its
   full-screen overlay would be wrong). Inherits currentColor, so it
   reads correctly on brick, gold, ghost and dark surfaces alike; sized
   in `em` so it tracks whatever type it sits next to. */
.ma-spinner-inline {
  display: inline-block;
  flex: 0 0 auto;
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  opacity: 0.9;
  animation: ma-spin 0.72s linear infinite;
}

/* ---- Snackbar ---- */
.ma-snack {
  position: fixed;
  bottom: calc(22px + max(var(--ma-inset-bottom), env(safe-area-inset-bottom, 0px)));
  left: 50%;
  transform: translateX(-50%) translateY(140%);
  z-index: 9999;
  max-width: min(92vw, 400px);
  padding: 12px 18px;
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.35;
  /* The toast is dark on purpose in both themes, it is the one thing
     that has to be legible over whatever is behind it. */
  color: var(--ma-ink-card-fg);
  background: var(--ma-ink-card);
  box-shadow: 0 4px 28px rgba(0, 0, 0, 0.38);
  transition: transform 0.25s ease, opacity 0.25s ease;
  opacity: 0;
  pointer-events: none;
  text-align: center;
}
.ma-snack.ma-snack--visible {
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}
.ma-snack.ma-snack--error { background: rgba(170, 30, 35, 0.96); }

/* ---- The reload bar (OPX-17) ----
   A deploy landed under an open page and the API answered 426. One line
   pinned to the top, tappable, over whatever is behind it. The screen
   underneath keeps its numbers and keeps scrolling, this never blanks
   it and never covers it with a sheet. Above the nav (9000) so a tap
   lands, below modals (9999) so an open sheet still wins. */
.ma-update-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 9500;
  display: block;
  width: 100%;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
  border: 0;
  font: inherit;
  font-size: 13px;
  line-height: 1.35;
  text-align: center;
  color: var(--ma-ink-card-fg);
  background: var(--ma-ink-card);
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.28);
  cursor: pointer;
  animation: ma-update-bar-in 0.2s ease;
}
.ma-update-bar:focus-visible { outline: 2px solid var(--ma-accent, currentColor); outline-offset: -3px; }
/* No reduced-motion block here: the shared layer's one global block
   collapses this animation's duration like every other, which is the
   rule test_motion_signature.py holds. */
@keyframes ma-update-bar-in {
  from { transform: translateY(-100%); }
  to { transform: translateY(0); }
}

/* ---- The read-only banner (TCP-9): the shop is not saving, one line. ---- */
.ma-read-only-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 9500;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
  font-size: 13px;
  line-height: 1.35;
  text-align: center;
  color: var(--ma-ink-card-fg);
  background: var(--ma-ink-card);
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.28);
  animation: ma-update-bar-in 0.2s ease;
}

/* ---- Undo toast (maUndoToast), post-action reversal window ----
   Lavender Zuzo border per design_handoff_expenses_redesign. Sits
   above the bottom nav (9000) + Ask Zuzo chip (9500) but below modals
   (9999) so an open sheet still covers it. */
.ma-undo-toast {
  position: fixed;
  bottom: calc(84px + max(var(--ma-inset-bottom), env(safe-area-inset-bottom, 0px)));
  left: 50%;
  transform: translateX(-50%) translateY(160%);
  z-index: 9600;
  display: inline-flex;
  align-items: center;
  gap: var(--ma-sp-2);
  max-width: min(94vw, 440px);
  padding: 10px 10px 10px 14px;
  border-radius: 999px;
  font-size: 13px;
  line-height: 1.2;
  color: var(--ma-ink);
  background: var(--ma-surface);
  border: 1px solid var(--ma-zuzo);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
  transition: transform 0.22s cubic-bezier(.2,.7,.3,1), opacity 0.22s ease;
  opacity: 0;
  pointer-events: none;
}
.ma-undo-toast.ma-undo-toast--visible {
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}
.ma-undo-toast__spark {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--ma-zuzo);
}
.ma-undo-toast__spark svg { width: 15px; height: 15px; }
.ma-undo-toast__msg {
  flex: 1 1 auto;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ma-undo-toast__count {
  flex: 0 0 auto;
  font-family: var(--ma-font-num, inherit);
  font-weight: 700;
  font-size: 12px;
  color: var(--ma-ink-3);
}
.ma-undo-toast__btn {
  flex: 0 0 auto;
  padding: 6px 14px;
  border-radius: 999px;
  border: none;
  background: var(--ma-zuzo);
  color: #fff;
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
.ma-undo-toast__btn:active { transform: scale(var(--ma-press)); }

/* ── Review v2 atoms (PLAN-expenses-review-v2) ─────────────────────
   Informational blue tone (price-check KindTag) + the shared
   exception-review atoms. Map onto the existing --ma-* palette; the
   blue trio mirrors the theme structure (light default / dark media /
   explicit data-theme overrides). */
:root            { --ma-blue: #2B6CB0; --ma-blue-soft: #E1ECF7; --ma-blue-ink: #1A4E84; }
@media (prefers-color-scheme: dark) { :root { --ma-blue: #5BB0F2; --ma-blue-soft: #15324A; --ma-blue-ink: #9BD3FF; } }
[data-theme="dark"]  { --ma-blue: #5BB0F2; --ma-blue-soft: #15324A; --ma-blue-ink: #9BD3FF; }
[data-theme="light"] { --ma-blue: #2B6CB0; --ma-blue-soft: #E1ECF7; --ma-blue-ink: #1A4E84; }

/* ── Platform series colours, Uber Eats / Yoco ────────────────────
   Every sales chart in the app is these two series, so they are
   tokens, not payload values. They used to arrive on `/sales/data`
   as `uber_color` / `yoco_color` from `platform_colors.py`, painted
   inline, which meant they could not follow the theme, and dark
   mode drew light-mode brand colours.

   The steps below are validated against the real chart surfaces
   (#FFFDF8 light, #212725 dark) for lightness band, chroma, contrast
   and colourblind separation. The shipped brand hexes failed: Uber's
   #06C167 sits at 2.34:1 on cream (needs 3:1) and outside the dark
   lightness band. These are the nearest passing steps and still read
   unmistakably as Uber green and Yoco blue.

   Deliberately NOT reusing --ma-forest / --ma-blue: those are the
   semantic success + information tones. A dish selling well on Uber
   is not "success", and the Items list used to say both at once -
   a #06C167 bar beside a --ma-forest label for the same platform.

   `-ink` is the readable-on-surface text step, for the rare place a
   platform name must be coloured type rather than a swatch. */
:root {
  --ma-uber: #0E8F55; --ma-uber-soft: #DCEFE5; --ma-uber-ink: #0A6B40;
  --ma-yoco: #0A72B8; --ma-yoco-soft: #DDEBF6; --ma-yoco-ink: #08578C;
}
@media (prefers-color-scheme: dark) {
  :root {
    --ma-uber: #1FAE6B; --ma-uber-soft: #143425; --ma-uber-ink: #8FE0B6;
    --ma-yoco: #3492DB; --ma-yoco-soft: #142A3D; --ma-yoco-ink: #9CCDF2;
  }
}
[data-theme="dark"] {
  --ma-uber: #1FAE6B; --ma-uber-soft: #143425; --ma-uber-ink: #8FE0B6;
  --ma-yoco: #3492DB; --ma-yoco-soft: #142A3D; --ma-yoco-ink: #9CCDF2;
}
[data-theme="light"] {
  --ma-uber: #0E8F55; --ma-uber-soft: #DCEFE5; --ma-uber-ink: #0A6B40;
  --ma-yoco: #0A72B8; --ma-yoco-soft: #DDEBF6; --ma-yoco-ink: #08578C;
}

/* Ring around the floating Ask Zuzo pill, separates it from the page
   behind. The hardcoded white ring glared in dark mode on device
   (2026-09-02), so it follows the theme like every other token. */
:root            { --ma-zuzo-halo: rgba(255, 255, 255, 0.7); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --ma-zuzo-halo: rgba(0, 0, 0, 0.45); } }
[data-theme="dark"]  { --ma-zuzo-halo: rgba(0, 0, 0, 0.45); }
[data-theme="light"] { --ma-zuzo-halo: rgba(255, 255, 255, 0.7); }

/* Surfaces that are dark ON PURPOSE, the batch-card header, the Gcina
   band, the dark hero, the toast. Three of them each carried their own
   hand-written dark-mode patch: a solid `--ma-ink` fill inverts to
   near-white in dark, so each one swapped to `--ma-elevated` plus a
   coloured rail, written twice (explicit theme + system preference) =
   six blocks for one idea. `--ma-elevated` in dark is #2A332F on a
   #212725 card on a #171C1A page, so the header came out within one
   step of the body it was meant to sit above, and the card lost its
   header (`design_handoff_money_out_uiux_fix/CONFORMANCE.md` gap 1).

   One token instead, and the rule it encodes: anything intentionally
   dark in the light theme gets DARKER in dark, never lighter, that is
   what keeps it reading as a distinct layer rather than as more page.
   The coloured rails stay: they carry meaning, not elevation. */
:root {
  --ma-ink-card:      #1F2D2A;
  --ma-ink-card-fg:   #F7F4ED;
  --ma-ink-card-line: rgba(247, 244, 237, 0.14);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ma-ink-card:      #0E1210;
    --ma-ink-card-fg:   #F0EDE6;
    --ma-ink-card-line: rgba(240, 237, 230, 0.12);
  }
}
[data-theme="dark"] {
  --ma-ink-card:      #0E1210;
  --ma-ink-card-fg:   #F0EDE6;
  --ma-ink-card-line: rgba(240, 237, 230, 0.12);
}
[data-theme="light"] {
  --ma-ink-card:      #1F2D2A;
  --ma-ink-card-fg:   #F7F4ED;
  --ma-ink-card-line: rgba(247, 244, 237, 0.14);
}

/* The empty half of a bar, and a bar that means nothing in particular.
   `design_handoff_money_out_uiux_fix/CONFORMANCE.md` gap 1 asked for
   both and neither was added, so every bar in Money out reached for a
   coloured token to draw its own background with, which is how a track
   comes to read as a fifth category. `--ma-track` is the groove;
   `--ma-track-fill` is the neutral fill for the one bar that carries no
   meaning of its own: Uncategorised, the part nobody has answered. */
:root {
  --ma-track:      #F0EADD;
  --ma-track-fill: #D8CFBE;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ma-track:      #2A302D;
    --ma-track-fill: #4A534D;
  }
}
[data-theme="dark"] {
  --ma-track:      #2A302D;
  --ma-track-fill: #4A534D;
}
[data-theme="light"] {
  --ma-track:      #F0EADD;
  --ma-track-fill: #D8CFBE;
}

/* The lines and the washes the accent tokens never had.
   `--ma-*-soft` is a *fill*, the ground of a chip, a note, a band. A
   surface that wants to be edged in the same family had nothing to
   reach for and reached for the soft value at 1.5px, which reads as a
   thicker version of its own background and disappears. And a wash is
   fainter than a soft: the Ask-today group is a whole block of page, so
   it takes a tint the eye reads as "this region", where `--ma-gold-soft`
   at that size reads as a warning.

   `--ma-raw-*` is the box the raw text off a slip sits in, the one
   surface on a card that is not ours: it is what the shop printed, and
   it is set apart so the operator can see we did not touch it. */
:root {
  --ma-gold-line:  #E8B33E;
  --ma-gold-wash:  #FDF6E3;
  --ma-zuzo-line:  #C7BFF0;
  --ma-raw-bg:     #F7F4ED;
  --ma-raw-line:   #D4CABE;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ma-gold-line:  #7A5A18;
    --ma-gold-wash:  #241C0E;
    --ma-zuzo-line:  #3A3170;
    --ma-raw-bg:     #1B201E;
    --ma-raw-line:   #3C443F;
  }
}
[data-theme="dark"] {
  --ma-gold-line:  #7A5A18;
  --ma-gold-wash:  #241C0E;
  --ma-zuzo-line:  #3A3170;
  --ma-raw-bg:     #1B201E;
  --ma-raw-line:   #3C443F;
}
[data-theme="light"] {
  --ma-gold-line:  #E8B33E;
  --ma-gold-wash:  #FDF6E3;
  --ma-zuzo-line:  #C7BFF0;
  --ma-raw-bg:     #F7F4ED;
  --ma-raw-line:   #D4CABE;
}

/* KindTag, per-exception kind pill */
.ma-kind-tag {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px 3px 7px; border-radius: 999px;
  font-weight: 800; font-size: 10.5px; letter-spacing: 0.2px; line-height: 1;
}
.ma-kind-tag svg { width: 11px; height: 11px; }
.ma-kind-tag--brick { background: var(--ma-brick-soft); color: var(--ma-brick-ink); }
.ma-kind-tag--gold  { background: var(--ma-gold-soft);  color: var(--ma-gold-ink); }
.ma-kind-tag--blue  { background: var(--ma-blue-soft);  color: var(--ma-blue-ink); }

/* ZuzoDot, sparkle badge (size set inline by the builder) */
.ma-zuzo-dot {
  border-radius: 999px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 35% 30%, #C9BEFF, var(--ma-zuzo) 65%);
  color: #1A1530;
}
.ma-zuzo-dot svg { width: 50%; height: 50%; }
.ma-zuzo-dot--pulse { animation: maZuzoPulse 1.8s ease-out infinite; }
@keyframes maZuzoPulse {
  0%   { box-shadow: 0 0 0 0 var(--ma-zuzo); }
  70%  { box-shadow: 0 0 0 9px rgba(0,0,0,0); }
  100% { box-shadow: 0 0 0 0 rgba(0,0,0,0); }
}

/* RawSlip, monospaced OCR text */
.ma-raw-slip {
  font-family: var(--ma-font-num, monospace); font-size: 12px; font-weight: 700;
  color: var(--ma-ink-3); letter-spacing: 1px; text-transform: uppercase;
}

/* ProgressDots, segmented progress rail */
.ma-progress-dots { display: flex; gap: 5px; align-items: center; }
.ma-progress-dots__seg {
  height: 5px; flex: 1; border-radius: 3px;
  background: var(--ma-border-strong); transition: background .3s;
}
.ma-progress-dots__seg--done { background: var(--ma-forest); }

/* AllClear, period-closed celebration card */
.ma-allclear {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 16px; padding: 28px; text-align: center;
}
.ma-allclear__ring {
  width: 76px; height: 76px; border-radius: 999px; background: var(--ma-forest-soft);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 8px var(--ma-forest-soft); color: var(--ma-forest);
}
.ma-allclear__ring svg { width: 40px; height: 40px; }
.ma-allclear__title {
  font-family: var(--ma-font-num, inherit); font-size: 24px; font-weight: 800;
  color: var(--ma-ink); letter-spacing: -0.6px;
}
.ma-allclear__sub {
  font-size: 13.5px; color: var(--ma-ink-2); margin-top: 8px;
  line-height: 1.5; max-width: 240px;
}
.ma-allclear__replay {
  margin-top: 4px; padding: 9px 16px; border-radius: 999px;
  background: var(--ma-elevated); border: 1px solid var(--ma-border);
  color: var(--ma-ink-2); cursor: pointer; font-weight: 700; font-size: 12.5px;
  display: inline-flex; gap: 6px; align-items: center;
}
.ma-allclear__replay svg { width: 13px; height: 13px; }

/* ---- KPI value ---- */
.kpi { font-size: var(--kpi-size); font-weight: 700; }

/* ---- Date preset pills ---- */
.preset-btn {
  padding: 7px 11px;
  font-size: 13px;
  border-radius: 8px;
  border: 1px solid var(--ma-ink-3);
  background: transparent;
  color: var(--ma-ink);
  cursor: pointer;
  white-space: nowrap;
}
.preset-btn.active {
  background: var(--ma-accent);
  border-color: var(--ma-accent);
  color: #fff;
}

/* ---- Bottom navigation bar ---- */
.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 9000;
  display: flex;
  background: var(--ma-cream);
  border-top: 1px solid rgba(128, 128, 128, 0.18);
  /* Home-indicator clearance: Telegram's full-screen inset (MAShell) or
     the browser's env(), whichever the client actually reports. */
  padding-bottom: max(var(--ma-inset-bottom), env(safe-area-inset-bottom, 0px));
}
.bottom-nav__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 8px 4px 6px;
  text-decoration: none;
  color: var(--ma-ink-3);
  font-size: 10px;
  gap: 3px;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.15s;
}
.bottom-nav__item--active { color: var(--ma-accent); }
.bottom-nav__item--disabled { opacity: 0.35; pointer-events: none; }
.nav-admin-only { display: none; }
.bottom-nav__icon { display: flex; align-items: center; justify-content: center; width: 22px; height: 22px; }
.bottom-nav__icon svg { width: 22px; height: 22px; }
.bottom-nav__label { font-weight: 500; letter-spacing: 0.01em; }

/* ─────────────────────────────────────────────────────────────
   The collapsible filter shell: sticky pill + animated panel,
   shared by MAFacetFilter's collapsible mode
   ───────────────────────────────────────────────────────────── */
.ma-filters-sentinel {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  pointer-events: none;
  visibility: hidden;
}
/* PLAN-filter-consolidation, flat full-bleed sharp bar.
   Was inheriting a rounded card look on /expenses from body padding +
   panel border-tops; now explicitly flat with a hairline divider
   separating collapsed pill from the content below. */
.ma-filters {
  position: sticky;
  top: 0;
  z-index: 800;
  margin: 0 -16px 8px;
  background: var(--ma-cream);
  border-bottom: 1px solid rgba(127, 127, 127, 0.16);
  border-radius: 0;            /* explicit, defeat any inherited rounding */
  transition: box-shadow 0.18s ease;
}
.ma-filters--lifted {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.ma-filters__pill {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 14px;            /* was 11px 16px, tighter, ~32px tall */
  border: none;
  border-radius: 0;             /* explicit, square corners */
  background: transparent;
  color: var(--ma-ink);
  font: inherit;
  font-size: 13px;              /* slightly smaller for the compact bar */
  cursor: pointer;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.ma-filters__pill-icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;                  /* was 18 */
  height: 16px;
  color: var(--ma-accent);
}
.ma-filters__pill-icon svg { width: 16px; height: 16px; }
.ma-filters__summary {
  flex: 1;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ma-filters__chevron {
  flex-shrink: 0;
  width: 14px;                  /* matches the new compact pill */
  height: 14px;
  color: var(--ma-ink-3);
  transition: transform 0.2s cubic-bezier(0.2, 0, 0, 1);
}
.ma-filters__chevron svg { width: 14px; height: 14px; }
.ma-filters[aria-expanded="true"] .ma-filters__chevron {
  transform: rotate(180deg);
}
/* Expandable panel, uses grid-template-rows trick for animatable height */
.ma-filters__panel-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.22s cubic-bezier(0.2, 0, 0, 1);
}
.ma-filters[aria-expanded="true"] .ma-filters__panel-wrap {
  grid-template-rows: 1fr;
}
.ma-filters__panel {
  overflow: hidden;
  min-height: 0;
}
.ma-filters__panel-inner {
  padding: 12px 16px 16px;
  background: rgba(127, 127, 127, 0.04);
}
.ma-filters__row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.ma-filters__btn {
  padding: 7px 12px;
  border-radius: 999px;
  border: 1px solid var(--ma-ink-3);
  background: transparent;
  color: var(--ma-ink);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.ma-filters__btn:active { transform: scale(var(--ma-press)); }
.ma-filters__btn[aria-pressed="true"] {
  background: var(--ma-accent);
  border-color: var(--ma-accent);
  color: #fff;
}

/* weekrange facet, prev/next chevrons + label pill in a row.
   Reuses .ma-filters__row for layout and .ma-filters__btn for the label. */
.ma-week-nav {
  align-items: center;
  gap: 8px;
}
.ma-week-nav__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--ma-ink-3);
  background: transparent;
  color: var(--ma-ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s, border-color 0.12s, transform 0.08s;
}
.ma-week-nav__btn:active { transform: scale(var(--ma-press-sm)); }
.ma-week-nav__btn svg { width: 14px; height: 14px; }
.ma-week-nav__label {
  flex: 1;
  text-align: center;
  min-width: 180px;
}

/* ─────────────────────────────────────────────────────────────
   MATabs, section sub-page tab strip (replaces page title)
   ───────────────────────────────────────────────────────────── */
.ma-tabs {
  display: flex;
  gap: 0;
  margin: 0 -16px 12px;
  padding: 0 8px;
  border-bottom: 1px solid var(--ma-surface);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.ma-tabs::-webkit-scrollbar { display: none; }
.ma-tabs__tab {
  flex-shrink: 0;
  display: block;
  padding: 13px 14px 11px;
  font-size: 15px;
  font-weight: 500;
  color: var(--ma-ink-3);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  -webkit-tap-highlight-color: transparent;
  white-space: nowrap;
  transition: color 0.12s;
}
.ma-tabs__tab:active { opacity: var(--ma-press-fade); }
.ma-tabs__tab[aria-current="page"] {
  color: var(--ma-accent);
  font-weight: 700;
  border-bottom-color: var(--ma-accent);
}

/* ─────────────────────────────────────────────────────────────
   MARangeCal, inline range calendar for a date-range picker
   ───────────────────────────────────────────────────────────── */
.ma-cal {
  margin-top: 12px;
  user-select: none;
}
.ma-cal__head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px;
}
.ma-cal__nav {
  width: 32px; height: 32px; border-radius: 50%;
  border: none; background: var(--ma-surface);
  color: var(--ma-ink);
  font: inherit; font-size: 16px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.ma-cal__nav:active { transform: scale(var(--ma-press-sm)); }
.ma-cal__nav svg { width: 14px; height: 14px; }
.ma-cal__title {
  font-size: 14px; font-weight: 700;
  color: var(--ma-ink);
  display: flex; align-items: center; gap: 14px;
}
.ma-cal__picker {
  position: relative;
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 6px;
  border-radius: 6px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-cal__picker:active { background: var(--ma-surface); }
.ma-cal__picker-label { pointer-events: none; }
.ma-cal__picker-chevron {
  width: 12px; height: 12px;
  pointer-events: none;
  opacity: 0.7;
}
.ma-cal__picker-select {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  border: none;
  font-size: 16px; /* prevent iOS zoom */
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
}
.ma-cal__dow {
  display: grid; grid-template-columns: repeat(7, 1fr);
  font-size: 10px; font-weight: 600; text-transform: uppercase;
  color: var(--ma-ink-3);
  letter-spacing: 0.04em;
  margin-bottom: 4px;
}
.ma-cal__dow span { text-align: center; padding: 4px 0; }
.ma-cal__grid {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.ma-cal__day {
  aspect-ratio: 1 / 1;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px;
  border: none;
  background: transparent;
  color: var(--ma-ink);
  cursor: pointer;
  border-radius: 8px;
  position: relative;
  -webkit-tap-highlight-color: transparent;
  font-family: inherit;
}
.ma-cal__day--blank { visibility: hidden; pointer-events: none; }
.ma-cal__day--today {
  font-weight: 700;
  outline: 1px solid var(--ma-ink-3);
  outline-offset: -3px;
}
.ma-cal__day--in-range {
  background: rgba(0, 153, 224, 0.14);
  border-radius: 0;
}
.ma-cal__day--start,
.ma-cal__day--end {
  background: var(--ma-accent);
  color: #fff;
  font-weight: 700;
  border-radius: 8px;
}
.ma-cal__day--start.ma-cal__day--end { border-radius: 8px; }
.ma-cal__day:active:not(.ma-cal__day--blank) { transform: scale(var(--ma-press-sm)); }

.ma-cal__helper {
  margin-top: 10px;
  font-size: 13px;
  color: var(--ma-ink-3);
  text-align: center;
  min-height: 18px;
}
.ma-cal__helper--result {
  color: var(--ma-ink);
  font-weight: 600;
}
.ma-cal__helper-clear {
  margin-left: 8px;
  font-size: 11px;
  color: var(--ma-accent);
  text-decoration: underline;
  cursor: pointer;
  background: none; border: none; padding: 0;
  font-family: inherit;
}
.ma-cal__apply {
  margin-top: 8px;
  width: 100%;
  padding: 11px;
  border-radius: 10px;
  border: none;
  background: var(--ma-accent);
  color: #fff;
  font: inherit; font-size: 14px; font-weight: 600;
  cursor: pointer;
  opacity: 0.4;
  transition: opacity 0.15s;
}
.ma-cal__apply--ready { opacity: 1; }
.ma-cal__apply--ready:active { transform: scale(var(--ma-press)); }

/* MARangeCal, modal wrapper */
.ma-cal-overlay {
  display: none;
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 9999;
  align-items: flex-end;
  justify-content: center;
}
.ma-cal-overlay.open { display: flex; }
.ma-cal-sheet {
  background: var(--ma-cream);
  border-radius: 18px 18px 0 0;
  width: 100%;
  max-width: 520px;
  max-height: min(90vh, var(--ma-sheet-max-h));
  display: flex; flex-direction: column;
  overflow: hidden;
  padding-bottom: var(--ma-inset-bottom);   /* full-screen home indicator */
  animation: ma-cal-slide-up 0.22s cubic-bezier(0.2, 0, 0, 1);
}
@keyframes ma-cal-slide-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
.ma-cal-sheet__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 18px 8px;
  flex-shrink: 0;
}
.ma-cal-sheet__title {
  font-size: 16px; font-weight: 700;
  color: var(--ma-ink);
}
.ma-cal-sheet__close {
  width: 32px; height: 32px; border-radius: 50%;
  border: none; background: var(--ma-surface);
  color: var(--ma-ink);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.ma-cal-sheet__close svg { width: 16px; height: 16px; }
.ma-cal-sheet__body {
  flex: 1;
  overflow-y: auto;
  padding: 4px 18px max(20px, env(safe-area-inset-bottom, 20px));
}
/* Inside the modal we don't need the top margin that the inline version had */
.ma-cal-sheet .ma-cal { margin-top: 0; }

/* ─── MAPager, shared pagination + search bar component ────────────── */
.ma-pager-search {
  margin-bottom: 10px;
}
.ma-pager-search input {
  width: 100%;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  background: var(--ma-surface);
  color: var(--ma-ink);
  font-size: 14px;
  font-family: inherit;
}
.ma-pager-info {
  font-size: 12px;
  color: var(--ma-ink-3);
  margin-bottom: 8px;
}
.ma-pager-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 14px 0;
  flex-wrap: wrap;
}
.ma-pager-nav button {
  min-width: 36px;
  height: 36px;
  padding: 0 8px;
  border-radius: 8px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  background: var(--ma-surface);
  color: var(--ma-ink);
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-pager-nav button:active { transform: scale(var(--ma-press-sm)); }
.ma-pager-nav button:disabled { opacity: 0.25; pointer-events: none; }
.ma-pager-nav button[aria-current="page"] {
  background: var(--ma-accent);
  color: #fff;
  border-color: var(--ma-accent);
}
.ma-pager-nav__ellipsis {
  width: 24px;
  text-align: center;
  font-size: 13px;
  color: var(--ma-ink-3);
  pointer-events: none;
}

/* ─── MATable, one set of rows drawn as cards or as a table (FE-10) ───
   A container carries data-density="cards|table" and MATable draws the
   matching shape. Motion is a function of density (hard rule 19): the
   card list keeps its entrance, a table row never animates. Every pixel
   count for a tier comes from --ma-bp-*, never a literal. */
.ma-table { display: block; }
.ma-table__el {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}
.ma-thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  text-align: left;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
  background: var(--ma-surface);
  border-bottom: 1px solid var(--ma-border);
  padding: 8px 10px;
  white-space: nowrap;
}
.ma-th--sort { cursor: pointer; user-select: none; }
.ma-th--on { color: var(--ma-ink); }
/* A numeric column's header sits over its cells. `.ma-thead th` above is
   one class plus one element, so a bare `.ma-th--right` (one class) loses
   to its `text-align: left`; the header of a Cost column was left-aligned
   over right-aligned cells. Compound it so the header follows its cells. */
.ma-thead th.ma-th--right { text-align: right; }
.ma-td {
  padding: 8px 10px;
  font-size: 13px;
  color: var(--ma-ink);
  border-bottom: 1px solid var(--ma-border);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ma-td--num { font-variant-numeric: tabular-nums; }
.ma-td--right { text-align: right; }
/* A table row never animates: no entrance, no transform. The active row
   is the keyboard cursor, a colour change only. */
.ma-trow { }
.ma-trow--active > .ma-td { background: var(--ma-cream); }
.ma-trow--card {
  animation: ma-tcard-in var(--ma-dur-swap) var(--ma-ease) both;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  padding: 10px 12px;
  margin-bottom: 8px;
}
.ma-tcard__main { font-size: 14px; font-weight: 600; color: var(--ma-ink); }
.ma-tcard__meta-row { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 2px; }
.ma-tcard__sub { font-size: 12px; color: var(--ma-ink-3); }
.ma-tcard__meta { font-size: 12px; color: var(--ma-ink-2); font-variant-numeric: tabular-nums; }
.ma-tempty { padding: 20px 10px; text-align: center; font-size: 13px; color: var(--ma-ink-3); }
@keyframes ma-tcard-in { from { opacity: 0; } to { opacity: 1; } }

/* --- Money out at the wide and desk tiers (FE-12) ---
   The list on the left and the thing you are looking at on the right.
   Every rule is gated on the FE-12 attribute the page only writes while
   WIDE_MONEY_OUT is on AND the shell's own wide/desk tier is on, so off
   (or on a phone) nothing below matches and the card list is untouched.
   The widths are the FE-7 tiers; the second column's measure comes from
   --ma-bp-*, never a literal. */
.ma-lines-wide {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ma-sp-6);
  align-items: start;
}
@media (min-width: 1100px) {
  /* The L2 detail becomes the second column at the desk tier and not
     before (FE-12): at 700 the list fills the width it has. The detail
     is empty until a row is opened, so at the wide tier it would be a
     blank half-screen. */
  .ma-lines-wide {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  }
}
.ma-lines-wide__list { min-width: 0; }
.ma-lines-wide__table { min-width: 0; }
.ma-lines-wide__detail { min-width: 0; position: sticky; top: var(--ma-sp-4); }
/* The same line sheet the phone gets, mounted into the column instead of
   over the page: no overlay, no drag handle, no rounded drag corner. */
.ma-ls--inline .ma-ls__sheet {
  position: static;
  max-height: none;
  border-radius: var(--ma-radius-2xl);
  border: 1px solid var(--ma-border);
  background: var(--ma-surface);
}
.ma-ls--inline .ma-ls-grab { display: none; }

/* ─── ALV (Augmented List View) components ─── */

/* MAFacetFilter, generalised multi-facet filter bar */
.ma-facet-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px;
  background: var(--ma-surface);
  border-radius: 12px;
  margin-bottom: 12px;
  align-items: flex-end;
}
/* Collapsible mode (the default) wins, sticky flat bar shouldn't
   inherit the .ma-facet-filter rounded card look. The pill carries
   its own padding; the panel-inner carries the tinted background.
   Solid theme bg is the fallback; modern browsers get the frosted-
   glass effect (semi-transparent + backdrop blur, iOS-system-bar
   style) so content under the sticky bar reads as a hinted blur
   rather than bleeding through. */
.ma-facet-filter.ma-facet-filter--collapsible {
  display: block;
  padding: 0;
  background: var(--ma-cream);
  border-radius: 0;
  margin-bottom: 8px;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ma-facet-filter.ma-facet-filter--collapsible {
    /* color-mix is widely supported in modern Chromium / Safari, but
       guard with the @supports block above so older WebViews still
       get the solid fallback above. */
    background: color-mix(in srgb, var(--ma-cream) 72%, transparent);
    -webkit-backdrop-filter: blur(14px) saturate(180%);
    backdrop-filter: blur(14px) saturate(180%);
  }
}
.ma-facet {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1 1 auto;
  min-width: 140px;
  position: relative;
}
.ma-facet__label {
  font-size: 11px;
  color: var(--ma-ink-3);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.ma-facet__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  position: relative;
}
.ma-facet__input {
  padding: 8px 10px;
  min-height: 36px;
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 8px;
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 13px;
  font-family: inherit;
  width: 100%;
  box-sizing: border-box;
}
.ma-facet__input:focus {
  outline: none;
  border-color: var(--ma-accent);
}
.ma-facet--daterange .ma-facet__daterange-inputs {
  display: flex;
  align-items: center;
  gap: 4px;
}
.ma-facet__daterange-sep {
  color: var(--ma-ink-3);
  font-size: 14px;
}
.ma-facet__daterange-start,
.ma-facet__daterange-end {
  flex: 1 1 0;
  min-width: 0;
}
.ma-facet__daterange-start[disabled],
.ma-facet__daterange-end[disabled] {
  opacity: 0.55;
  background: rgba(0, 0, 0, 0.03);
}
.ma-facet__pillgroup {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.ma-facet__pill {
  display: inline-block;
  padding: 6px 12px;
  min-height: 32px;
  border-radius: 16px;
  border: 1px solid rgba(0, 0, 0, 0.12);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-facet__pill--active {
  background: var(--ma-accent);
  color: #fff;
  border-color: var(--ma-accent);
}
.ma-facet__pill:active { transform: scale(var(--ma-press)); }
.ma-facet__clear {
  position: absolute;
  top: 0;
  right: 0;
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.12);
  background: var(--ma-cream);
  color: var(--ma-ink-3);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.ma-facet__clear:hover {
  background: var(--ma-brick-solid);
  color: #fff;
  border-color: var(--ma-brick-solid);
}
.ma-facet__search-icon {
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  color: var(--ma-ink-3);
  pointer-events: none;
  z-index: 1;
}
.ma-facet__search-icon svg { width: 16px; height: 16px; }
.ma-facet--search .ma-facet__input { padding-left: 30px; }
.ma-facet--multiselect .ma-facet__input,
.ma-facet--multiselect select[multiple] {
  height: auto;
  min-height: 72px;
}
.ma-facet-filter__clear-all {
  padding: 8px 14px;
  min-height: 36px;
  border-radius: 8px;
  border: 1px dashed rgba(0, 0, 0, 0.2);
  background: transparent;
  color: var(--ma-ink-3);
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  align-self: flex-end;
}
.ma-facet-filter__clear-all:hover {
  background: rgba(239, 68, 68, 0.08);
  border-color: var(--ma-brick);
  color: var(--ma-brick);
}

/* MAContextChip, linked context chips (receipt, recipe, item, …) */
.ma-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 12px;
  font-size: 12px;
  text-decoration: none;
  color: var(--ma-ink);
  background: rgba(0, 0, 0, 0.05);
  margin: 2px 4px 2px 0;
  vertical-align: middle;
  max-width: 100%;
}
.ma-chip:hover { background: rgba(0, 0, 0, 0.1); }
.ma-chip:active { transform: scale(var(--ma-press)); }
.ma-chip__icon {
  font-size: 12px;
  line-height: 1;
}
.ma-chip__label {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}
.ma-chip__meta {
  color: var(--ma-ink-3);
  margin-left: 2px;
  font-weight: 400;
}
.ma-chip--inactive {
  opacity: 0.4;
  pointer-events: none;
}
.ma-chip--receipt { background: rgba(36, 129, 204, 0.1); }
.ma-chip--recipe { background: rgba(245, 158, 11, 0.12); }
.ma-chip--item { background: rgba(16, 185, 129, 0.1); }
.ma-chip--supplier { background: rgba(139, 92, 246, 0.1); }
.ma-chip--ledger-entry { background: rgba(107, 114, 128, 0.12); }
.ma-chip--payment { background: rgba(234, 179, 8, 0.12); }
.ma-chip--stock-level { background: rgba(6, 182, 212, 0.1); }
.ma-chip--staff-member { background: rgba(236, 72, 153, 0.1); }

/* MARowActions, inline action button group */
.ma-row-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 8px;
}
.ma-row-action {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 8px 14px;
  min-height: 36px;
  border-radius: 8px;
  border: 1px solid rgba(0, 0, 0, 0.15);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-row-action:active { transform: scale(var(--ma-press)); }
.ma-row-action--primary {
  background: var(--ma-accent);
  color: #fff;
  border-color: var(--ma-accent);
}
.ma-row-action--danger {
  background: var(--ma-brick-solid);
  color: #fff;
  border-color: var(--ma-brick-solid);
}
.ma-row-action--disabled {
  opacity: 0.4;
  pointer-events: none;
}
.ma-row-action__icon {
  display: inline-flex;
  align-items: center;
  width: 14px;
  height: 14px;
}
.ma-row-action__icon svg { width: 14px; height: 14px; }

/* MARowRefresh, row base + transient refreshing state */
.ma-row {
  transition: opacity 0.15s, background 0.15s;
}
@keyframes ma-row-refresh-pulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 0.85; }
}
.ma-row--refreshing {
  animation: ma-row-refresh-pulse 0.9s ease-in-out infinite;
  pointer-events: none;
}

/* ── Settings list rows ────────────────────────────────────────────────
   Grouped row list used by the gear Settings hub (/settings) and the Staff
   settings page. Lifted here from staff_settings_page.html when the global
   Settings hub became a second consumer (reuse-first). A section is a card
   with internal dividers; each row is icon · body (name + value) · chevron.
   Icon tint modifiers map to the warm-earth accent tokens. */
.settings-section {
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  margin: 0 var(--ma-sp-5);
  overflow: hidden;
}
.settings-row {
  display: flex; align-items: center; gap: var(--ma-sp-3);
  padding: var(--ma-sp-3);
  cursor: pointer; user-select: none;
  text-decoration: none;
  color: var(--ma-ink);
}
.settings-row + .settings-row { border-top: 1px solid var(--ma-border); }
.settings-row:active { background: var(--ma-cream); }
.settings-row__icon {
  width: 32px; height: 32px;
  border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.settings-row__icon svg { width: 16px; height: 16px; }
.settings-row__icon--gold   { background: var(--ma-gold-soft);   color: var(--ma-gold-ink); }
.settings-row__icon--brick  { background: var(--ma-brick-soft);  color: var(--ma-brick-ink); }
.settings-row__icon--ink    { background: var(--ma-ink);         color: var(--ma-cream); }
.settings-row__icon--forest { background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.settings-row__icon--zuzo   { background: var(--ma-zuzo-soft, rgba(139,123,232,.18)); color: var(--ma-zuzo-ink); }
.settings-row__icon--blue   { background: var(--ma-blue-soft);   color: var(--ma-blue-ink); }
.settings-row__body { flex: 1; min-width: 0; }
.settings-row__name {
  font-size: 13.5px; font-weight: 800;
  color: var(--ma-ink); line-height: 1.2;
}
.settings-row__value {
  margin-top: 2px;
  font-size: 11.5px; font-weight: 600;
  color: var(--ma-ink-3);
}
.settings-row__value-num {
  font-family: var(--ma-font-num);
  font-weight: 700;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.settings-row__chev {
  flex: 0 0 auto;
  color: var(--ma-ink-3);
}
.settings-row__chev svg { width: 14px; height: 14px; }
.settings-help {
  margin: var(--ma-sp-2) var(--ma-sp-5);
  font-size: 11.5px;
  color: var(--ma-ink-3);
  line-height: 1.4;
}

/* MAFixModal, reuses the modal-overlay / modal-sheet bottom-sheet pattern.
   Base classes live in shared CSS so pages that only use MAFixModal don't
   need to redefine them. z-index 9999 keeps modal above bottom nav (9000). */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 9999;
  display: none;
  align-items: flex-end;
  justify-content: center;
}
.modal-overlay.is-open { display: flex; }
.modal-sheet {
  background: var(--ma-cream);
  color: var(--ma-ink);
  width: 100%;
  max-width: 560px;
  max-height: min(90vh, var(--ma-sheet-max-h));
  border-radius: 16px 16px 0 0;
  display: flex;
  flex-direction: column;
  padding-bottom: var(--ma-inset-bottom);   /* full-screen home indicator */
}
.modal-header {
  padding: 14px 16px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.modal-header h2 {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
}
.modal-body {
  padding: 12px 16px;
  overflow-y: auto;
  flex: 1;
}
.modal-footer {
  padding: 12px 16px;
  display: flex;
  gap: 8px;
  border-top: 1px solid rgba(0, 0, 0, 0.06);
  justify-content: flex-end;
}
.modal-btn {
  padding: 10px 18px;
  min-height: 40px;
  border-radius: 10px;
  border: 1px solid rgba(0, 0, 0, 0.15);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}
.modal-btn--ghost { background: transparent; }
.modal-btn--zuzo { background: var(--ma-zuzo); color: #fff; border-color: var(--ma-zuzo); }
.modal-btn--primary {
  background: var(--ma-accent);
  color: #fff;
  border-color: var(--ma-accent);
}

.ma-fix-modal .modal-body { max-height: 70vh; }
.ma-fix-modal__context {
  margin-bottom: 10px;
  font-size: 13px;
  color: var(--ma-ink-3);
}
.ma-fix-modal__context p { margin: 0 0 8px 0; }
.ma-fix-modal__context-display {
  padding: 8px 10px;
  background: var(--ma-surface);
  border-radius: 8px;
  font-size: 13px;
  color: var(--ma-ink);
  font-weight: 600;
}
.ma-fix-modal__form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.ma-fix-modal__field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ma-fix-modal__field label {
  font-size: 12px;
  font-weight: 600;
  color: var(--ma-ink-3);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.ma-fix-modal__field input[type="text"],
.ma-fix-modal__field input[type="number"],
.ma-fix-modal__field input[type="date"],
.ma-fix-modal__field select,
.ma-fix-modal__field textarea {
  padding: 10px 12px;
  min-height: 40px;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 8px;
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 14px;
  font-family: inherit;
  width: 100%;
  box-sizing: border-box;
}
.ma-fix-modal__field textarea { resize: vertical; min-height: 72px; }
.ma-fix-modal__field--required label::after {
  content: ' *';
  color: var(--ma-brick);
}
.ma-fix-modal__radio {
  display: block;
  padding: 6px 0;
  font-size: 14px;
}
.ma-fix-modal__radio input { margin-right: 6px; }
.ma-fix-modal__help {
  font-size: 12px;
  color: var(--ma-ink-3);
}
.ma-fix-modal__error {
  padding: 10px 12px;
  background: rgba(239, 68, 68, 0.12);
  color: var(--ma-brick);
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  margin-top: 10px;
}

/* ───────────────────────── MAEntityPicker ───────────────────────── */
/* MAEntityPicker, app-wide single-pick bottom sheet.
   Redesigned May 2026 per design/design_handoff_entity_picker:
   drag handle, sticky search, optional filter chips with counts,
   sectioned cards with section heads (count badge), tap-to-select
   dismisses for single-select (no Cancel for single), tinted-icon
   empty state, sticky "+ Add new" dashed CTA. Multi-select keeps
   the Done footer (only valid use of a confirm button here).
   Theme tokens (warm cream / charcoal), no --tg-theme-* fallbacks.
   ─────────────────────────────────────────────────────────────── */
.ma-picker-overlay {
  position: fixed; inset: 0;
  background: rgba(20, 18, 14, 0.55);
  z-index: 10000;
  display: none;
  align-items: flex-end;
  justify-content: center;
}
[data-theme="dark"] .ma-picker-overlay {
  background: rgba(0, 0, 0, 0.65);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ma-picker-overlay {
    background: rgba(0, 0, 0, 0.65);
  }
}
.ma-picker-overlay.is-open { display: flex; }

.ma-picker-sheet {
  background: var(--ma-surface);
  color: var(--ma-ink);
  width: 100%;
  max-width: 560px;
  max-height: min(92vh, var(--ma-sheet-max-h));
  border-radius: 20px 20px 0 0;
  border-top: 1px solid var(--ma-border);
  box-shadow: 0 -10px 40px rgba(20, 18, 14, 0.25);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: var(--ma-font-body);
  padding-bottom: var(--ma-inset-bottom);   /* full-screen home indicator */
}
[data-theme="dark"] .ma-picker-sheet {
  box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.6);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ma-picker-sheet {
    box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.6);
  }
}

.ma-picker-handle {
  width: 38px; height: 4px;
  border-radius: 999px;
  background: var(--ma-border-strong);
  margin: 10px auto 4px;
  flex: 0 0 auto;
}

.ma-picker-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 16px 12px;
  border-bottom: none;
  flex: 0 0 auto;
}
.ma-picker-header h2 {
  margin: 0;
  font-size: 18px; font-weight: 800;
  letter-spacing: -0.3px;
  color: var(--ma-ink);
}
.ma-picker-close {
  width: 30px; height: 30px;
  border-radius: 999px;
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  color: var(--ma-ink-3);
  cursor: pointer; padding: 0;
  font-size: 14px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.ma-picker-close:active { transform: scale(var(--ma-press-sm)); }

.ma-picker-search {
  padding: 0 16px 10px;
  border-bottom: none;
  flex: 0 0 auto;
}
.ma-picker-search-wrap {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 12px;
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  border-radius: 12px;
}
.ma-picker-search-wrap svg {
  width: 16px; height: 16px;
  color: var(--ma-ink-3);
  flex: 0 0 auto;
}
.ma-picker-search input {
  flex: 1; min-width: 0;
  border: none; outline: none; background: transparent;
  font: 600 13.5px var(--ma-font-body);
  color: var(--ma-ink);
  padding: 0;
}
.ma-picker-search input::placeholder { color: var(--ma-ink-3); font-weight: 500; }
.ma-picker-search-clear {
  width: 22px; height: 22px;
  border-radius: 999px;
  background: var(--ma-border);
  color: var(--ma-ink-2);
  border: none; cursor: pointer;
  font-size: 11px; line-height: 1;
  display: none;
  align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.ma-picker-search input:not(:placeholder-shown) ~ .ma-picker-search-clear,
.ma-picker-search-clear.is-visible {
  display: inline-flex;
}

/* Filter chips. */
.ma-picker-filters {
  padding: 0 16px 12px;
  display: flex; gap: 6px;
  overflow-x: auto;
  flex: 0 0 auto;
  scrollbar-width: none;
}
.ma-picker-filters::-webkit-scrollbar { display: none; }
.ma-picker-chip {
  padding: 7px 12px;
  border-radius: 999px;
  background: var(--ma-cream);
  color: var(--ma-ink-2);
  border: 1px solid var(--ma-border);
  font: 700 12px var(--ma-font-body);
  cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px;
}
.ma-picker-chip[aria-pressed="true"] {
  background: var(--ma-ink);
  color: var(--ma-cream);
  border-color: var(--ma-ink);
}
.ma-picker-chip:active { transform: scale(var(--ma-press)); }
.ma-picker-chip-count {
  font-family: var(--ma-font-num);
  font-size: 11px; font-weight: 700;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--ma-border);
  color: var(--ma-ink-2);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-picker-chip[aria-pressed="true"] .ma-picker-chip-count {
  background: rgba(255, 255, 255, 0.2);
  color: var(--ma-cream);
}

.ma-picker-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 16px 8px;
}

.ma-picker-section {
  margin-bottom: 14px;
}
.ma-picker-section__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 4px 8px;
  font: 800 10.5px var(--ma-font-body);
  letter-spacing: 1px; text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-picker-section__count {
  font-family: var(--ma-font-num);
  opacity: 0.8;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-picker-section__card {
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  border-radius: 14px;
  overflow: hidden;
}

.ma-picker-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  text-align: left;
  padding: 12px;
  background: transparent;
  border: none;
  color: var(--ma-ink);
  cursor: pointer;
  font-family: var(--ma-font-body);
}
.ma-picker-row + .ma-picker-row {
  border-top: 1px solid var(--ma-border);
}
.ma-picker-row:hover, .ma-picker-row:focus-visible {
  background: var(--ma-brick-soft);
  outline: none;
}
.ma-picker-row:active { transform: scale(var(--ma-press-lg)); }

/* MAEntityPicker, folding shelves (collapsible), actions under the list,
   the caller's slot at the top. The shelf header is MAAccordion's. */
.ma-picker-body .ma-acc { margin: 0 0 12px; }
.ma-picker-body .ma-acc > .ma-acc__h + * { margin-top: 4px; }
.ma-picker-slot:empty { display: none; }
.ma-picker-actions { margin-top: 4px; padding-top: 6px; border-top: 1px solid var(--ma-border); }
.ma-picker-action {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; min-height: 52px; padding: 10px 12px;
  text-align: left; background: transparent; border: none; border-radius: 12px;
  color: var(--ma-brick); cursor: pointer; font-family: var(--ma-font-body);
}
.ma-picker-action:hover, .ma-picker-action:focus-visible { background: var(--ma-brick-soft); outline: none; }
.ma-picker-action:active { transform: scale(var(--ma-press-lg)); }
.ma-picker-action__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ma-picker-action__label { font: 700 14px var(--ma-font-body); }
.ma-picker-action__hint { font: 600 11.5px var(--ma-font-body); color: var(--ma-ink-3); }
.ma-picker-action svg { width: 16px; height: 16px; flex: 0 0 auto; }
.ma-picker-row .ma-ladder { flex: 0 0 auto; }
.ma-picker-row.is-selected {
  background: var(--ma-brick-soft);
}
/* Multi-select checkbox, only shown when picker is in multi mode. */
.ma-picker-row__check {
  flex-shrink: 0;
  width: 22px; height: 22px;
  border-radius: 6px;
  border: 1.5px solid var(--ma-border-strong);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800;
  color: var(--ma-brick);
  background: var(--ma-surface);
}
.ma-picker-row.is-selected .ma-picker-row__check {
  background: var(--ma-brick-solid); color: #fff; border-color: var(--ma-brick-solid);
}
.ma-picker-row__main {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0; flex: 1;
}
.ma-picker-row__label {
  font-size: 14px; font-weight: 800;
  color: var(--ma-ink); line-height: 1.25;
}
.ma-picker-row__meta {
  font-size: 12px; font-weight: 600;
  color: var(--ma-ink-3); line-height: 1.35;
}
.ma-picker-row__chev {
  flex: 0 0 auto;
  color: var(--ma-ink-3);
}
.ma-picker-row__chev svg { width: 14px; height: 14px; }

/* Muted section rows, smaller avatars, dimmer names, no chev. */
.ma-picker-row--muted .ma-picker-row__label {
  font-size: 13.5px;
  color: var(--ma-ink-2);
}

/* Empty state, tinted circle around a search icon + helper text. */
.ma-picker-empty {
  padding: 40px 20px;
  text-align: center;
  color: var(--ma-ink-3);
}
.ma-picker-empty__icon {
  width: 56px; height: 56px;
  margin: 0 auto 12px;
  border-radius: 16px;
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  color: var(--ma-ink-3);
  display: inline-flex; align-items: center; justify-content: center;
}
.ma-picker-empty__icon svg { width: 22px; height: 22px; }
.ma-picker-empty__title {
  font-size: 14px; font-weight: 800;
  color: var(--ma-ink-2);
}
.ma-picker-empty__hint {
  font-size: 12px; font-weight: 600;
  margin-top: 4px;
}

/* Footer, multi-select Done; or single-select "+ Add new" dashed CTA;
   or hidden when neither applies. */
.ma-picker-footer {
  display: flex; gap: 8px;
  padding: 8px 16px 16px;
  border-top: 1px solid var(--ma-border);
  background: var(--ma-surface);
  flex: 0 0 auto;
}
.ma-picker-btn {
  flex: 1;
  padding: 13px 16px;
  border-radius: 14px;
  border: none;
  font: 700 14px var(--ma-font-body);
  cursor: pointer;
}
.ma-picker-btn:active { transform: scale(var(--ma-press)); }
.ma-picker-btn--ghost {
  background: transparent;
  color: var(--ma-ink);
  border: 1.5px solid var(--ma-border-strong);
}
.ma-picker-btn--primary {
  background: var(--ma-brick-solid);
  color: #fff;
  font-weight: 800;
}
.ma-picker-btn--add {
  background: var(--ma-cream);
  color: var(--ma-brick);
  border: 1.5px dashed var(--ma-brick);
  font-weight: 800;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.ma-picker-btn--add svg { width: 15px; height: 15px; }

/* MACategoriseModal, categorisation modal sheet
   Shares the .ma-picker-overlay shell + footer button styles above so it
   stacks correctly with MAEntityPicker (z-index 9999 > bottom-nav 9000).
   Distinct content classes (.ma-cat-*) so styling can evolve without
   bleeding into MAEntityPicker. */
.ma-cat-sheet { max-height: min(90vh, var(--ma-sheet-max-h)); }
.ma-cat-subtitle {
  padding: 0 16px 8px;
  font-size: 12px;
  color: var(--ma-ink-3);
}
.ma-cat-body {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 4px 14px 8px;
  display: flex; flex-direction: column; gap: 12px;
}
.ma-cat-rail-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 2px;
  font-size: 12px; font-weight: 600;
  color: var(--ma-ink-3);
  text-transform: uppercase; letter-spacing: 0.4px;
}
.ma-cat-rail-title { letter-spacing: 0.6px; }
.ma-cat-sparkle {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid rgba(127,127,127,0.25);
  background: transparent;
  color: var(--ma-accent);
  font-size: 12px; font-weight: 600;
  cursor: pointer; font-family: inherit;
  text-transform: none; letter-spacing: 0;
}
.ma-cat-sparkle:hover { background: rgba(0,153,224,0.06); }
.ma-cat-sparkle:disabled { opacity: 0.55; cursor: wait; }
.ma-cat-sparkle.is-busy svg { animation: ma-cat-sparkle-spin 0.8s linear infinite; }
@keyframes ma-cat-sparkle-spin { to { transform: rotate(360deg); } }

.ma-cat-rail { display: flex; flex-direction: column; gap: 8px; }
.ma-cat-empty {
  padding: 16px 8px;
  text-align: center;
  font-size: 13px;
  color: var(--ma-ink-3);
}

.ma-cat-card {
  border: 1px solid rgba(127,127,127,0.18);
  border-radius: 12px;
  padding: 10px 12px;
  display: flex; flex-direction: column; gap: 6px;
  background: var(--ma-cream);
}
.ma-cat-card--new {
  border-color: rgba(34,197,94,0.45);
  background: rgba(34,197,94,0.04);
}
.ma-cat-card-main {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 14px;
}
.ma-cat-card-key {
  font-weight: 700;
  color: var(--ma-ink);
}
.ma-cat-card-cat {
  font-size: 12px;
  color: var(--ma-ink-3);
}
.ma-cat-card-conf {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px; font-weight: 700;
}
.ma-cat-card-conf.is-high { background: rgba(34,197,94,0.15); color: var(--ma-forest-ink); }
.ma-cat-card-conf.is-mid  { background: rgba(234,179,8,0.15); color: var(--ma-gold-ink); }
.ma-cat-card-conf.is-low  { background: rgba(239,68,68,0.15); color: var(--ma-brick); }
.ma-cat-card-newpill {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 10px; font-weight: 800; letter-spacing: 0.5px;
  background: var(--ma-forest-ink); color: white;
}
.ma-cat-card-reason {
  font-size: 12px;
  color: var(--ma-ink-3);
  line-height: 1.4;
}
.ma-cat-similar {
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 6px;
  background: rgba(234,179,8,0.12);
  color: var(--ma-gold-ink);
}
.ma-cat-card-btn {
  align-self: flex-end;
  padding: 8px 14px;
  border-radius: 8px;
  border: none;
  font-size: 13px; font-weight: 600;
  cursor: pointer; font-family: inherit;
}
.ma-cat-card-btn--primary { background: var(--ma-accent); color: white; }

/* ───────────────────────────────────────────────────────────────
   MACategoriseModal, ranked-card layout (the design's "Zuzo's
   Top Guesses" look). One row per suggestion: numbered circle ·
   item name + category · reason · confidence bar · Apply button.
   The top-1 row gets a lavender outline so it reads as the
   primary suggestion.
   ─────────────────────────────────────────────────────────── */
.ma-cat-rank-card {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-cat-rank-card--top {
  border-color: var(--ma-zuzo);
  box-shadow: 0 0 0 1px var(--ma-zuzo);
  background: color-mix(in srgb, var(--ma-zuzo-soft) 70%, var(--ma-surface));
}
.ma-cat-rank-card__num {
  width: 28px; height: 28px;
  border-radius: 999px;
  background: var(--ma-zuzo-soft);
  color: var(--ma-zuzo-ink);
  font-family: var(--ma-font-num);
  font-weight: 800; font-size: 12.5px;
  display: inline-flex; align-items: center; justify-content: center;
  align-self: flex-start;
}
.ma-cat-rank-card--top .ma-cat-rank-card__num {
  background: var(--ma-zuzo); color: #fff;
}
.ma-cat-rank-card__body { min-width: 0; }
.ma-cat-rank-card__head {
  font: 700 13px var(--ma-font-body);
  color: var(--ma-ink);
  letter-spacing: -0.1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ma-cat-rank-card__cat {
  font-weight: 600; color: var(--ma-ink-3);
  margin-left: 4px;
}
.ma-cat-rank-card__reason {
  margin-top: 2px;
  font: 600 11.5px var(--ma-font-body);
  color: var(--ma-ink-3);
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ma-cat-rank-card__bar {
  height: 3px;
  margin-top: 6px;
  border-radius: 999px;
  background: var(--ma-border);
  overflow: hidden;
}
.ma-cat-rank-card__fill {
  height: 100%;
  background: var(--ma-zuzo);
  border-radius: 999px;
}
.ma-cat-rank-card__actions {
  display: flex; flex-direction: column; align-items: flex-end;
  gap: 6px;
  align-self: stretch;
  justify-content: space-between;
}
.ma-cat-rank-card__conf {
  font-family: var(--ma-font-num);
  font-weight: 800; font-size: 11.5px;
  color: var(--ma-zuzo-ink);
  background: var(--ma-zuzo-soft);
  padding: 1px 8px;
  border-radius: 999px;
}
.ma-cat-rank-card--top .ma-cat-rank-card__conf {
  background: color-mix(in srgb, var(--ma-zuzo) 28%, transparent);
  color: var(--ma-zuzo-ink);
}
.ma-cat-rank-card__apply {
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--ma-zuzo-soft);
  color: var(--ma-zuzo-ink);
  border: 1px solid var(--ma-zuzo);
  font: 700 12px var(--ma-font-body);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-cat-rank-card--top .ma-cat-rank-card__apply {
  background: var(--ma-zuzo); color: #0E0B26;
}
.ma-cat-rank-card__apply:active { transform: scale(var(--ma-press)); }

/* New-item variant, green outline, "+ NEW" pill in place of the
   numbered circle. Reuses the same grid + body styling. */
.ma-cat-rank-card--new {
  border-color: color-mix(in srgb, var(--ma-forest) 60%, var(--ma-border));
  background: color-mix(in srgb, var(--ma-forest-soft) 50%, var(--ma-surface));
}
.ma-cat-rank-card--new .ma-cat-rank-card__num {
  background: var(--ma-forest); color: #fff;
  font-size: 10px;
  width: 32px;
  border-radius: 8px;
}
.ma-cat-rank-card--new .ma-cat-rank-card__apply {
  background: var(--ma-forest); color: #fff; border-color: var(--ma-forest);
}

/* "Apply to all N instances" checkbox row below the ranked cards.
   Default checked, most categorisations should sweep all matching
   raw_name lines. The alias write that lands on Apply will cover
   future receipts automatically; the checkbox tells the backend
   whether to also backfill matching lines in the existing payload. */
.ma-cat-apply-all {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  background: var(--ma-zuzo-soft);
  border: 1px solid color-mix(in srgb, var(--ma-zuzo) 40%, var(--ma-border));
  border-radius: 10px;
  margin-top: 2px;
}
.ma-cat-apply-all input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--ma-zuzo);
  flex: 0 0 auto;
  margin-top: 1px;
}
.ma-cat-apply-all__body { min-width: 0; flex: 1; }
.ma-cat-apply-all__head {
  font: 700 13px var(--ma-font-body);
  color: var(--ma-ink);
  letter-spacing: -0.1px;
}
.ma-cat-apply-all__head em {
  font-style: normal;
  color: var(--ma-zuzo-ink);
  font-weight: 800;
}
.ma-cat-apply-all__sub {
  margin-top: 2px;
  font: 600 11.5px var(--ma-font-body);
  color: var(--ma-ink-3);
}

/* "OR DO IT MANUALLY" divider, eyebrow text with hairlines on
   either side. Sits between the AI cards and the manual fallbacks. */
.ma-cat-divider {
  display: flex; align-items: center; gap: 10px;
  margin: 6px 0 2px;
  font: 700 10.5px var(--ma-font-body);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-cat-divider::before,
.ma-cat-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--ma-border);
}

/* Larger "Pick an existing item" card, the prominent manual
   fallback below the divider. Replaces the smaller ma-cat-pick-
   existing button on the redesign. */
.ma-cat-pick-card {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 14px;
  border-radius: 12px;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  text-align: left;
  font-family: inherit;
}
.ma-cat-pick-card:active { transform: scale(var(--ma-press-lg)); }
.ma-cat-pick-card__icon {
  width: 38px; height: 38px;
  border-radius: 10px;
  background: var(--ma-border);
  color: var(--ma-ink-2);
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.ma-cat-pick-card__icon svg { width: 18px; height: 18px; }
.ma-cat-pick-card__body { flex: 1; min-width: 0; }
.ma-cat-pick-card__head {
  font: 700 14px var(--ma-font-body);
  color: var(--ma-ink);
  letter-spacing: -0.1px;
}
.ma-cat-pick-card__sub {
  margin-top: 2px;
  font: 600 12px var(--ma-font-body);
  color: var(--ma-ink-3);
}
.ma-cat-pick-card__chev {
  color: var(--ma-ink-3);
  flex: 0 0 auto;
}

.ma-cat-section {
  border: 1px solid rgba(127,127,127,0.15);
  border-radius: 10px;
  padding: 0 10px;
  background: var(--ma-surface);
}
.ma-cat-section > summary {
  cursor: pointer;
  padding: 10px 4px;
  font-size: 13px; font-weight: 600;
  color: var(--ma-ink);
  list-style: none;
}
.ma-cat-section > summary::-webkit-details-marker { display: none; }
.ma-cat-section > summary::before {
  content: "›";
  display: inline-block;
  margin-right: 8px;
  transition: transform 0.15s ease;
}
.ma-cat-section[open] > summary::before { transform: rotate(90deg); }

/* ── MAAccordion, collapsible sections that remember ─────────────────
   <details class="ma-acc" data-acc="meat" open>
     <summary class="ma-acc__h">chevron · label · sub · count</summary>
     …the section body (a .rx-list, a card, rows)…
   </details>
   The header is the same section label every list page draws (12px,
   700, uppercase, ink-3) with a 44px tap target; the chevron turns, the
   body sits one small step below. Open/closed is remembered by
   MAAccordion.mount, nothing here depends on it. */
.ma-acc { margin: var(--ma-sp-5) 0 0; }
.ma-acc > summary { list-style: none; }
.ma-acc > summary::-webkit-details-marker { display: none; }
/* The head of a fold is a CONTROL, so it is drawn as one: a heading in
   sentence case at full ink, not a caption.

   It used to be 12px/700 uppercase on .08em tracking in `--ma-ink-3` -
   pale warm grey, which is the micro-label style `.ma-section-title`
   uses. That style is right for a caption ("HOW YOUR SHOPS PRICE"
   labels a block and does nothing when you tap it) and wrong for the
   one row on the screen that opens a shelf: "MEAT" read as overhead
   above the list rather than as the way into it, and at 12px pale grey
   it was the least legible text on a screen full of things to tap.

   Two consumers had already worked around it rather than live with it -
   `.ma-td-group` and `.ma-lines-group` each turn the tracking, the
   uppercase and the colour back off locally. A second copy of the same
   override is the reuse-first rule's signal that the BASE is wrong, so
   this is the base changing and Today dropping its copy.

   Uppercase stays where it means something: a caption is a tracked
   micro-label, a fold head is a heading. The two roles now look
   different, which they did not when both were 12px grey capitals.

   And sentence case is the plainer read, root CLAUDE.md's copy rule
   asks that a Grade-9 reader with English as a second language gets it
   first time, and "Meat" clears that more easily than "M E A T". */
.ma-acc__h {
  display: flex; align-items: center; gap: var(--ma-sp-2);
  /* The head shares its card's content inset.
     `padding: 0 2px` is right for a fold at PAGE level, where the body
     already gutters 14px. Inside a card it was wrong and nothing said
     so: the rows carry the card's own padding (Things 11px 12px, Lines
     9px 10px) while the head carried 2px, so the chevron hung ~10px
     left of the names under it and the count hung ~10px right of their
     chips, a `4` three pixels off the card's edge while every row in
     the same card sat thirteen (user 2026-09-16, "the values are too
     close to the card edge").
     A card states its own inset rather than this file listing cards: a
     new folding card that says nothing falls back to 2px, which is
     exactly today's behaviour, so this can only help.
     Today (`.ma-td-group`) is the one card this does not describe, its
     rows are nested cards of their own (`.ma-td-card`, its own border
     and padding) rather than rows flush to the edge, so its head sets
     its padding outright and opts out of the property.
     `tests/e2e/fold_inset.py` measures head against rows. */
  min-height: 44px; padding: 0 var(--ma-acc-inset, 2px);
  cursor: pointer; user-select: none; -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  font: 700 14px var(--ma-font-body); letter-spacing: 0; text-transform: none;
  color: var(--ma-ink);
}
.ma-acc__h:active { opacity: var(--ma-press-fade); }
.ma-acc__chev {
  width: 14px; height: 14px; flex: 0 0 auto; display: inline-flex;
  transition: transform var(--ma-dur-swap) var(--ma-ease);
}
.ma-acc__chev svg { width: 100%; height: 100%; }
.ma-acc[open] > .ma-acc__h .ma-acc__chev { transform: rotate(90deg); }
.ma-acc__l { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ma-acc__sub {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 400; letter-spacing: 0; text-transform: none; font-size: 11.5px;
}
.ma-acc__n {
  margin-left: auto; flex: 0 0 auto;
  font-family: var(--ma-font-num); font-weight: 700; font-size: 12px;
  letter-spacing: 0; text-transform: none;
  /* Secondary, deliberately. The head is full ink now, so a count in
     the same colour would read as a second heading at the far end of
     the row rather than as how many are inside. */
  color: var(--ma-ink-3);
}
.ma-acc > .ma-acc__h + * { margin-top: var(--ma-sp-2); }

.ma-cat-pick-existing {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%;
  margin: 12px 0 8px;
  padding: 12px;
  border-radius: 8px;
  border: 1px solid rgba(127,127,127,0.3);
  background: transparent;
  font-size: 14px; font-weight: 600;
  color: var(--ma-accent);
  cursor: pointer; font-family: inherit;
}
.ma-cat-pick-existing:hover { background: rgba(0,153,224,0.06); }
.ma-cat-pick-existing:active { opacity: var(--ma-press-fade); }

.ma-cat-create {
  display: flex; flex-direction: column; gap: 10px;
  padding: 4px 0 12px;
}
.ma-cat-create label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 12px; font-weight: 600;
  color: var(--ma-ink-3);
}
.ma-cat-create input,
.ma-cat-create select {
  padding: 10px;
  border-radius: 8px;
  border: 1px solid rgba(127,127,127,0.25);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 14px; font-family: inherit;
}
.ma-cat-create input:focus,
.ma-cat-create select:focus { outline: 2px solid var(--ma-accent); outline-offset: -1px; }

/* .ma-ask-input, a number and a button on one row: the review deck's
   Count the whole slip and MAOrganise.askNumber (How many packs?). Was
   the old set-quantity modal's input row; kept when that modal went. */
.ma-ask-input {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 4px 0;
}
.ma-ask-input input[type="number"] {
  flex: 1 1 120px;
  padding: 10px;
  border-radius: 8px;
  border: 1px solid rgba(127,127,127,0.25);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 15px; font-family: inherit;
  min-width: 0;
}
.ma-ask-input input:focus { outline: 2px solid var(--ma-accent); outline-offset: -1px; }

/* MAPriceHistoryModal, per-canonical-item price-history chart */
.ma-ph-sheet { max-height: min(90vh, var(--ma-sheet-max-h)); }
.ma-ph-chart {
  width: 100%;
  margin: 4px 0 12px;
  background: var(--ma-surface);
  border-radius: 8px;
  padding: 8px;
}
.ma-ph-chart svg { width: 100%; height: auto; display: block; }
.ma-ph-legend {
  display: flex; flex-wrap: wrap; gap: 12px;
  font-size: 12px;
  color: var(--ma-ink-3);
  margin-bottom: 12px;
}
.ma-ph-legend__item {
  display: inline-flex; align-items: center; gap: 6px;
}
.ma-ph-legend__swatch {
  display: inline-block;
  width: 12px; height: 12px;
  border-radius: 3px;
}
.ma-ph-table__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.ma-ph-table__table th,
.ma-ph-table__table td {
  padding: 6px 8px;
  text-align: left;
  border-bottom: 1px solid rgba(127,127,127,0.15);
}
.ma-ph-table__table th {
  font-weight: 600;
  color: var(--ma-ink-3);
}
.ma-ph-table__price {
  font-weight: 600;
  text-align: right;
  color: var(--ma-accent);
}

/* MAFacetFilter, entitypicker button styling */
.ma-facet--entitypicker .ma-facet__entitypicker {
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(127,127,127,0.25);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 14px;
  cursor: pointer;
  font-family: inherit;
}
.ma-facet--entitypicker .ma-facet__entitypicker:active { opacity: var(--ma-press-fade); }

/* ─────────────────────────────────────────────────────────────
   MAProvenanceChip, tier-coloured pill explaining where a
   resolved value came from. Paired with shared/receipt_readiness.py
   (Python) which emits the source tokens. One class per tier;
   kept compact to sit next to a value on a list row.
   ───────────────────────────────────────────────────────────── */
.ma-prov-chip {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 9px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.55;
  letter-spacing: 0.01em;
  white-space: nowrap;
  vertical-align: baseline;
  border: 1px solid transparent;
  background: var(--ma-surface);
  color: var(--ma-ink-3);
}
/* Durable human state, solid accent fill, strongest visual weight. */
.ma-prov-chip--locked {
  background: var(--ma-accent);
  color: #fff;
  border-color: var(--ma-accent);
}
/* Human approved (not yet locked), accent outline. */
.ma-prov-chip--human {
  background: transparent;
  color: var(--ma-accent);
  border-color: var(--ma-accent);
}
/* AI at / above auto-approve threshold, soft green. */
.ma-prov-chip--ai {
  background: rgba(34, 197, 94, 0.14);
  color: #166534;
  border-color: rgba(34, 197, 94, 0.3);
}
/* AI below auto-approve, amber caution. */
.ma-prov-chip--ai-extracted {
  background: rgba(245, 158, 11, 0.16);
  color: var(--ma-gold-ink);
  border-color: rgba(245, 158, 11, 0.35);
}
/* Resolver tier 2, alias EMA. Accent-muted. */
.ma-prov-chip--alias {
  background: rgba(0, 153, 224, 0.12);
  color: #075985;
  border-color: rgba(0, 153, 224, 0.28);
}
/* Resolver tier 1.5, supplier_price (PLAN-supplier-price-resolver-tier).
   Cost-anchored qty derivation; same teal family as alias since both are
   resolver-derived, slightly bolder to differentiate. */
.ma-prov-chip--supplier-price {
  background: rgba(20, 184, 166, 0.14);
  color: #0f766e;
  border-color: rgba(20, 184, 166, 0.32);
}
/* Resolver tier 3, item default. Neutral slate. */
.ma-prov-chip--item-default {
  background: rgba(100, 116, 139, 0.12);
  color: #334155;
  border-color: rgba(100, 116, 139, 0.28);
}
/* Resolver tier 4, category default. Paler, italic to signal fallback. */
.ma-prov-chip--category-default {
  background: rgba(100, 116, 139, 0.06);
  color: #64748b;
  border-color: rgba(100, 116, 139, 0.2);
  font-style: italic;
}
/* ── MACategoryPanel, what a bar is made of, inside the bar ───────
   Drawn into `MACharts.rows`'s panel, so it owns no fold and no
   chevron: the chart's row already opened. Two lines a row, because
   the question is what was bought, how much of it and where. */
.ma-cat-panel { padding: 2px 0 var(--ma-sp-3); }
.ma-cat-said {
  font-size: 10px; font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
  padding: 0 var(--ma-sp-3) var(--ma-sp-2);
}
.ma-cat-empty {
  padding: var(--ma-sp-4) var(--ma-sp-3);
  font-size: 12.5px; color: var(--ma-ink-3, #8A8077);
}
.ma-cat-items { display: block; }
.ma-cat-item { border-top: 1px solid var(--ma-border, #E6DFD2); }
.ma-cat-items > .ma-cat-item:first-child { border-top: none; }
.ma-cat-row {
  display: flex; flex-direction: column; gap: 3px;
  width: 100%; text-align: left; cursor: pointer;
  background: none; border: none; color: inherit; font: inherit;
  padding: var(--ma-sp-3);
  -webkit-tap-highlight-color: transparent;
}
/* Nothing behind it to open, see MACategoryPanel.itemHtml. */
.ma-cat-row--flat { cursor: default; }
.ma-cat-row__top,
.ma-cat-row__sub {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.ma-cat-row__name {
  font: 600 13.5px var(--ma-font-body); color: var(--ma-ink, #1F2D2A);
  line-height: 1.3; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-cat-row__amt {
  font-family: var(--ma-font-num); font-variant-numeric: tabular-nums;
  font-weight: 700; font-size: 13.5px; color: var(--ma-ink, #1F2D2A);
  white-space: nowrap;
}
.ma-cat-row__said,
.ma-cat-row__when {
  font-size: 11px; color: var(--ma-ink-3, #8A8077); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-cat-row__when { flex: 0 0 auto; font-family: var(--ma-font-num); }
/* The purchases behind a thing bought more than once. Same fold as the
   chart row above it, for the same reason: the row stays put. */
.ma-cat-buys {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--ma-dur-grow) var(--ma-ease);
}
.ma-cat-item[aria-expanded="true"] > .ma-cat-buys { grid-template-rows: 1fr; }
/* One grid item, which is what makes the fold a fold: `0fr` sizes the
   first row only, so a track of five children collapses one and leaves
   four on screen. */
.ma-cat-buys__in { overflow: hidden; min-height: 0; min-width: 0; }
.ma-cat-buy {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  text-align: left; cursor: pointer;
  background: none; border: none; color: inherit; font: inherit;
  padding: 8px var(--ma-sp-3) 8px var(--ma-sp-5);
  border-top: 1px dashed var(--ma-border, #E6DFD2);
  -webkit-tap-highlight-color: transparent;
}
.ma-cat-buy__when {
  font-family: var(--ma-font-num); font-size: 11.5px;
  color: var(--ma-ink-2, #5C5852); flex: 0 0 auto;
}
.ma-cat-buy__size {
  font-family: var(--ma-font-num); font-size: 11.5px;
  color: var(--ma-ink-3, #8A8077); flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-cat-buy__amt {
  font-family: var(--ma-font-num); font-variant-numeric: tabular-nums;
  font-size: 12px; font-weight: 700; color: var(--ma-ink, #1F2D2A);
  white-space: nowrap; flex: 0 0 auto;
}

/* ── sales_orders, every order, as a card in the chat ─────────────
   The Orders tab's answer and the first few rows, in a chat bubble.
   No vh, no fixed, no sticky: a surface draws inside a bubble. */
.ma-ords-answer { margin: var(--ma-sp-3) 0 var(--ma-sp-2); }
.ma-ords-answer__big {
  font-family: var(--ma-font-num); font-size: 26px; font-weight: 700;
  letter-spacing: -0.6px; line-height: 1.1;
}
.ma-ords-answer__sub { font-size: 12.5px; font-weight: 700; color: var(--ma-ink-2); margin-top: 2px; }
.ma-ords-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--ma-sp-2); }
.ma-ords-tile { border-radius: var(--ma-radius-md); padding: 8px 10px; }
.ma-ords-tile--uber { background: var(--ma-uber-soft); }
.ma-ords-tile--yoco { background: var(--ma-yoco-soft); }
.ma-ords-tile__head { font-size: 11px; font-weight: 800; letter-spacing: 0.03em; }
.ma-ords-tile--uber .ma-ords-tile__head { color: var(--ma-uber-ink); }
.ma-ords-tile--yoco .ma-ords-tile__head { color: var(--ma-yoco-ink); }
.ma-ords-tile__val {
  font-family: var(--ma-font-num); font-size: 15px; font-weight: 700; margin-top: 2px;
}
.ma-ords-tile__sub { font-size: 11px; font-weight: 700; color: var(--ma-ink-2); }
.ma-ords-rows {
  margin-top: var(--ma-sp-3); border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg); overflow: hidden; background: var(--ma-surface);
}
.ma-ords-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 4px 8px;
  align-items: baseline; width: 100%; text-align: left; padding: 9px 11px 9px 13px;
  background: transparent; border: 0; border-top: 1px solid var(--ma-border);
  position: relative; font-family: inherit; color: inherit;
}
.ma-ords-rows > .ma-ords-row:first-child { border-top: 0; }
.ma-ords-row::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; }
.ma-ords-row--uber::before { background: var(--ma-uber); }
.ma-ords-row--yoco::before { background: var(--ma-yoco); }
.ma-ords-row__when {
  font-family: var(--ma-font-num); font-size: 11.5px; font-weight: 700; color: var(--ma-ink-3);
  white-space: nowrap;
}
.ma-ords-row__what {
  font-size: 12.5px; font-weight: 600; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-ords-row__money {
  font-family: var(--ma-font-num); font-size: 13.5px; font-weight: 700; white-space: nowrap;
}
.ma-ords-row--bad .ma-ords-row__money { color: var(--ma-brick-ink); }
.ma-ords-row__flag {
  grid-column: 2 / -1; font-size: 11px; font-weight: 800; color: var(--ma-brick-ink);
}

/* ── MAOrderSheet, this order ─────────────────────────────────────
   One order, wherever it is tapped: a row on Sales › Orders, a code on
   a feedback row in Quality. Composes the shipped bottom sheet
   (.modal-overlay / .modal-sheet), so the chrome is the app's and only
   the blocks inside it are here. */
.ma-os { max-height: 88vh; animation: ma-os-up var(--ma-dur-swap) ease-out; }
@keyframes ma-os-up { from { transform: translateY(16px); opacity: 0.6; } to { transform: none; opacity: 1; } }
body.ma-sheet-open { overflow: hidden; }
.ma-os__hdr { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.ma-os__hdr h2 { font-family: var(--ma-font-num); letter-spacing: 0.03em; }
.ma-os__sub {
  margin-left: auto; font-size: 11.5px; font-weight: 700;
  color: var(--ma-ink-3);
}
.ma-os__h { margin-top: var(--ma-sp-4); }
.ma-os__h:first-child { margin-top: 0; }

/* What was in it */
.ma-os-lines { padding: 0; overflow: hidden; margin-top: var(--ma-sp-2); }
.ma-os-line {
  display: flex; align-items: baseline; gap: 9px; padding: 9px 12px;
  border-top: 1px solid var(--ma-border);
}
.ma-os-lines > .ma-os-line:first-child { border-top: 0; }
.ma-os-line__qty {
  font-family: var(--ma-font-num); font-size: 12.5px; font-weight: 700;
  color: var(--ma-ink-3); flex: 0 0 auto; min-width: 22px;
}
.ma-os-line__name { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 700; }
.ma-os-line__price {
  font-family: var(--ma-font-num); font-size: 13.5px; font-weight: 700;
}
/* A line that came off the order. Struck through at its own price, never
   as a negative: the money it gave back is the ladder's refund row, and
   showing a minus in both places reads as twice the money gone. */
.ma-os-line--off .ma-os-line__name,
.ma-os-line--off .ma-os-line__qty { text-decoration: line-through; color: var(--ma-ink-3); }
.ma-os-line--off .ma-os-line__price { color: var(--ma-ink-3); }

/* The ladder, customer paid → the cut → you got */
.ma-os-ladder { margin-top: var(--ma-sp-2); padding: 12px 13px; }
.ma-os-lrow { display: flex; align-items: baseline; gap: 10px; padding: 5px 0; }
.ma-os-lrow__k { font-size: 13px; font-weight: 700; color: var(--ma-ink-2); }
.ma-os-lrow__v {
  margin-left: auto; font-family: var(--ma-font-num); font-size: 14px; font-weight: 700;
}
.ma-os-lrow--cut .ma-os-lrow__k,
.ma-os-lrow--cut .ma-os-lrow__v { color: var(--ma-brick-ink); }
.ma-os-lrow--out {
  border-top: 1px solid var(--ma-border); margin-top: 4px; padding-top: 9px;
}
.ma-os-lrow--out .ma-os-lrow__k { color: var(--ma-ink); font-weight: 800; }
.ma-os-lrow--out .ma-os-lrow__v { font-size: 18px; letter-spacing: -0.4px; }
.ma-os-lrow__pct {
  font-family: var(--ma-font-num); font-size: 11.5px; font-weight: 700;
  color: var(--ma-brick-ink); background: var(--ma-brick-soft);
  border-radius: var(--ma-radius-xs); padding: 1px 5px;
}
.ma-os-note {
  font-size: 11.5px; color: var(--ma-ink-3); margin-top: 8px; line-height: 1.45;
}

/* What the customer said */
.ma-os-said { margin-top: var(--ma-sp-2); padding: 11px 12px; }
.ma-os-said__head { display: flex; align-items: baseline; gap: 8px; }
.ma-os-said__title { font-size: 13px; font-weight: 800; }
.ma-os-said--bad .ma-os-said__title { color: var(--ma-brick-ink); }
.ma-os-said__when {
  margin-left: auto; font-size: 11px; font-weight: 700; color: var(--ma-ink-3);
}
.ma-os-said__body { font-size: 12.5px; color: var(--ma-ink-2); margin-top: 4px; }
.ma-os-said__body--i { font-style: italic; }
.ma-os-said__cost {
  font-size: 12px; font-weight: 800; color: var(--ma-brick-ink); margin-top: 5px;
}

/* On shift */
.ma-os-staff {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 8px;
}
.ma-os-staff__one {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 700; font-size: 12.5px;
}

.ma-os-panel {
  border-radius: var(--ma-radius-md); padding: 11px 12px; font-size: 12.5px;
  font-weight: 600; color: var(--ma-ink-2); background: var(--ma-cream);
  border: 1px solid var(--ma-border); margin-top: var(--ma-sp-2);
}
.ma-os-panel--caveat {
  background: var(--ma-gold-soft); border-color: var(--ma-gold); color: var(--ma-gold-ink);
}
.ma-os-skel { display: flex; flex-direction: column; gap: 9px; margin-top: var(--ma-sp-2); }

/* ── MAReceiptSheet, the paper behind a number ────────────────────
   Composes the shipped bottom sheet and borrows the line sheet's grab
   handle and header row, so every sheet in money out reads as the same
   kind of thing. */
.ma-rs__sheet {
  gap: 12px; padding: 10px 16px 22px;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.ma-rs-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.ma-rs-head__shop {
  font-size: 19px; font-weight: 800; letter-spacing: -0.4px;
  color: var(--ma-ink, #1F2D2A); min-width: 0;
}
.ma-rs-head__total {
  font-family: var(--ma-font-num); font-variant-numeric: tabular-nums;
  font-size: 19px; font-weight: 800; letter-spacing: -0.4px;
  color: var(--ma-ink, #1F2D2A); white-space: nowrap;
}
.ma-rs-when {
  margin-top: -6px;
  font-size: 12px; color: var(--ma-ink-3, #8A8077);
  font-family: var(--ma-font-num);
}
/* The photo is capped rather than shown whole: it is here to recognise
   the purchase by, and a full-length till slip would push every line
   off the sheet. The full receipt page is one tap below. */
/* The photo is the answer to "what did this actually say", so it is a
   target: it opens into MAPhotoZoom rather than sitting at sheet width. */
.ma-rs-photo {
  position: relative; display: block; width: 100%; padding: 0;
  border-radius: 10px; border: 1px solid var(--ma-border, #E6DFD2);
  background: var(--ma-cream, #F7F4ED);
  overflow: hidden; cursor: pointer;
}
.ma-rs-photo img {
  display: block; width: 100%; max-height: 190px;
  object-fit: cover; object-position: top;
}
.ma-rs-photo__zoom {
  position: absolute; right: 8px; bottom: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 8px;
  background: rgba(31, 45, 42, .82); color: var(--ma-cream, #F7F4ED);
}
.ma-rs-photo__zoom svg { width: 15px; height: 15px; }
/* Not a warning, the two numbers differ and the operator reading the
   middle of this sheet is entitled to know before they trust it. */
.ma-rs-gap {
  font-size: 11.5px; line-height: 1.45; padding: 9px 11px; border-radius: 9px;
  background: var(--ma-gold-soft, #FBE9B8); color: var(--ma-gold-ink, #7A5610);
}
.ma-rs-said {
  font-size: 10.5px; font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-rs-lines {
  background: var(--ma-cream, #F7F4ED);
  border: 1px dashed var(--ma-border-strong, #D4CABE);
  border-radius: 10px; padding: 4px 12px;
}
.ma-rs-line {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 9px 0; border-top: 1px dashed var(--ma-border, #E6DFD2);
}
.ma-rs-lines > .ma-rs-line:first-child { border-top: none; }
.ma-rs-line__body { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ma-rs-line__name {
  font-size: 13.5px; font-weight: 600; color: var(--ma-ink, #1F2D2A);
  line-height: 1.3;
}
.ma-rs-line__size {
  font-family: var(--ma-font-num); font-size: 11px;
  color: var(--ma-ink-3, #8A8077);
}
.ma-rs-line__amt {
  font-family: var(--ma-font-num); font-variant-numeric: tabular-nums;
  font-size: 13.5px; font-weight: 700; color: var(--ma-ink, #1F2D2A);
  white-space: nowrap;
}
/* A printed value is a tap target: tap it and the photo goes to where it
   was printed (RCP-5). The affordance is the value itself, the whole
   value is the button, and the underline on press is the only decoration
   it wears: a pencil or a ring of its own would sit beside every number
   on the sheet. */
.ma-rs-tap {
  display: inline; margin: 0; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; text-align: inherit; letter-spacing: inherit;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  text-decoration: underline; text-decoration-style: dotted;
  text-decoration-color: var(--ma-border-strong, #D4CABE);
  text-underline-offset: 3px;
}
.ma-rs-tap.ma-rs-line__name,
.ma-rs-tap.ma-rs-line__amt,
.ma-rs-tap.ma-rs-line__size { display: block; }
.ma-rs-tap:active { text-decoration-style: solid; }
/* The sticker someone snapped on this line (RCP-7): one small chip that
   opens the second photo, under the line's own words. It only appears
   when the reader is on, and it says where the weight came from. */
.ma-rs-att {
  display: inline-flex; align-items: center; gap: 4px; align-self: flex-start;
  margin-top: 2px; padding: 2px 8px 2px 6px; border: 1px solid var(--ma-border, #E6DFD2);
  border-radius: 999px; background: none; cursor: pointer;
  font: inherit; font-size: 11px; color: var(--ma-ink-3, #8A8077);
  -webkit-tap-highlight-color: transparent;
}
.ma-rs-att svg { width: 13px; height: 13px; }
.ma-rs-att:active { background: var(--ma-cream, #F7F4ED); }
.ma-rs-when__tap { display: inline; }
.ma-rs-line__size {
  font-family: var(--ma-font-num); font-size: 11px;
  color: var(--ma-ink-3, #8A8077);
}
/* The line the operator came for. Nothing passes a `highlight` yet -
   the surfaces know which purchase was tapped, not which receipt line
   it came off, so this rule waits for that id to reach the wire. */
.ma-rs-line--marked {
  background: var(--ma-gold-soft, #FBE9B8);
  box-shadow: inset 3px 0 0 var(--ma-gold, #E3B23C);
  border-radius: 6px; padding-left: 9px; padding-right: 9px;
  margin: 0 -9px;
}

/* ── MAPriceSheet, what a thing costs over time ───────────────────
   Composes the shipped bottom sheet and borrows the line sheet's grab
   handle and header row, so the two read as the same kind of thing. */
.ma-ps__sheet {
  gap: 13px; padding: 10px 16px 22px;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.ma-ps-now { display: flex; flex-direction: column; gap: 3px; }
.ma-ps-now__price {
  font-family: var(--ma-font-display); font-size: 32px; font-weight: 700;
  letter-spacing: -1px; color: var(--ma-ink, #1F2D2A);
}
.ma-ps-now__head { font-size: 12px; color: var(--ma-ink-3, #8A8077); }
/* What the series is not certain of. Said, never drawn over. */
.ma-ps-caveat {
  font-size: 11.5px; line-height: 1.45; padding: 9px 11px; border-radius: 9px;
  background: var(--ma-gold-soft, #FBE9B8); color: var(--ma-gold-ink, #7A5610);
}
.ma-ps-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; }
.ma-ps-tile {
  padding: 10px 9px; border-radius: 10px; min-width: 0;
  background: var(--ma-surface, #FFFDF8);
  border: 1px solid var(--ma-border, #E6DFD2);
}
/* A coloured tile is a claim. Only a comparison the data can carry gets
   one; the rest stay quiet, whatever the arithmetic says. */
.ma-ps-tile--up {
  background: var(--ma-brick-soft, #F6E3DB);
  border-color: rgba(168, 51, 31, .28);
}
.ma-ps-tile--up .ma-ps-tile__big { color: var(--ma-brick-ink, #6E1E0F); }
.ma-ps-tile--down {
  background: var(--ma-forest-soft, #DBE8DC);
  border-color: rgba(46, 94, 62, .28);
}
.ma-ps-tile--down .ma-ps-tile__big { color: var(--ma-forest-ink, #1F4A2E); }
.ma-ps-tile__big {
  font-family: var(--ma-font-num); font-size: 19px; font-weight: 700;
  letter-spacing: -.5px; color: var(--ma-ink-3, #8A8077);
}
.ma-ps-tile__label {
  margin-top: 3px; font-size: 9.5px; font-weight: 700; letter-spacing: .4px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-ps-tile__sub {
  margin-top: 2px; font-size: 10.5px; line-height: 1.35;
  color: var(--ma-ink-3, #8A8077);
}
/* One bar per purchase, as divs, no chart library for eleven numbers. */
.ma-ps-bars {
  display: flex; align-items: flex-end; gap: 4px; height: 92px;
  padding: 10px 11px; border-radius: 10px;
  background: var(--ma-cream, #F7F4ED);
}
.ma-ps-bar {
  flex: 1; height: 100%; min-width: 6px; padding: 0; cursor: pointer;
  background: none; border: none; display: flex; align-items: flex-end;
}
.ma-ps-bar__fill { display: block; width: 100%; border-radius: 3px 3px 0 0; }
.ma-ps-bar--plain .ma-ps-bar__fill { background: var(--ma-border-strong, #D8CFBE); }
.ma-ps-bar--good  .ma-ps-bar__fill { background: var(--ma-forest, #2E5E3E); }
.ma-ps-bar--now   .ma-ps-bar__fill { background: var(--ma-brick, #A8331F); }
.ma-ps-saving {
  font-size: 12px; font-weight: 700; line-height: 1.45;
  padding: 10px 11px; border-radius: 10px;
  background: var(--ma-forest-soft, #DBE8DC); color: var(--ma-forest-ink, #1F4A2E);
}
.ma-ps-shops { display: flex; flex-direction: column; gap: 6px; }
.ma-ps-shop {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 10px 11px; border-radius: 9px;
  background: var(--ma-surface, #FFFDF8);
  border: 1px solid var(--ma-border, #E6DFD2);
}
.ma-ps-shop--best { border-color: rgba(46, 94, 62, .4); }
.ma-ps-shop__name { font-size: 12.5px; font-weight: 700; min-width: 0; }
.ma-ps-shop__tag {
  font-size: 9.5px; font-weight: 700; letter-spacing: .3px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-ps-shop__tag--best { color: var(--ma-forest, #2E5E3E); }
.ma-ps-shop__price {
  flex: 0 0 auto; font-family: var(--ma-font-num); font-size: 13px; font-weight: 700;
}
.ma-ps-alert {
  padding: 11px 12px; border-radius: 10px;
  background: var(--ma-cream, #F7F4ED);
  display: flex; flex-direction: column; gap: 8px;
}
.ma-ps-alert__head { font-size: 12.5px; font-weight: 800; }
.ma-ps-alert__row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ma-ps-alert__switch {
  margin-left: auto; min-height: 30px; padding: 5px 13px; border-radius: 999px;
  border: 1.5px solid var(--ma-border, #E6DFD2); background: transparent;
  font-size: 11.5px; font-weight: 800; color: var(--ma-ink-3, #8A8077);
  cursor: pointer;
}
.ma-ps-alert__switch.is-on {
  border-color: var(--ma-forest, #2E5E3E);
  background: var(--ma-forest-soft, #DBE8DC);
  color: var(--ma-forest-ink, #1F4A2E);
}
.ma-ps-slip { width: 100%; min-height: 44px; }
/* The strip on Money out, movers, and a door into the sheet. */
.ma-ps-watch {
  padding: 12px; border-radius: 12px;
  background: var(--ma-surface, #FFFDF8);
  border: 1px solid var(--ma-border, #E6DFD2);
  display: flex; flex-direction: column; gap: 6px;
}
.ma-ps-watch__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .8px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-ps-watch__on { text-transform: none; letter-spacing: 0; font-weight: 600; }
.ma-ps-watch__row {
  display: flex; align-items: baseline; gap: 9px; width: 100%;
  padding: 8px 0; min-height: 38px; text-align: left;
  background: none; border: none; cursor: pointer;
}
.ma-ps-watch__name { flex: 1; font-size: 13px; font-weight: 700; min-width: 0; }
.ma-ps-watch__pct {
  font-family: var(--ma-font-num); font-size: 13px; font-weight: 700;
}
.ma-ps-watch__pct--up   { color: var(--ma-brick, #A8331F); }
.ma-ps-watch__pct--down { color: var(--ma-forest, #2E5E3E); }
.ma-ps-watch__win {
  flex: 0 0 auto; font-size: 10.5px; color: var(--ma-ink-3, #8A8077);
}

/* ── MABox, the slips as an album or a list ───────────────────
   Box and Organise V3, frames 1 and 2. Cards is a photo album on a
   dark ground whatever the theme, a photograph is looked at, not read,
   and the frosted card over it carries the words. List is the same
   slips as cards with a thumbnail; its left border is the one thing a
   thumb reads at scrolling speed: gold wants you, forest is done, hint
   has nothing behind it. */
.ma-box-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 12px; padding: 4px 0 12px;
}
/* Narrowed, the first line names the shop rather than the day's work, so
   it carries the weight the note does not need. Same two lines, same
   height, the album cannot afford a third. */
.ma-box-head--narrowed .ma-box-note {
  font-weight: 700; color: var(--ma-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-box-head__words { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ma-box-note { font-size: 12.5px; color: var(--ma-ink-2); }
.ma-box-total {
  font-family: var(--ma-font-num); font-size: 17px; font-weight: 700;
  letter-spacing: -.4px; color: var(--ma-ink);
}
/* The Cards | List toggle: the shared segmented atom, sized to two words. */
.ma-box-seg { flex: 0 0 auto; min-width: 128px; }
.ma-box-seg .ma-seg__btn { min-height: 34px; padding: 0 12px; }

/* ── the album ── */
/* The album card's height. Measured and overridden per album by
   MABox.sizeAlbum; this is what it falls back to, and what it was
   before anything measured it. */
:root { --ma-box-card-h: clamp(380px, 54vh, 560px); }
.ma-box-album {
  position: relative; margin: 0 -14px; padding: 12px 0 14px;
  background: #14181A; border-radius: 18px;
  /* Not a token: the ground under a photograph is the same in both
     themes on purpose, so the card over it is drawn the same way. */
  color: #FFFDF8;
}
.ma-box-album--empty { padding: 40px 14px; }
.ma-box-track {
  display: flex; gap: 12px; padding: 0 7%;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.ma-box-track::-webkit-scrollbar { display: none; }
.ma-box-card {
  position: relative; flex: 0 0 86%; scroll-snap-align: center;
  /* The slip gets the room that is actually left, MABox.sizeAlbum
     measures it and writes --ma-box-card-h on the album, overriding the
     default below. If the measure never runs the card is exactly what it
     always was, so a failure costs the extra room and nothing else. */
  height: var(--ma-box-card-h);
  border-radius: 16px; overflow: hidden;
  background: #1D2225; box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
}
.ma-box-photo { position: absolute; inset: 0; background: #F7F4ED; }
.ma-box-photo__img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top;
}
/* No photo: the channel's own name for the slot, never an empty grey box
   that reads as a picture which failed to load. */
.ma-box-photo__none {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 6px; padding: 24px;
  text-align: center; color: var(--ma-ink-3);
  background:
    repeating-linear-gradient(0deg, transparent 0 22px, rgba(31, 45, 42, .06) 22px 23px),
    var(--ma-cream);
}
.ma-box-photo__none-label { font-size: 15px; font-weight: 800; color: var(--ma-ink-2); }
.ma-box-photo__none-sub { font-size: 11.5px; }
/* Glass, the way a modern phone floats a card over a photograph: a
   translucent surface the slip shows through, a heavy blur that keeps
   the words legible whatever is behind them, a hairline light edge and
   a soft highlight along the top. The solid tint is the fallback for a
   WebView with no backdrop-filter, there, translucent over a dense
   receipt is unreadable, so the glass is only ever drawn where the blur
   is. */
.ma-box-over {
  position: absolute; left: 12px; right: 12px; bottom: 12px;
  display: flex; flex-direction: column; gap: 8px;
  padding: 14px 14px 12px; border-radius: 20px;
  background: rgba(255, 253, 248, .9);
  border: 1px solid rgba(255, 255, 255, .55);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .6),
    0 12px 32px rgba(0, 0, 0, .28);
  color: var(--ma-ink);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ma-box-over {
    background:
      linear-gradient(180deg, rgba(255, 255, 255, .28), rgba(255, 255, 255, 0) 45%),
      rgba(255, 253, 248, .5);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    backdrop-filter: blur(24px) saturate(180%);
  }
}
/* What sits on the glass is glass too, a solid pill or button reads as
   a patch stuck on the card. */
.ma-box-over .ma-pill--neutral { background: rgba(255, 255, 255, .45); }
.ma-box-over .ma-btn--ghost {
  background: rgba(255, 255, 255, .35); border-color: rgba(255, 255, 255, .7);
}
/* The card floats over a photograph, so it keeps the light palette in
   dark mode too, its own ground, not the page's. */
:root[data-theme="dark"] .ma-box-over,
:root[data-theme="dark"] .ma-box-photo__none {
  --ma-ink: #1F2D2A; --ma-ink-2: #4A5550; --ma-ink-3: #8A8077;
  --ma-cream: #F7F4ED; --ma-surface: #FFFDF8; --ma-border: #E6DFD2; --ma-border-strong: #D4CABE;
}
.ma-box-over__shop {
  display: inline-flex; align-items: center; gap: 2px; align-self: flex-start;
  border: 0; background: transparent; padding: 0; cursor: pointer;
  font-family: var(--ma-font-body); font-size: 19px; font-weight: 800;
  letter-spacing: -.3px; color: var(--ma-brick); text-align: left;
}
.ma-box-over__shop svg { width: 16px; height: 16px; }
/* A slip with no shop behind its name: the name, not a button. */
.ma-box-over__shop--flat, .ma-box-slip__shop--flat { cursor: default; color: var(--ma-ink); }
.ma-box-over__meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  font-size: 12px; color: var(--ma-ink-2);
}
.ma-box-over__amt {
  margin-left: auto; font-family: var(--ma-font-num); font-size: 15px;
  font-weight: 800; letter-spacing: -.3px; color: var(--ma-ink);
}
.ma-box-over__amt--quiet { color: var(--ma-ink-3); font-weight: 500; }
.ma-box-over__state { font-size: 12.5px; font-weight: 700; color: var(--ma-ink); }
.ma-box-dots {
  display: flex; justify-content: center; gap: 5px; padding: 12px 0 0;
}
.ma-box-dot {
  width: 6px; height: 6px; border-radius: 3px;
  background: rgba(255, 255, 255, .35);
  transition: width var(--ma-dur-swap) var(--ma-ease);
}
.ma-box-dot--on { width: 18px; background: #fff; }

/* ── the list ── */
.ma-box-list { display: flex; flex-direction: column; gap: 10px; }
.ma-box-search { margin-bottom: 0; }
.ma-box-quick { display: flex; flex-direction: column; gap: 8px; }
.ma-box-quick__row { display: flex; flex-direction: column; gap: 5px; }
.ma-box-quick__label {
  font-size: 10px; font-weight: 700; letter-spacing: .8px;
  text-transform: uppercase; color: var(--ma-ink-3);
}
.ma-box-quick__chips {
  display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px;
  scrollbar-width: none;
}
.ma-box-quick__chips::-webkit-scrollbar { display: none; }
/* The row scrolls sideways; a chip that wraps turns one line into three
   and pushes the list off the first screen. */
.ma-box-quick__chips .ma-pill {
  flex: 0 0 auto; white-space: nowrap; min-height: 32px;
  border: 1.5px solid var(--ma-border-strong); background: var(--ma-surface);
  cursor: pointer; font-family: inherit;
}
.ma-box-quick__chips .ma-pill--ink-solid { border-color: var(--ma-ink); background: var(--ma-ink); }
.ma-box-quick__chips .ma-pill b { font-weight: 800; }
.ma-box-count { font-size: 11px; color: var(--ma-ink-3); text-align: center; }
.ma-box-add {
  display: block; width: 100%; min-height: 44px;
  padding: 12px; border-radius: 11px;
  border: 1.5px dashed var(--ma-brick); background: transparent;
  color: var(--ma-brick); font-size: 12.5px; font-weight: 800;
  cursor: pointer; font-family: inherit;
}
.ma-box-slips { display: flex; flex-direction: column; gap: 10px; }
.ma-box-slip {
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px; border-radius: 14px;
  background: var(--ma-surface); border: 1px solid var(--ma-border);
  border-left-width: 4px;
  /* The strip and the fold run to the card's edge, so the card has to
     clip them back into its own corners. */
  overflow: hidden;
}
.ma-box-slip--gold   { border-left-color: var(--ma-gold); }
.ma-box-slip--forest { border-left-color: var(--ma-forest); }
.ma-box-slip--hint   { border-left-color: var(--ma-border-strong); }
.ma-box-slip__top { display: flex; align-items: center; gap: 10px; }
.ma-box-thumb {
  position: relative; width: 56px; height: 72px; flex: 0 0 auto;
  border-radius: 8px; overflow: hidden; background: var(--ma-cream);
  border: 1px solid var(--ma-border);
}
.ma-box-thumb__img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }
.ma-box-thumb .ma-box-photo__none { gap: 2px; padding: 4px; }
.ma-box-thumb .ma-box-photo__none-label { font-size: 9px; line-height: 1.2; }
.ma-box-thumb .ma-box-photo__none-sub { display: none; }
.ma-box-slip__who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ma-box-slip__shop {
  display: block; border: 0; background: transparent; padding: 0; cursor: pointer;
  text-align: left; font-family: var(--ma-font-body); font-size: 16px; font-weight: 800;
  letter-spacing: -.2px; color: var(--ma-brick);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Wraps rather than truncates: "Photo · Nomsa" is the hand it came
   through, and a hand cut to "No…" says nothing. */
.ma-box-slip__meta { font-size: 11.5px; line-height: 1.35; color: var(--ma-ink-3); }
.ma-box-slip__amt {
  flex: 0 0 auto; font-family: var(--ma-font-num); font-size: 17px;
  font-weight: 800; letter-spacing: -.3px; color: var(--ma-ink);
}
/* Counted on its other half, or not money yet. Dimmed rather than
   hidden, a blank where an amount belongs reads as a broken row. */
.ma-box-slip__amt--quiet { color: var(--ma-ink-3); font-weight: 500; }
.ma-box-slip__state { display: flex; align-items: center; gap: 8px; }

/* ── the card that opens ──────────────────────────────────────────
   The collapsed card names what is on the slip; its strip is the
   fold's own <summary>; the fold under it is the slip's lines.
   Design: the Slips card canvas, 2026-09. */

/* The top block is thumbnail | everything else, so the pills sit under
   the shop's own name rather than under the photo. */
.ma-box-slip__top { align-items: flex-start; gap: 11px; }
.ma-box-slip__head { display: flex; align-items: flex-start; gap: 8px; }
.ma-box-slip__head .ma-box-slip__shop { flex: 1; min-width: 0; }
.ma-box-slip__who { gap: 7px; }

/* The photo is its own tap target and says so, everywhere else in the
   app the photo is inert and the whole row navigates. */
.ma-box-thumb--live { padding: 0; cursor: pointer; }
.ma-box-thumb__zoom {
  position: absolute; right: 4px; bottom: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 6px;
  background: rgba(31, 45, 42, .82); color: var(--ma-cream, #F7F4ED);
}
.ma-box-thumb__zoom svg { width: 12px; height: 12px; }

/* What it is, the light pills. Deliberately quieter than the state
   pills they sit beside: they are the card's CONTENT, not its state,
   and five of them shouting would beat the shop's own name. */
.ma-box-slip__things { display: flex; flex-wrap: wrap; gap: 5px; }
.ma-box-thing {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 8px; border-radius: var(--ma-radius-pill);
  font-family: var(--ma-font-body); font-size: 11px; font-weight: 700;
  letter-spacing: .2px; line-height: 1.35;
  background: var(--ma-cream); border: 1px solid var(--ma-border);
  color: var(--ma-ink-2);
}
.ma-box-thing svg { width: 9px; height: 9px; flex: 0 0 auto; }
.ma-box-thing--more {
  background: transparent; border-color: var(--ma-border-strong);
  color: var(--ma-ink-3); cursor: pointer;
}
/* The one thing on the card nobody has settled. Gold, because gold is
   already the app's word for "this wants you". */
.ma-box-thing--other {
  background: var(--ma-gold-soft); border-color: transparent;
  color: var(--ma-gold-ink); font-weight: 800; cursor: pointer;
}

/* ── the strip, which is the fold's summary ── */
.ma-box-acc { border-top: 1px solid var(--ma-border); margin: 2px -12px -12px; }
.ma-box-strip {
  display: flex; align-items: center; gap: 8px;
  padding: 0 12px; background: var(--ma-cream); cursor: pointer;
  list-style: none; -webkit-tap-highlight-color: transparent;
}
.ma-box-strip::-webkit-details-marker { display: none; }
.ma-box-strip__n {
  flex: 0 1 auto; min-width: 0; display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 700; color: var(--ma-ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-box-strip__dot { width: 7px; height: 7px; border-radius: 4px; flex: 0 0 auto; }
.ma-box-strip__dot--gold   { background: var(--ma-gold); }
.ma-box-strip__dot--forest { background: var(--ma-forest); }
/* The work. Leads away to Organise, so it stops the press reaching the
   summary it sits inside. */
.ma-box-strip__sort {
  flex: 0 0 auto; min-height: 44px; margin: 5px 0;
  display: inline-flex; align-items: center; gap: 3px; padding: 0 9px;
  border-radius: 9px; border: 1px solid transparent;
  background: var(--ma-gold-soft); color: var(--ma-gold-ink);
  font-family: inherit; font-size: 11px; font-weight: 800;
  white-space: nowrap; cursor: pointer;
}
.ma-box-strip__sort svg { width: 11px; height: 11px; }
/* The look. Not a button, the whole summary is the target, and a
   button inside it would halve the thing a thumb can hit. */
.ma-box-strip__fold {
  flex: 1; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: flex-end; gap: 5px;
  color: var(--ma-brick); font-size: 12px; font-weight: 800;
  /* Two lines here would make the strip 60px on some cards and 44px on
     others, and a list of cards whose strips differ in height reads as
     a rendering bug. */
  white-space: nowrap;
}
.ma-box-strip__chev {
  width: 14px; height: 14px;
  transition: transform var(--ma-dur-swap) var(--ma-ease);
}
.ma-box-acc[open] .ma-box-strip__chev { transform: rotate(180deg); }

/* ── the fold ── */
.ma-box-fold { padding: 4px 12px 12px; background: var(--ma-cream); }
.ma-box-fold__none {
  padding: 14px 2px; font-size: 12px; color: var(--ma-ink-3); text-align: center;
}
.ma-box-row {
  display: grid; grid-template-columns: 22px minmax(0, 1fr) auto;
  column-gap: 10px; align-items: start; padding: 9px 0;
  border-bottom: 1px solid var(--ma-border);
}
.ma-box-row:last-of-type { border-bottom: none; }
/* A dot, never a number: a number beside "10 kg" reads as a quantity,
   which is the one thing on this row it must not be confused with. */
.ma-box-row__dot {
  width: 7px; height: 7px; border-radius: 4px; margin-top: 5px;
  justify-self: center; background: var(--ma-forest);
}
.ma-box-row--open .ma-box-row__dot { background: var(--ma-gold); }
.ma-box-row__body { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
/* The slip's own words. The reader is holding that paper, or is one tap
   from the photo of it, so this is the line and not the caption. */
.ma-box-row__printed {
  font-size: 13px; font-weight: 700; line-height: 1.3; letter-spacing: .1px;
  color: var(--ma-ink); overflow-wrap: anywhere;
}
.ma-box-row__what { font-size: 11px; line-height: 1.35; color: var(--ma-ink-3); }
.ma-box-row__what b { font-weight: 700; color: var(--ma-ink-2); }
.ma-box-row--open .ma-box-row__what { color: var(--ma-gold-ink); }
.ma-box-row__amt {
  margin-top: 1px; font-family: var(--ma-font-num); font-size: 13.5px;
  font-weight: 700; color: var(--ma-ink); font-variant-numeric: tabular-nums;
}
.ma-box-fold__split {
  display: flex; align-items: center; gap: 8px; padding: 13px 0 3px;
}
.ma-box-fold__split-label {
  font-size: 10px; font-weight: 800; letter-spacing: .8px;
  text-transform: uppercase; color: var(--ma-gold-ink);
}
.ma-box-fold__split-rule { flex: 1; height: 1px; background: var(--ma-gold-soft); }
.ma-box-fold__split-n { font-size: 10.5px; font-weight: 800; color: var(--ma-gold-ink); }
/* Whether the lines reach what the slip printed at the bottom, the one
   thing neither a list of lines nor a total can say on its own. */
.ma-box-fold__sum {
  display: flex; align-items: flex-start; gap: 7px;
  margin-top: 12px; padding: 9px 11px; border-radius: 10px;
  background: var(--ma-forest-soft); color: var(--ma-forest-ink);
  font-size: 11.5px; font-weight: 700; line-height: 1.45;
}
.ma-box-fold__sum svg { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 1px; }
.ma-box-fold__sum--gap {
  background: var(--ma-gold-soft); color: var(--ma-gold-ink);
}
.ma-box-fold__acts { display: flex; gap: 8px; margin-top: 10px; }
.ma-box-fold__acts .ma-btn { min-height: 44px; }
.ma-box-fold__act { flex: 1; }
.ma-box-empty {
  padding: 28px 12px; text-align: center; font-size: 12.5px;
  color: var(--ma-ink-3);
}
.ma-box-empty--dark { color: rgba(255, 253, 248, .7); }
/* Two documents, one purchase. Zuzo's colour, because it is a proposal
   and not a finding, nothing is written until a person taps. */
.ma-box-pair {
  margin-bottom: 12px; padding: 13px; border-radius: 12px;
  background: var(--ma-zuzo-soft);
  border: 1px solid var(--ma-zuzo);
  display: flex; flex-direction: column; gap: 7px;
}
.ma-box-pair__head { font-size: 12.5px; font-weight: 800; color: var(--ma-zuzo-ink); }
.ma-box-pair__body,
.ma-box-pair__gap { font-size: 12px; line-height: 1.45; color: var(--ma-zuzo-ink); }
.ma-box-pair__gap { font-weight: 700; }
.ma-box-pair__actions { display: flex; gap: 8px; margin-top: 3px; }
.ma-box-pair__actions .ma-btn { flex: 1; }

/* ── MALineSheet, one sheet for one line ──────────────────────────
   Composes the shipped .modal-overlay / .modal-sheet bottom sheet, so
   the z-index rule (9999 over the 9000 nav) and the flex column that
   keeps the footer visible come for free. Only what is particular to
   this sheet is typed here. */
/* A sheet that can be read to the end.
   `.modal-sheet` caps its height at min(90vh, --ma-sheet-max-h) and the
   pattern's own `.modal-body` is what scrolls inside it, but the line
   sheet and the price sheet put their content straight into the shell,
   so there was no scrolling element anywhere and the overflow was simply
   clipped. Measured at 390×844: 867 px of content in a 792 px box with
   `overflow-y: visible`, which cut off the last 75 px, the lock button
   among them. A sheet whose primary action is below the fold and cannot
   be scrolled to is a sheet that cannot be used.
   `overscroll-behavior: contain` keeps a flick at the end of the list
   from scrolling the page behind the sheet. */
.ma-ls__sheet {
  gap: 13px;
  padding: 10px 16px 22px;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.ma-ls-grab {
  width: 38px; height: 4px; border-radius: 999px;
  background: var(--ma-border, #E6DFD2); align-self: center;
}
.ma-ls-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.ma-ls-where {
  font-size: 10.5px; font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077); min-width: 0;
}
.ma-ls-close {
  border: none; background: none; padding: 4px;
  font-size: 12px; font-weight: 700; color: var(--ma-brick, #A8331F); cursor: pointer;
}
/* The line as printed. Space Grotesk and a dashed edge say "this is the
   document, not our reading of it" without a label having to. */
.ma-ls-raw {
  background: var(--ma-cream, #F7F4ED);
  border: 1px dashed var(--ma-border-strong, #D4CABE);
  border-radius: 9px; padding: 11px 13px;
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.ma-ls-raw__text,
.ma-ls-raw__amount {
  font-family: 'Space Grotesk', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; letter-spacing: .4px;
}
.ma-ls-raw__text { color: var(--ma-ink-2, #4A5550); min-width: 0; word-break: break-word; }
.ma-ls-raw__amount { font-weight: 700; flex: 0 0 auto; }
.ma-ls-rawnote { display: flex; align-items: center; gap: 9px; }
.ma-ls-rawnote > span { flex: 1; font-size: 11px; color: var(--ma-ink-3, #8A8077); }
.ma-ls-slip {
  flex: 0 0 auto; padding: 7px 11px; border-radius: 999px;
  border: 1.5px solid var(--ma-border, #E6DFD2);
  background: var(--ma-surface, #FFFDF8);
  font-size: 11.5px; font-weight: 800; color: var(--ma-ink-2, #4A5550);
  cursor: pointer; min-height: 34px;
}
/* The name that is not a name, shared/ambiguous_names.py. Gold, because
   it wants a word rather than reporting a fault. */
.ma-ls-amb {
  background: var(--ma-gold-soft, #FBE9B8); border-radius: 11px; padding: 12px;
  display: flex; flex-direction: column; gap: 6px;
}
.ma-ls-amb__head {
  font-size: 11px; font-weight: 800; letter-spacing: .8px;
  text-transform: uppercase; color: var(--ma-gold-ink, #7A5610);
}
.ma-ls-amb__body {
  font-size: 12px; font-weight: 600; line-height: 1.45; color: var(--ma-gold-ink, #7A5610);
}
.ma-ls-section { display: flex; flex-direction: column; gap: 8px; }
.ma-ls-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 12px; min-height: 48px; text-align: left;
  background: var(--ma-surface, #FFFDF8);
  border: 1px solid var(--ma-border, #E6DFD2); border-radius: 10px; cursor: pointer;
}
.ma-ls-row__body { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ma-ls-row__label {
  font-size: 10px; font-weight: 700; letter-spacing: .8px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-ls-row__value { font-size: 14.5px; font-weight: 800; color: var(--ma-ink, #1F2D2A); }
.ma-ls-row__hint { font-size: 11px; line-height: 1.4; color: var(--ma-ink-3, #8A8077); }
.ma-ls-anchor {
  background: var(--ma-zuzo-soft, #E5E1F8); border-radius: 10px; padding: 11px 12px;
  display: flex; align-items: center; gap: 9px;
}
.ma-ls-anchor__text {
  flex: 1; font-size: 11.5px; font-weight: 700; line-height: 1.4;
  color: var(--ma-zuzo-ink, #3D3287);
}
.ma-ls-anchor__switch {
  padding: 5px 10px; border-radius: 999px; min-height: 30px;
  border: 1px solid var(--ma-zuzo, #8B7BE8); background: transparent;
  color: var(--ma-zuzo-ink, #3D3287); font-size: 10.5px; font-weight: 800; cursor: pointer;
}
/* The host MALineTell mounts into. It brings its own surface, so this
   only reserves the slot. */
.ma-ls-tell { display: block; }
/* What changed on this line (CMD-23). One fold, closed by default, so
   the high number of automatic changes on a line never reads as alarm;
   opening it gives one plain sentence per change, newest first. Drawn
   only while LINE_HISTORY_STRIP is on and the server sent sentences. */
.ma-ls-history {
  background: var(--ma-cream, #F7F4ED); border-radius: 10px; padding: 4px 12px;
}
.ma-ls-history__list { display: flex; flex-direction: column; gap: 6px; padding: 2px 0 8px; }
.ma-ls-history__row { font-size: 11.5px; line-height: 1.45; color: var(--ma-ink-2, #4A5550); }
.ma-ls-history__path { color: var(--ma-ink-3, #8A8077); margin-top: 2px; }
.ma-ls-history__link { color: var(--ma-ink-2, #4A5550); font-weight: 700; }
.ma-ls-notes {
  background: var(--ma-cream, #F7F4ED); border-radius: 10px; padding: 11px 12px;
  display: flex; flex-direction: column; gap: 5px;
}
.ma-ls-notes__head {
  font-size: 10px; font-weight: 800; letter-spacing: .8px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-ls-notes__row { font-size: 11.5px; line-height: 1.45; color: var(--ma-ink-2, #4A5550); }
.ma-ls-actions { display: flex; gap: 8px; }
.ma-ls-actions .ma-btn { min-height: 44px; }
.ma-ls-actions [data-ls-notfood] { flex: 1; }
.ma-ls-actions [data-ls-lock] { flex: 2; }
/* Why the button is off, said rather than implied. A greyed control with
   no reason is the shared failure of the four modals this replaces. */
.ma-ls-blocked {
  font-size: 11.5px; color: var(--ma-ink-3, #8A8077); text-align: center; margin-top: -6px;
}

/* A page's own way out to something that is not a tab. One rule, so a
   second page needing one does not invent a second style. */
.ma-page-foot-link {
  margin: 14px 0 0;
  font-size: 12.5px;
}
.ma-page-foot-link a {
  color: var(--ma-brick, #A8331F);
  text-decoration: none;
  font-weight: 700;
}

/* ── MAShops, what each shop costs you ────────────────────────────
   The list is most money first; the detail is five blocks in the order
   an operator asks them. Two colours carry meaning and nothing else
   does: **forest** on the saving (money you would keep) and **gold** on
   the work a shop makes (a row that wants you). A page where every block
   is coloured is a page where neither stands out. */
.ma-shop-list {
  display: flex; flex-direction: column; gap: 1px;
  background: var(--ma-surface, #FFFDF8);
  border: 1px solid var(--ma-border, #E8E2D6);
  border-radius: 12px; overflow: hidden;
}
.ma-shop-row {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--ma-surface, #FFFDF8); border: 0;
  border-bottom: 1px solid var(--ma-border, #E8E2D6);
  padding: 12px 13px; min-height: 44px; font: inherit; color: inherit;
}
.ma-shop-row:last-child { border-bottom: 0; }
.ma-shop-row__main { display: flex; flex-direction: column; gap: 3px; }
.ma-shop-row__top,
.ma-shop-row__bottom {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.ma-shop-row__name {
  font-size: 13.5px; font-weight: 700; color: var(--ma-ink, #1F2D2A);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-shop-row__total {
  flex: 0 0 auto; font-family: 'Space Grotesk', ui-monospace, monospace;
  font-size: 13.5px; font-weight: 700; color: var(--ma-ink, #1F2D2A);
}
.ma-shop-row__share { font-size: 11px; color: var(--ma-ink-3, #8A8077); }
/* The work a shop makes. Gold, because it is the thing to do something
   about, the rand figure beside it already speaks for itself. */
.ma-shop-row__work {
  flex: 0 0 auto; font-size: 10.5px; font-weight: 700;
  padding: 2px 8px; border-radius: 999px;
  background: var(--ma-gold-soft, #FBE9B8);
  color: var(--ma-gold-ink, #7A5610);
}

.ma-shop { display: flex; flex-direction: column; gap: 14px; }
.ma-shop-head { display: flex; flex-direction: column; gap: 1px; }
.ma-shop-head__n {
  font-family: 'Space Grotesk', ui-monospace, monospace;
  font-size: 30px; font-weight: 700; line-height: 1.05;
  color: var(--ma-ink, #1F2D2A);
}
.ma-shop-head__s {
  font-size: 12px; font-weight: 800; letter-spacing: .6px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-shop-head__l { font-size: 12px; color: var(--ma-ink-3, #8A8077); }

/* Six months, as divs. Eleven numbers do not need a chart library, and
   a page that loads one pays for it on every open. */
.ma-shop-bars {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px;
  align-items: end;
}
.ma-shop-bar { display: flex; flex-direction: column; gap: 4px; }
/* The groove, in the groove's own token.
   It was `--ma-cream`, which is the page's ground, so the track was
   the same colour as everything behind it: six months of bars read as
   three blocks floating in space, and the three months with no spend
   showed nothing at all rather than an empty slot. `--ma-track` exists
   for exactly this and answers in both themes. */
.ma-shop-bar__track {
  height: 64px; display: flex; align-items: flex-end;
  background: var(--ma-track); border-radius: 6px; overflow: hidden;
}
.ma-shop-bar__fill {
  width: 100%; background: var(--ma-forest, #2E5E3E); border-radius: 6px;
  min-height: 3px;
}
.ma-shop-bar__l {
  text-align: center; font-size: 10px; color: var(--ma-ink-3, #8A8077);
}

.ma-shop-chips { display: flex; flex-wrap: wrap; gap: 6px; }

/* Money you would keep. The one green thing on the page. */
.ma-shop-saving {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 13px; border-radius: 12px;
  background: var(--ma-forest-soft, #DBE8DC);
  border: 1px solid rgba(46, 94, 62, .3);
}
.ma-shop-saving__l {
  flex: 1; font-size: 12.5px; font-weight: 700; line-height: 1.45;
  color: var(--ma-forest-ink, #1F4A2E);
}

.ma-shop-block { display: flex; flex-direction: column; gap: 8px; }
.ma-shop-block .ma-section-title { margin: 0; }
.ma-shop-buys {
  display: flex; flex-direction: column; gap: 1px;
  background: var(--ma-surface, #FFFDF8);
  border: 1px solid var(--ma-border, #E8E2D6);
  border-radius: 12px; overflow: hidden;
}
.ma-shop-buy {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  width: 100%; text-align: left; cursor: pointer; font: inherit;
  color: inherit; background: var(--ma-surface, #FFFDF8); border: 0;
  border-bottom: 1px solid var(--ma-border, #E8E2D6);
  padding: 11px 13px; min-height: 44px;
}
.ma-shop-buy:last-child { border-bottom: 0; }
.ma-shop-buy__n {
  font-size: 12.5px; font-weight: 700; color: var(--ma-ink, #1F2D2A);
}
.ma-shop-buy__p {
  font-family: 'Space Grotesk', ui-monospace, monospace;
  font-size: 12.5px; color: var(--ma-ink-2, #4A5550); white-space: nowrap;
}
.ma-shop-buy__gap {
  grid-column: 1 / -1; font-size: 11px; color: var(--ma-brick, #A8331F);
}
.ma-shop-buy__best {
  grid-column: 1 / -1; font-size: 11px; color: var(--ma-forest-ink, #1F4A2E);
}

.ma-shop-behave {
  margin: 0; padding-left: 18px;
  display: flex; flex-direction: column; gap: 5px;
  font-size: 12px; line-height: 1.5; color: var(--ma-ink-2, #4A5550);
}

/* The half a registry never had. */
.ma-shop-work {
  padding: 12px 13px; border-radius: 12px;
  background: var(--ma-gold-soft, #FBE9B8);
  border: 1px solid rgba(232, 179, 62, .45);
}
.ma-shop-work .ma-section-title { color: var(--ma-gold-ink, #7A5610); }
.ma-shop-work__l,
.ma-shop-work__m {
  font-size: 12.5px; line-height: 1.45; color: var(--ma-gold-ink, #7A5610);
}
.ma-shop-work__l { font-weight: 700; }
.ma-shop-work .ma-btn { margin-top: 4px; min-height: 42px; }

.ma-shop-foot { display: flex; gap: 8px; }
.ma-shop-foot .ma-btn { flex: 1; min-height: 42px; }

/* The page's own way back, where a tabbed page has its date chip. */
.ma-shop-crumb {
  font-size: 12px; font-weight: 700; letter-spacing: .3px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}

.ma-shop-empty {
  padding: 32px 14px; text-align: center; font-size: 12.5px;
  color: var(--ma-ink-3, #8A8077);
}

/* ── MAToday, what wants a word ───────────────────────────────────
   The daily minute. Two counters, then groups in the order the answers
   expire. A card IS the decision, so it carries its actions rather than
   linking to them.

   One colour carries meaning: **gold** on the clock of a line whose
   answer goes home tonight, and on the panel of a name this shop reuses.
   Everything else is the page's own ink, a screen where every card is
   coloured is a screen where the urgent one does not stand out. */
/* Zuzo's paragraph, in Zuzo's colour, above the groups. It replaced two
   counter tiles and a caption: three ways of saying one thing, none of
   which said who was talking. */
.ma-td-brief {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--ma-zuzo-soft);
  border: 1px solid var(--ma-zuzo-line);
  border-radius: 12px; padding: 12px 13px;
  margin-bottom: 12px;
}
.ma-td-brief__mark {
  flex: 0 0 auto; width: 17px; height: 17px; margin-top: 1px;
  color: var(--ma-zuzo-ink);
}
.ma-td-brief__mark svg { width: 100%; height: 100%; display: block; }
.ma-td-brief__text {
  font-size: 13.5px; font-weight: 600; line-height: 1.45;
  color: var(--ma-zuzo-ink);
}

.ma-td-list { display: flex; flex-direction: column; gap: 12px; }
/* A group is a card of cards: one surface, the cards inside it, so a
   fold reads as one block rather than four. */
.ma-td-group {
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  border-radius: 14px;
  overflow: hidden;
}
/* The one group whose answers expire. Gold ground and a gold edge, and
   nothing on the rest, a screen where every group is coloured is a
   screen where the urgent one does not stand out. */
.ma-td-group--now {
  background: var(--ma-gold-wash);
  border: 1.5px solid var(--ma-gold-line);
}
.ma-td-group__body {
  display: flex; flex-direction: column; gap: 10px; padding: 10px;
}

/* The head says four things, the word its cards wear, what the group
   is, who can answer it, and how many, so none of them has to be read
   out of a sentence. */
.ma-td-group__head {
  display: flex; align-items: flex-start; gap: 9px; min-width: 0; flex: 1;
}
.ma-td-group__chip {
  flex: 0 0 auto; margin-top: 1px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .2px;
  text-transform: none;
  background: var(--ma-gold-soft); color: var(--ma-gold-ink);
}
.ma-td-group:not(.ma-td-group--now) .ma-td-group__chip {
  background: var(--ma-surface); color: var(--ma-ink-2);
  border: 1px solid var(--ma-border);
}
.ma-td-group__words {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
.ma-td-group__title {
  font-size: 13.5px; font-weight: 800; line-height: 1.3;
  color: var(--ma-ink); text-transform: none; letter-spacing: 0;
}
/* Who can answer it. This is the line that decides whether an operator
   reads the group at all, so it is never the thing that gets clipped. */
.ma-td-group__sub {
  font-size: 11.5px; font-weight: 500; line-height: 1.35;
  color: var(--ma-ink-3); text-transform: none; letter-spacing: 0;
}

.ma-td-card {
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: 12px; padding: 13px 14px;
  display: flex; flex-direction: column; gap: 9px;
}
.ma-td__top {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.ma-td__where {
  font-size: 10.5px; font-weight: 700; letter-spacing: .5px;
  text-transform: uppercase; color: var(--ma-ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-td__clock {
  flex: 0 0 auto; font-size: 10.5px; font-weight: 700;
  color: var(--ma-ink-3);
}
/* An answer that goes home at six. The only countdown on the screen. */
.ma-td__clock--now {
  padding: 2px 8px; border-radius: 999px;
  background: var(--ma-gold-soft);
  color: var(--ma-gold-ink);
}
/* What was printed on the paper, in a box of its own: evidence, not
   another line of the card. The slip button on its right is the way
   back to the photo, which is what half these questions are answered
   by looking at. */
.ma-td__rawbox {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 11px; border-radius: 10px;
  background: var(--ma-raw-bg);
  border: 1px solid var(--ma-raw-line);
}
.ma-td__raw {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--ma-font-num);
  font-size: 12.5px; font-weight: 700; color: var(--ma-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-td__amount {
  flex: 0 0 auto; font-family: var(--ma-font-num);
  font-size: 13.5px; font-weight: 700; color: var(--ma-ink);
}
.ma-td__slip {
  flex: 0 0 auto; width: 30px; height: 30px; padding: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--ma-border); border-radius: 8px;
  background: var(--ma-surface); color: var(--ma-ink-2); cursor: pointer;
}
.ma-td__slip svg { width: 100%; height: 100%; display: block; }
.ma-td__made {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
}
/* The answer leads. Promoted from a 12.5px label to a headline because
   evidence under a headline reads as proof, and evidence on its own
   reads as a puzzle. It wraps, a clipped answer is not an answer. */
.ma-td__answer {
  font-size: 16.5px; font-weight: 800; line-height: 1.25;
  color: var(--ma-ink); min-width: 0;
  overflow-wrap: anywhere;
}
.ma-td__why,
.ma-td__flag {
  font-size: 12.5px; line-height: 1.5; color: var(--ma-ink-2);
}
.ma-td__flag { font-weight: 700; }
/* Zuzo's aside, why this question cannot simply be looked up. In
   Zuzo's colour, so a card carrying one is visibly a card Zuzo is
   talking about rather than a row in a queue. It was gold, which is the
   clock's colour on this screen and said "urgent" about a sentence that
   is not urgent, it is explanatory. */
.ma-td__note {
  font-size: 12px; line-height: 1.5;
  padding: 10px 11px; border-radius: 10px;
  background: var(--ma-zuzo-soft);
  border: 1px solid var(--ma-zuzo-line);
  color: var(--ma-zuzo-ink);
}
/* The ladder. The primary carries the answer in words, "It's Chicken
   drumstick wing", so it gets the whole row and is allowed to wrap;
   .ma-btn is nowrap by default and an ellipsis through the answer would
   defeat the point of putting it there. Under it, the rung that
   disagrees: a card with one button is a card that can only be obeyed. */
.ma-td__acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
.ma-td__acts .ma-btn { flex: 1 1 100%; min-height: 44px; }
.ma-td__act { white-space: normal; line-height: 1.25; padding: 8px 12px; }
.ma-td__act2 { flex: 1 1 100%; }

/* Today's group heads are a block, not a line: the shared accordion's
   one-line ellipsis would cut the subtitle off entirely. Scoped to this
   feed, every other accordion labels a shelf in two or three words. */
.ma-td-group > .ma-acc__h { align-items: flex-start; padding: 12px 13px; gap: 8px; }
.ma-td-group > .ma-acc__h .ma-acc__l {
  white-space: normal; overflow: visible; text-overflow: clip;
  overflow-wrap: anywhere; line-height: 1.35;
  /* The tracking, the uppercase and the colour this used to turn back
     off are off in the base now, see `.ma-acc__h`. What is left here
     is the one thing that really is local: a group title in this feed
     is a sentence, so it wraps where every other shelf ellipsises. */
}
.ma-td-group > .ma-acc__h .ma-acc__chev { margin-top: 3px; }
.ma-td-group > .ma-acc__h .ma-acc__n { margin-top: 1px; }

/* ── MAReadiness, the owner's card and the admin's tracks ────
   Two renderings of one payload. The owner's half carries no
   percentage anywhere: the bar is a shape, the key is three counts.
   The admin's half is the only place in Money out a share is a
   number on screen, because the person clearing the queue is the
   one it means something to. */
.ma-ready, .ma-cov {
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  padding: 14px;
  margin-bottom: var(--ma-sp-4);
  display: flex; flex-direction: column; gap: 11px;
}
.ma-ready__head { display: flex; align-items: flex-start; gap: 10px; }
.ma-ready__disc {
  width: 30px; height: 30px; border-radius: 999px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--ma-gold-soft); color: var(--ma-gold-ink);
  transition: background var(--ma-dur-swap) var(--ma-ease),
              color var(--ma-dur-swap) var(--ma-ease);
}
.ma-ready__disc svg { width: 15px; height: 15px; }
.ma-ready__disc--done    { background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-ready__disc--working { background: var(--ma-zuzo-soft);   color: var(--ma-zuzo-ink); }
.ma-ready__text  { flex: 1; min-width: 0; }
.ma-ready__title { margin: 0; font: 800 14px var(--ma-font-body); color: var(--ma-ink); }
.ma-ready__sub   { margin: 3px 0 0; font: 400 11.5px/1.45 var(--ma-font-body); color: var(--ma-ink-2); }

.ma-ready__bar {
  display: flex; height: 8px; border-radius: 999px;
  background: var(--ma-border); overflow: hidden;
}
.ma-ready__seg { height: 100%; transition: width var(--ma-dur-grow) var(--ma-ease); }
.ma-ready__seg--done      { background: var(--ma-forest); }
.ma-ready__seg--sorting   { background: var(--ma-zuzo); }
.ma-ready__seg--needs-you { background: var(--ma-gold); }

.ma-ready__key { display: flex; flex-wrap: wrap; gap: 6px 12px; margin: 0; padding: 0; list-style: none; }
.ma-ready__key li {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 11px var(--ma-font-body); color: var(--ma-ink-2);
}
.ma-dot { width: 8px; height: 8px; border-radius: 999px; flex: 0 0 auto; }
.ma-dot--done      { background: var(--ma-forest); }
.ma-dot--sorting   { background: var(--ma-zuzo); }
.ma-dot--needs-you { background: var(--ma-gold); }

.ma-ready__actions { display: flex; flex-wrap: wrap; gap: 7px; }
.ma-ready__actions .ma-btn {
  flex: 1 1 140px; min-height: 44px; padding: 8px 10px;
  font-size: 12px; font-weight: 800; white-space: normal; line-height: 1.25;
  letter-spacing: -.1px;
}

.ma-cov__head { display: flex; align-items: baseline; gap: 8px; }
/* One line, and the exposure figure keeps its end of it. "Covered ·
   weighted by rand" wrapped to two at 390 px and the second line landed
   under the rands, which read as a caption on the wrong number. */
.ma-cov__label {
  flex: 1; min-width: 0;
  font: 800 10px var(--ma-font-body); letter-spacing: .09em;
  text-transform: uppercase; color: var(--ma-ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ma-cov__exposed {
  flex: 0 0 auto;
  font: 700 12px var(--ma-font-num); color: var(--ma-brick-ink);
}
.ma-cov__row {
  display: grid; grid-template-columns: 1fr auto; gap: 5px 8px;
  width: 100%; text-align: left; border: 0; background: none; padding: 4px 0;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.ma-cov__row:active { opacity: var(--ma-press-fade); }
.ma-cov__row-label { font: 700 12px var(--ma-font-body); color: var(--ma-ink); min-width: 0; }
.ma-cov__row-pct   { font: 700 12px var(--ma-font-num); color: var(--ma-ink-2); }
.ma-cov__row .ma-bar { grid-column: 1 / -1; }
.ma-cov__row-sub   { grid-column: 1 / -1; font: 400 10.5px/1.4 var(--ma-font-body); color: var(--ma-ink-3); }
.ma-cov__rule { border: 0; border-top: 1px solid var(--ma-border); margin: 2px 0; width: 100%; }
.ma-cov__dial { display: flex; flex-direction: column; gap: 9px; }
.ma-cov__dial-title { font: 800 12.5px var(--ma-font-body); color: var(--ma-ink); }
.ma-cov__dial-sub   { margin: 3px 0 0; font: 400 11px/1.4 var(--ma-font-body); color: var(--ma-ink-3); }

/* The three-step picker under the dial. Three postures, not a slider:
   the operator is choosing between them, and a slider would invite a
   precision the threshold does not have. */
.ma-seg {
  display: flex; gap: 4px; padding: 3px;
  background: var(--ma-cream); border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-md);
}
.ma-seg__btn {
  flex: 1; min-height: 38px; border: 0; background: transparent;
  border-radius: 8px; cursor: pointer;
  font: 800 12px var(--ma-font-num); color: var(--ma-ink-3);
  -webkit-tap-highlight-color: transparent;
}
.ma-seg__btn--on { background: var(--ma-surface); color: var(--ma-ink); box-shadow: 0 1px 3px rgba(0,0,0,.10); }
.ma-seg__btn:active { opacity: var(--ma-press-fade); }

/* The bar atom the tracks and the shelves share. */
.ma-bar {
  display: block; height: 7px; border-radius: 999px;
  background: var(--ma-border); overflow: hidden;
}
.ma-bar__fill { display: block; height: 100%; border-radius: 999px; background: var(--ma-ink-3); }
.ma-bar__fill--forest { background: var(--ma-forest); }
.ma-bar__fill--gold   { background: var(--ma-gold); }
.ma-bar__fill--brick  { background: var(--ma-brick); }
.ma-bar__fill--zuzo   { background: var(--ma-zuzo); }
.ma-bar__fill--open   { background: var(--ma-ink-3); opacity: .4; }

/* ── MARegion, one region, one state ─────────────────────────
   The box a region falls back to when it has no rows to draw. One
   shape for both reasons, because the difference between them is the
   sentence, not the furniture, and the two want opposite things from
   the operator, so the sentence has to carry it. */
.ma-region {
  padding: 30px 16px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
}
.ma-region__msg { margin: 0; font: 400 12.5px/1.5 var(--ma-font-body); }
.ma-region--empty .ma-region__msg { color: var(--ma-ink-3); }
.ma-region--error .ma-region__msg { color: var(--ma-brick-ink); font-weight: 700; }

.ma-td-empty {
  padding: 32px 14px; text-align: center; font-size: 12.5px; line-height: 1.5;
  color: var(--ma-ink-3, #8A8077);
}

/* ── MAThings, the names, answered once ───────────────────────────
   One list over what the kitchen buys. Shelves fold through MAAccordion
   (never a page-local <details> memory), a row opens into its names, how
   it is sold and how each shop prices it.

   Two colours carry meaning here and nothing else does. **Zuzo's purple**
   is on the two things the slips taught rather than anybody typed, how a
   shop prices a thing, and what a reused name turned out to be. **Gold**
   is on a row that wants a word. Everything else is the page's own ink,
   because a list of two hundred things reads as noise the moment more
   than a few rows are coloured. */
.ma-thing-take { margin-bottom: 12px; }

.ma-thing-shops {
  margin-bottom: 14px;
  padding: 12px 13px;
  border-radius: 12px;
  background: var(--ma-zuzo-soft, #E5E1F8);
  border: 1px solid rgba(139, 123, 232, .45);
  border-left: 3px solid var(--ma-zuzo, #8B7BE8);
  display: flex; flex-direction: column; gap: 8px;
}
.ma-thing-shops .ma-section-title { color: var(--ma-zuzo-ink, #3D3287); margin: 0; }
.ma-thing-shop { display: flex; flex-direction: column; gap: 3px; }
.ma-thing-shop__l {
  font-size: 12px; line-height: 1.45; color: var(--ma-zuzo-ink, #3D3287);
}
/* A shop that prices one thing both ways is the finding, not a failure
   to learn, it is why every line from it gets asked about. */
.ma-thing-shop__mixed {
  font-size: 11.5px; line-height: 1.45; font-weight: 700;
  color: var(--ma-zuzo-ink, #3D3287);
}

.ma-thing-list { display: flex; flex-direction: column; gap: 10px; }
/* A shelf is a card of rows: one surface, hairlines between the things
   inside it, so a fold reads as one block rather than nine. */
.ma-thing-shelf {
  /* Matches `.ma-thing__h`'s 12px, so the shelf's chevron and count
     line up with the names and chips under them. */
  --ma-acc-inset: 12px;
  background: var(--ma-surface, #FFFDF8);
  border: 1px solid var(--ma-border, #E8E2D6);
  border-radius: 12px;
  overflow: hidden;
}
.ma-thing-shelf__body { display: flex; flex-direction: column; gap: 0; }

.ma-thing {
  background: var(--ma-surface, #FFFDF8);
  border-bottom: 1px solid var(--ma-border, #E8E2D6);
}
.ma-thing:last-child { border-bottom: 0; }
.ma-thing__h {
  list-style: none; cursor: pointer;
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  padding: 11px 12px; min-height: 44px; align-items: center;
}
.ma-thing__h::-webkit-details-marker { display: none; }
.ma-thing__name {
  font-size: 13.5px; font-weight: 700; color: var(--ma-ink, #1F2D2A);
}
.ma-thing__meta {
  grid-row: 1 / span 1; grid-column: 2;
  display: flex; align-items: center; gap: 8px; justify-content: flex-end;
}
/* The price line WRAPS. `nowrap` made this column as wide as its
   longest line and the grid's other column `1fr`, so a long shop name
   pushed the row past the card: "Tomatoes · R 18.00 a kg · Fruit & Veg
   City · 5 Sep · Sure" wanted 366px of a 344px row and the word chip
   went off the right edge, the operator saw "Ne…" where "Never seen"
   should be, on exactly the rows that most needed reading.

   Wrapping is the fix that keeps the NAME. Letting the name's column
   shrink instead (`minmax(0, 1fr)`) also stops the overflow, but it
   takes the room from the name, "Boerewors" came out 15px wide, an
   ellipsis and nothing else. Measured: wrapping costs the list no
   height at all, because these rows already carry a note line and had
   the slack. */
.ma-thing__price {
  font-size: 11.5px; color: var(--ma-ink-2, #4A5550);
  /* Breaks between the facts, never inside one, `priceHtml` holds each
     part of the server's " · "-joined line together. */
  white-space: normal;
}
.ma-thing__price-p { white-space: nowrap; }
/* The separator may break, and takes the space either side with it, so a
   wrapped line does not start or end on a stray middle dot. */
.ma-thing__price-sep { white-space: normal; }
/* The row that wants a word says what it wants, in the same place every
   time. No note is the common case and reads as fine. */
.ma-thing__note {
  grid-column: 1 / -1;
  font-size: 11px; color: var(--ma-gold-ink, #7A5610);
}
.ma-thing__body {
  padding: 0 12px 12px;
  display: flex; flex-direction: column; gap: 10px;
}

.ma-thing__names { display: flex; flex-direction: column; gap: 6px; }
.ma-thing__names-h {
  font-size: 10px; font-weight: 800; letter-spacing: .8px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}
.ma-thing__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ma-thing__chip {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 4px 9px; border-radius: 999px;
  font-family: 'Space Grotesk', ui-monospace, monospace;
  font-size: 11px; line-height: 1.3;
  background: var(--ma-cream, #F7F4ED);
  color: var(--ma-ink-2, #4A5550);
  border: 1px solid var(--ma-border, #E8E2D6);
}
/* A name nobody has confirmed yet, dashed, because it is a suggestion
   sitting on the row rather than an answer given. */
.ma-thing__chip--unsure { border-style: dashed; }
/* What a reused name turned out to be. Not what it IS, these are the
   answers it has had, one shop at a time, so they are history and wear
   Zuzo's colour rather than the settled ink. */
.ma-thing__chip--was {
  background: var(--ma-zuzo-soft, #E5E1F8);
  color: var(--ma-zuzo-ink, #3D3287);
  border-color: rgba(139, 123, 232, .45);
}
.ma-thing__chip--more { color: var(--ma-ink-3, #8A8077); border-style: dashed; }
.ma-thing__chip-sub { font-size: 10px; opacity: .8; }

.ma-thing__anchor {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border-radius: 10px;
  background: var(--ma-cream, #F7F4ED);
}
.ma-thing__anchor-l {
  flex: 1; font-size: 11.5px; font-weight: 700; line-height: 1.4;
  color: var(--ma-ink-2, #4A5550);
}

.ma-thing__habits { display: flex; flex-direction: column; gap: 5px; }
.ma-thing__habit {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 11.5px; line-height: 1.45; color: var(--ma-zuzo-ink, #3D3287);
}
.ma-thing__habit-l { flex: 1; }
.ma-thing__habit-you {
  flex: 0 0 auto; font-size: 10px; font-weight: 800; letter-spacing: .3px;
  text-transform: uppercase; color: var(--ma-ink-3, #8A8077);
}

.ma-thing__acts { display: flex; flex-wrap: wrap; gap: 8px; }
.ma-thing__acts .ma-btn { flex: 1 1 auto; min-height: 38px; }

/* A name row has no size, no anchor and no buttons, there is nothing to
   answer here. The left edge says which kind of row it is at a glance. */
.ma-thing--was { border-left: 3px solid var(--ma-zuzo, #8B7BE8); }

.ma-thing-empty {
  padding: 28px 12px; text-align: center; font-size: 12.5px;
  color: var(--ma-ink-3, #8A8077);
}

/* ── MAMoneyWord, the four words ──────────────────────────────────
   Sure · Looks right · Not sure · Never seen. The operator wording of
   the resolver ladder, mirroring shared/money_out_words.py. Three tones
   from the shipped palette, no fourth colour: forest for settled, gold
   for wants-a-word, brick for nothing-known. No percentage, ever, a
   number invites arithmetic nobody can do. */
/* ── MAOrganise, one line as a card (Box and Organise V3, frames 3–7) ── */
.ma-org-card {
  position: relative; display: flex; flex-direction: column; gap: 12px;
  padding: 14px 14px 16px; border-radius: 18px;
  background: var(--ma-surface); border: 1px solid var(--ma-border);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08);
  touch-action: pan-y; will-change: transform;
}
.ma-org-card--open { gap: 14px; }
.ma-org-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ma-org-back {
  display: inline-flex; align-items: center; gap: 2px; border: 0; background: transparent;
  padding: 0; font-size: 12.5px; font-weight: 700; color: var(--ma-brick); cursor: pointer;
}
.ma-org-back svg { width: 16px; height: 16px; }
.ma-org-counter { font-size: 11px; font-weight: 700; color: var(--ma-ink-3); text-align: right; }
.ma-org-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.ma-org-shop { font-size: 17px; font-weight: 800; letter-spacing: -.3px; color: var(--ma-ink); }
.ma-org-meta__sub { font-size: 12px; color: var(--ma-ink-2); }
.ma-org-amount {
  margin-left: auto; font-family: var(--ma-font-num); font-size: 19px; font-weight: 800;
  letter-spacing: -.4px; color: var(--ma-ink);
}
/* The line as printed, the document, not our reading of it. */
.ma-org-raw {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 9px 11px; border: 1px dashed var(--ma-border-strong); border-radius: 9px;
  background: var(--ma-cream);
}
.ma-org-raw__text, .ma-org-raw__amount {
  font-family: 'Space Grotesk', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; letter-spacing: .4px;
}
.ma-org-raw__text { color: var(--ma-ink-2); min-width: 0; word-break: break-word; }
.ma-org-raw__amount { font-weight: 700; flex: 0 0 auto; }
.ma-org-crop .ma-crop { margin: 0; }
.ma-org-tools { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ma-org-tool {
  display: inline-flex; align-items: center; gap: 5px; padding: 7px 11px; min-height: 34px;
  border-radius: 999px; border: 1.5px solid var(--ma-border); background: var(--ma-surface);
  font-size: 11.5px; font-weight: 800; color: var(--ma-ink-2); cursor: pointer;
}
.ma-org-tool svg { width: 14px; height: 14px; }
.ma-org-section { display: flex; flex-direction: column; gap: 8px; }
.ma-org-section__title {
  font-size: 10.5px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--ma-ink-3);
}
/* A row is a question with its answer and one word, tap to change. */
.ma-org-row {
  display: flex; flex-direction: column; gap: 3px; width: 100%; text-align: left;
  padding: 10px 12px; min-height: 48px; border-radius: 10px; cursor: pointer;
  background: var(--ma-surface); border: 1px solid var(--ma-border);
  font-family: var(--ma-font-body); color: var(--ma-ink);
}
.ma-org-row__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ma-org-row__label { font-size: 10px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-org-row__value { font-size: 14.5px; font-weight: 800; color: var(--ma-ink); }
.ma-org-row__value--none { font-weight: 600; color: var(--ma-gold-ink); }
.ma-org-row__sub { font-size: 11px; line-height: 1.4; color: var(--ma-ink-3); }
.ma-org-kinds { display: flex; flex-wrap: wrap; gap: 6px; margin-top: -2px; }
.ma-org-vat {
  align-self: flex-start; margin-top: 4px; padding: 0; border: 0; background: transparent;
  font-size: 11px; font-weight: 700; color: var(--ma-ink-2); text-decoration: underline dotted;
  cursor: pointer; font-family: var(--ma-font-body);
}
/* How it comes, the vertical ladder, one step a row. */
.ma-org-ladder { display: flex; flex-direction: column; gap: 4px; }
.ma-org-step {
  display: grid; grid-template-columns: 18px auto 1fr auto; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: 9px; background: var(--ma-cream);
}
.ma-org-step--result { background: var(--ma-forest-soft); }
.ma-org-step__op { font-family: var(--ma-font-num); font-weight: 800; font-size: 15px; color: var(--ma-ink-3); text-align: center; }
.ma-org-step__value { font-family: var(--ma-font-num); font-weight: 800; font-size: 15px; letter-spacing: -.3px; color: var(--ma-ink); }
.ma-org-step__words { font-size: 12px; color: var(--ma-ink-2); min-width: 0; }
.ma-org-ask {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%;
  padding: 10px 12px; border-radius: 9px; cursor: pointer; text-align: left;
  border: 1px dashed var(--ma-gold); background: var(--ma-gold-soft);
  font-family: var(--ma-font-body); font-size: 12.5px; font-weight: 700; color: var(--ma-gold-ink);
}
.ma-org-ask__cta { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .6px; }
/* Where the container word came from, under the ladder's first step. */
.ma-org-comes {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
  padding: 0 10px 6px; font-size: 11px; line-height: 1.4; color: var(--ma-ink-3);
}
.ma-org-comes__link {
  border: 0; padding: 0; background: none; cursor: pointer; font-family: var(--ma-font-body);
  font-size: 11px; font-weight: 800; color: var(--ma-forest-ink); text-decoration: underline; text-underline-offset: 2px;
}
/* The Comes in chips on the How-much sheet, the units row's shape. */
.ma-hm__comes { display: flex; flex-direction: column; gap: 6px; }
/* What one costs you. */
.ma-org-costs { display: flex; flex-direction: column; gap: 4px; }
.ma-org-cost {
  display: grid; grid-template-columns: 1fr auto; grid-template-areas: "label rand" "sub chip";
  align-items: center; column-gap: 8px; row-gap: 2px; padding: 8px 10px;
  border-radius: 9px; background: var(--ma-cream);
}
.ma-org-cost__label { grid-area: label; font-size: 13px; font-weight: 700; color: var(--ma-ink); }
.ma-org-cost__rand { grid-area: rand; font-family: var(--ma-font-num); font-weight: 800; font-size: 15px; color: var(--ma-ink); text-align: right; }
.ma-org-cost__sub { grid-area: sub; font-size: 11px; color: var(--ma-ink-3); }
.ma-org-cost .ma-ladder { grid-area: chip; justify-self: end; }
/* In the kitchen. */
.ma-org-kitchen { display: flex; flex-direction: column; gap: 4px; }
.ma-org-krow {
  display: grid; grid-template-columns: 1fr auto; grid-template-areas: "label tail" "value tail" "sub tail";
  column-gap: 10px; row-gap: 2px; padding: 8px 10px; border-radius: 9px;
  background: var(--ma-cream); align-items: center;
}
.ma-org-krow__label { grid-area: label; font-size: 10px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-org-krow__value { grid-area: value; font-size: 14px; font-weight: 800; color: var(--ma-ink); }
.ma-org-krow__value--none { font-weight: 600; font-size: 12px; color: var(--ma-ink-3); }
.ma-org-krow__sub { grid-area: sub; font-size: 11px; color: var(--ma-ink-3); }
.ma-org-krow__tail { grid-area: tail; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.ma-org-weigh {
  padding: 5px 10px; min-height: 30px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--ma-zuzo); background: transparent;
  font-family: var(--ma-font-body); font-size: 11px; font-weight: 800; color: var(--ma-zuzo-ink);
}
.ma-org-folded { font-size: 11.5px; color: var(--ma-ink-3); }
.ma-org-folded summary { cursor: pointer; padding: 6px 2px; font-weight: 700; }
.ma-org-folded ul { margin: 0 0 4px; padding-left: 18px; }
.ma-org-folded li { padding: 2px 0; }
/* When you take this line… */
.ma-org-take { display: flex; flex-direction: column; gap: 5px; padding: 11px 12px; border-radius: 11px; background: var(--ma-forest-soft); }
.ma-org-take__title { font-size: 10.5px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase; color: var(--ma-forest-ink); }
.ma-org-take__line { font-size: 13px; line-height: 1.45; color: var(--ma-forest-ink); }
.ma-org-take__line b { font-family: var(--ma-font-num); }
.ma-org-take__blocked { font-size: 12.5px; font-weight: 700; color: var(--ma-gold-ink); }
.ma-org-take__next { font-size: 12.5px; color: var(--ma-ink-2); margin-top: 4px; }
.ma-org-take__confirm { font-size: 12.5px; color: var(--ma-gold-ink); margin-top: 4px; }
.ma-org-actions { display: flex; flex-direction: column; gap: 8px; }
.ma-org-hint { display: flex; align-items: center; justify-content: space-between; font-size: 11px; color: var(--ma-ink-3); }
.ma-org-later {
  border: 0; background: transparent; padding: 4px 0; cursor: pointer;
  font-family: var(--ma-font-body); font-size: 12px; font-weight: 700; color: var(--ma-ink-2);
}
.ma-org-ov {
  position: absolute; top: 14px; padding: 4px 10px; border-radius: 8px; font-size: 12px;
  font-weight: 800; opacity: 0; transition: opacity .1s; pointer-events: none;
}
.ma-org-ov--take { right: 14px; background: var(--ma-forest); color: #fff; }
.ma-org-ov--opt { left: 14px; background: var(--ma-gold); color: #3A2A05; }
.ma-org-done { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 36px 16px; text-align: center; }
.ma-org-done__title { font-size: 17px; font-weight: 800; color: var(--ma-ink); }
.ma-org-done__sub { font-size: 12.5px; color: var(--ma-ink-3); margin-bottom: 8px; }
/* What kind of thing (frame 7). */
.ma-org-kind, .ma-org-ask-sheet { display: flex; flex-direction: column; gap: 12px; padding: 12px 16px 22px; max-height: 90vh; }
.ma-org-ask-sheet { overflow-y: auto; }
.ma-org-kind__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ma-org-kind__title { font-size: 17px; font-weight: 800; letter-spacing: -.3px; color: var(--ma-ink); }
.ma-org-kind__close { border: 0; background: none; padding: 4px; font-size: 12px; font-weight: 700; color: var(--ma-brick); cursor: pointer; font-family: var(--ma-font-body); }
.ma-org-kind__intro { font-size: 12.5px; line-height: 1.45; color: var(--ma-ink-2); }
/* The list scrolls; the head and the Use button stay where the thumb is. */
.ma-org-kind__list { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; min-height: 0; overscroll-behavior: contain; }
.ma-org-kind__row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; cursor: pointer;
  padding: 10px 12px; border-radius: 10px; border: 1.5px solid var(--ma-border); background: var(--ma-surface);
  font-family: var(--ma-font-body); color: var(--ma-ink);
}
.ma-org-kind__row.is-on { border-color: var(--ma-forest); background: var(--ma-forest-soft); }
.ma-org-kind__body { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ma-org-kind__label { font-size: 14px; font-weight: 800; }
.ma-org-kind__eg { font-size: 11px; color: var(--ma-ink-3); }
.ma-org-kind__why { font-size: 11.5px; line-height: 1.4; color: var(--ma-ink-2); }
.ma-org-kind__tag {
  flex: 0 0 auto; padding: 3px 8px; border-radius: 999px; background: var(--ma-cream);
  font-size: 10px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; color: var(--ma-ink-3);
}
.ma-org-kind__foot { padding-top: 4px; }
.ma-org-ask-sheet__unit { font-size: 12.5px; font-weight: 700; color: var(--ma-ink-2); align-self: center; }
/* Look closer (frame 6), the slip zoomed in place, the ring on the line. */
.ma-org-closer-host { align-items: stretch; }
.ma-org-closer { display: flex; flex-direction: column; gap: 10px; width: 100%; padding: 12px 14px 20px; background: var(--ma-surface); border-radius: 18px 18px 0 0; margin-top: auto; max-height: 92%; }
.ma-org-closer__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ma-org-closer__title { font-size: 15px; font-weight: 800; color: var(--ma-ink); }
.ma-org-stage { position: relative; overflow: hidden; border-radius: 12px; background: #1D2225; height: clamp(300px, 52vh, 560px); touch-action: none; }
.ma-org-zoom { position: relative; width: 100%; transform-origin: 0 0; }
.ma-org-zoom__img { display: block; width: 100%; height: auto; max-width: none; user-select: none; -webkit-user-drag: none; }
.ma-org-ring {
  position: absolute; box-sizing: border-box; border: 0; border-radius: 8px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, .28); cursor: grab; touch-action: none;
}
/* The line's box, as a fill: no outline, the tint padded past the box so the
   row it names is easy to see. `color-mix` keeps it one rule in both themes,
   off the theme's own `--ma-brick`. */
.ma-org-ring::before {
  content: ''; position: absolute; inset: -5px; border-radius: 11px;
  background: color-mix(in srgb, var(--ma-brick) 22%, transparent);
  pointer-events: none;
}
.ma-org-closer__foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ma-org-closer__hint { flex: 1; font-size: 11px; color: var(--ma-ink-3); }
/* The pill the foot grows once the ring has been dragged: read the row the
   ring now sits on. Its own row above the tools, because it is the next
   thing to do and Reset is not. */
.ma-org-reread-row { flex: 1 0 100%; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ma-org-reread {
  display: inline-flex; align-items: center; min-height: 36px; padding: 8px 14px;
  border-radius: 999px; border: 1.5px solid var(--ma-forest); background: var(--ma-forest-soft);
  font-family: var(--ma-font-body); font-size: 12.5px; font-weight: 800; color: var(--ma-forest);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.ma-org-reread__why { font-size: 11px; line-height: 1.4; color: var(--ma-ink-3); }
.ma-org-closer__note { font-size: 11.5px; line-height: 1.45; color: var(--ma-ink-2); }

/* ── MAHowMuch, How much is in it? (design/design_handoff_how_much) ── */
.ma-hm__big {
  display: flex; align-items: baseline; gap: 10px; padding: 12px 16px; border-radius: 14px;
  background: var(--ma-surface); border: 1.5px solid var(--ma-forest); box-shadow: 0 0 0 3px var(--ma-forest-soft);
}
.ma-hm__big--empty { border-color: var(--ma-border-strong); box-shadow: none; background: var(--ma-cream); }
.ma-hm__big--rough { border-color: var(--ma-gold); box-shadow: 0 0 0 3px var(--ma-gold-soft); }
.ma-hm__n {
  flex: 1; min-width: 0; border: 0; background: transparent; padding: 0; outline: none;
  font-family: var(--ma-font-display); font-size: 44px; font-weight: 700; letter-spacing: -1px; line-height: 1; color: var(--ma-ink);
  -moz-appearance: textfield;
}
.ma-hm__n::-webkit-outer-spin-button, .ma-hm__n::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ma-hm__n::placeholder { color: var(--ma-ink-3); font-weight: 500; }
.ma-hm__u { font-family: var(--ma-font-num); font-size: 20px; font-weight: 700; color: var(--ma-ink-2); flex: 0 0 auto; }
.ma-hm__units { display: flex; flex-wrap: wrap; gap: 6px; }
/* How it comes, level by level, one rung a row above the field. */
.ma-hm__lvl { display: flex; flex-direction: column; gap: 8px; padding: 12px 13px; border-radius: 12px;
  background: var(--ma-surface); border: 1.5px solid var(--ma-forest); box-shadow: 0 0 0 3px var(--ma-forest-soft); }
.ma-hm__lvl-q { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; font-weight: 800; color: var(--ma-ink-2); }
.ma-hm__lvl-q--last { margin-top: 2px; }
.ma-hm__addlvl { display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 2px; border: 0; background: none;
  font: 800 12px var(--ma-font-body); color: var(--ma-ink-3); text-align: left; cursor: pointer; }
.ma-hm__addlvl svg { width: 14px; height: 14px; flex: 0 0 auto; }
.ma-hm__lvl-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ma-hm__n--lvl { font-size: 32px; flex: 1 1 90px; min-width: 90px; }
.ma-hm__whyrow { display: flex; align-items: flex-start; gap: 8px; }
.ma-hm__whyrow .ma-ladder { flex: 0 0 auto; margin-top: 1px; }
/* The sum under the field, `6 tins × 195 g = 1 170 g on this slip`. */
.ma-hm__sum { display: flex; flex-direction: column; gap: 6px; padding: 11px 13px; border-radius: 11px; background: var(--ma-cream); border: 1px solid var(--ma-border); }
.ma-hm__sum-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-family: var(--ma-font-num); font-size: 13px; color: var(--ma-ink-2); }
.ma-hm__sum-row b { color: var(--ma-ink); font-weight: 700; }
.ma-hm__sum-eq { flex: 1; min-width: 0; }
.ma-hm__sum-is { flex: 0 0 auto; font-weight: 700; color: var(--ma-ink); }
.ma-hm__sum-is--ok { color: var(--ma-forest-ink); }
.ma-hm__sum-why { font-family: var(--ma-font-body, inherit); font-size: 11px; color: var(--ma-ink-3); line-height: 1.4; }
/* The pack answer, read back in the operator's words:
   `So 1 bag is 1.5 kg and holds 12 packs, right?` */
.ma-hm__read { font-family: var(--ma-font-body, inherit); font-size: 11.5px; line-height: 1.45; color: var(--ma-ink-2); }
.ma-hm__u--lead { font-size: 22px; }
.ma-hm__then { display: flex; flex-direction: column; gap: 4px; padding: 11px 12px; border-radius: 11px; background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-hm__then--empty { background: var(--ma-cream); color: var(--ma-ink-3); }
.ma-hm__then-t { font-size: 10.5px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase; }
.ma-hm__then-l { font-size: 13px; line-height: 1.45; font-family: var(--ma-font-num); }
.ma-hm .ma-btn[disabled] { opacity: .45; }
.ma-hm__way { gap: 12px; }
.ma-hm__way-ic { width: 36px; height: 36px; border-radius: 10px; background: var(--ma-cream); display: inline-flex; align-items: center; justify-content: center; color: var(--ma-ink-2); flex: 0 0 auto; }
.ma-hm__way-ic svg { width: 18px; height: 18px; }
/* Ask Zuzo to look it up, the fourth way (design/design_handoff_how_much_zuzo/).
   Purple while Zuzo works, gold once it is a Rough number of yours, grey with
   nothing found. The finds are ordinary guess rows plus one line of money. */
.ma-hm__way--zuzo .ma-hm__way-ic { background: var(--ma-zuzo-soft); color: var(--ma-zuzo-ink); }
.ma-hm__look { display: flex; flex-direction: column; gap: 10px; padding: 12px 14px 14px; border-radius: 14px;
  background: var(--ma-surface); border: 1.5px solid var(--ma-gold); box-shadow: 0 0 0 3px var(--ma-gold-soft); }
.ma-hm__look--working { border-color: var(--ma-zuzo); box-shadow: 0 0 0 3px var(--ma-zuzo-soft); }
.ma-hm__look--none { border-color: var(--ma-border-strong); box-shadow: none; }
.ma-hm__look-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ma-hm__think { flex: 1; min-width: 0; }
.ma-hm__clock { margin-left: auto; font-family: var(--ma-font-num); font-size: 12px; font-weight: 700; color: var(--ma-ink-3); flex: 0 0 auto; }
.ma-hm__kicker { display: inline-flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 800; letter-spacing: .9px; text-transform: uppercase; color: var(--ma-ink-3); min-width: 0; }
.ma-hm__kicker-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ma-zuzo); flex: 0 0 auto; }
.ma-hm__mark { display: inline-flex; width: 24px; height: 24px; border-radius: 7px; background: var(--ma-zuzo-soft); color: var(--ma-zuzo-ink); align-items: center; justify-content: center; flex: 0 0 auto; }
.ma-hm__mark svg { width: 14px; height: 14px; }
.ma-hm__look-say { font-size: 13px; line-height: 1.45; color: var(--ma-ink); }
.ma-hm__qrow { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.ma-hm__qlabel { font-size: 10px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-hm__q { display: inline-flex; align-items: center; padding: 4px 9px; border-radius: 8px; background: var(--ma-cream); border: 1px dashed var(--ma-border-strong);
  font-family: var(--ma-font-num); font-size: 11.5px; letter-spacing: .3px; color: var(--ma-ink-2); }
.ma-hm__look-note { font-size: 11px; color: var(--ma-ink-3); line-height: 1.4; }
.ma-hm__look-foot { display: flex; align-items: center; gap: 10px; }
.ma-hm__lookslot { display: flex; flex-direction: column; }
.ma-hm__opt { align-items: flex-start; }
.ma-hm__opt .ma-ladder { margin-top: 2px; }
.ma-hm__opt--on { border-color: var(--ma-forest); box-shadow: 0 0 0 2px var(--ma-forest-soft); }
.ma-hm__check { display: flex; align-items: flex-start; gap: 6px; font-size: 11.5px; line-height: 1.4; font-weight: 600; margin-top: 2px; }
.ma-hm__check svg { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 1px; }
.ma-hm__check--fits { color: var(--ma-forest-ink); }
.ma-hm__check--dear, .ma-hm__check--cheap { color: var(--ma-gold-ink); }
.ma-hm__check--else, .ma-hm__check--none { color: var(--ma-ink-2); }
.ma-hm__page { font-size: 11.5px; color: var(--ma-ink-3); }
.ma-hm__look-links { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ma-hm__why { font: inherit; font-size: 11.5px; font-weight: 800; color: var(--ma-zuzo-ink); text-decoration: underline; text-underline-offset: 2px; background: none; border: 0; padding: 0; cursor: pointer; }
.ma-hm__why-body { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; border-radius: 10px; background: var(--ma-cream); }
.ma-hm__why-p { margin: 0; font-size: 12px; line-height: 1.45; color: var(--ma-ink-2); }
.ma-hm__why-t { font-size: 10px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase; color: var(--ma-ink-3); margin-top: 4px; }
.ma-hm__why-l { margin: 0; padding-left: 16px; font-size: 11.5px; line-height: 1.45; color: var(--ma-ink-2); }

/* ── MAWhatIsIt, What is this? (design/design_handoff_what_is_it) ── */
.ma-what-sheet { max-height: min(94vh, var(--ma-sheet-max-h)); }
.ma-what-scroll { overflow-y: auto; -webkit-overflow-scrolling: touch; }
.ma-what { display: flex; flex-direction: column; gap: 12px; padding: 4px 16px 22px; }
.ma-what__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ma-what__where { font-size: 10.5px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-what__close { border: 0; background: transparent; padding: 6px 0; font: 700 12px var(--ma-font-body); color: var(--ma-brick); cursor: pointer; }
.ma-what__title { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -.3px; color: var(--ma-ink); }
.ma-what__sub { font-size: 11.5px; line-height: 1.4; color: var(--ma-ink-3); }
.ma-what__stitle { margin-top: 6px; font-size: 11px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-what__answer {
  display: flex; flex-direction: column; gap: 10px; padding: 14px; border-radius: 14px;
  background: var(--ma-surface); border: 1.5px solid var(--ma-forest); box-shadow: 0 0 0 3px var(--ma-forest-soft);
}
.ma-what__answer--gold { border-color: var(--ma-gold); box-shadow: 0 0 0 3px var(--ma-gold-soft); }
.ma-what__answer--brick { border-color: var(--ma-brick); box-shadow: 0 0 0 3px var(--ma-brick-soft); }
.ma-what__answer--zuzo { border-color: var(--ma-zuzo); box-shadow: 0 0 0 3px var(--ma-zuzo-soft); }
.ma-what__top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ma-what__kicker { display: inline-flex; align-items: center; gap: 6px; font-size: 10px; font-weight: 800; letter-spacing: .9px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-what__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ma-zuzo); }
.ma-what__name { font-size: 17px; font-weight: 800; letter-spacing: -.2px; line-height: 1.2; color: var(--ma-ink); }
.ma-what__kind { font-size: 12px; font-weight: 600; color: var(--ma-ink-2); }
.ma-what__why { font-size: 11.5px; line-height: 1.45; color: var(--ma-ink-3); }
.ma-what__evidence { display: flex; gap: 8px; }
.ma-what__ev { flex: 1; display: flex; flex-direction: column; gap: 1px; padding: 8px 10px; border-radius: 9px; background: var(--ma-cream); min-width: 0; }
.ma-what__ev-l { font-size: 9.5px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-what__ev-v { font-family: var(--ma-font-num); font-size: 13px; font-weight: 700; color: var(--ma-ink); }
.ma-what__answer .ma-btn svg { width: 18px; height: 18px; }
.ma-what__after { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 11.5px; line-height: 1.4; color: var(--ma-ink-3); }
.ma-what__link { flex: 0 0 auto; border: 0; background: transparent; padding: 6px 0; font: 800 11.5px var(--ma-font-body); color: var(--ma-forest-ink); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.ma-what__rows { display: flex; flex-direction: column; gap: 8px; }
.ma-what__row {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 56px; padding: 11px 12px; text-align: left;
  background: var(--ma-surface); border: 1px solid var(--ma-border); border-radius: 10px; cursor: pointer; font-family: var(--ma-font-body);
}
.ma-what__row-body { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ma-what__row-name { font-size: 14.5px; font-weight: 800; color: var(--ma-ink); }
.ma-what__row-hint { font-size: 11px; line-height: 1.4; color: var(--ma-ink-3); }
.ma-what__row-ic { width: 36px; height: 36px; border-radius: 10px; background: var(--ma-cream); display: inline-flex; align-items: center; justify-content: center; color: var(--ma-ink-2); flex: 0 0 auto; }
.ma-what__row-ic svg { width: 18px; height: 18px; }
.ma-what__row--new .ma-what__row-ic { background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-what__chev { display: inline-flex; width: 18px; height: 18px; color: var(--ma-ink-3); flex: 0 0 auto; }
.ma-what__chev svg { width: 18px; height: 18px; }
.ma-what__search {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 48px; padding: 10px 12px; border-radius: 12px; text-align: left;
  background: var(--ma-cream); border: 1.5px solid var(--ma-border-strong); cursor: pointer; font-family: var(--ma-font-body);
}
.ma-what__search svg { width: 18px; height: 18px; color: var(--ma-ink-3); flex: 0 0 auto; }
.ma-what__search-ph { flex: 1; font-size: 14px; color: var(--ma-ink-3); }
.ma-what__search-hint { font-size: 10.5px; font-weight: 700; color: var(--ma-ink-3); }
.ma-what__strip { display: flex; align-items: center; gap: 10px; padding: 11px 12px; border-radius: 10px; background: var(--ma-brick-soft); color: var(--ma-brick-ink); }
.ma-what__strip-t { flex: 1; font-size: 12.5px; font-weight: 700; line-height: 1.35; }
.ma-what__same { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 9px; background: var(--ma-gold-soft); }
.ma-what__same-t { flex: 1; font-size: 11.5px; line-height: 1.4; color: var(--ma-gold-ink); }
.ma-what__small {
  display: inline-flex; align-items: center; min-height: 30px; padding: 5px 10px; border-radius: 999px; flex: 0 0 auto; cursor: pointer;
  border: 1px solid var(--ma-border-strong); background: var(--ma-surface); font: 800 10.5px var(--ma-font-body); color: var(--ma-ink-2);
}
.ma-what__foot { display: flex; flex-direction: column; gap: 10px; }
.ma-what__again { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 36px; border: 0; background: transparent; font: 800 12px var(--ma-font-body); color: var(--ma-zuzo-ink); cursor: pointer; }
.ma-what__again svg { width: 14px; height: 14px; }
/* Name it, the step the sheet takes for a line no name fits
   (design/design_handoff_name_it/). One card, in the same overlay:
   a form is a destination, so it takes the sheet over rather than
   stacking a second sheet on top of it. */
.ma-what__answer--quiet { border-color: var(--ma-border-strong); box-shadow: none; }
.ma-what__dot--you { background: var(--ma-forest); }
.ma-what__field { display: flex; flex-direction: column; gap: 6px; }
.ma-what__field-l { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 10px; font-weight: 800; letter-spacing: .9px; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-what__input {
  width: 100%; min-height: 52px; padding: 12px 14px; border-radius: 12px; box-sizing: border-box;
  background: var(--ma-surface); border: 1.5px solid var(--ma-forest); box-shadow: 0 0 0 3px var(--ma-forest-soft);
  font: 800 19px/1.2 var(--ma-font-body); letter-spacing: -.2px; color: var(--ma-ink);
}
.ma-what__input::placeholder { font-weight: 600; font-size: 16px; color: var(--ma-ink-3); }
.ma-what__input:placeholder-shown { border-color: var(--ma-border-strong); box-shadow: none; }
.ma-what__input--gold, .ma-what__input--gold:placeholder-shown { border-color: var(--ma-gold); box-shadow: 0 0 0 3px var(--ma-gold-soft); }
.ma-what__hint { font-size: 11.5px; line-height: 1.45; color: var(--ma-ink-3); }
.ma-what__chips { display: flex; flex-wrap: wrap; gap: 8px; }
.ma-what__more { border-style: dashed; color: var(--ma-ink-3); }
.ma-what__slip { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border-radius: 10px; background: var(--ma-cream); }
.ma-what__slip-t { flex: 1; font-size: 12px; line-height: 1.4; color: var(--ma-ink-2); }
.ma-what__link--mid { align-self: center; }
.ma-what__back { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 44px; border: 0; background: transparent; font: 800 13px var(--ma-font-body); color: var(--ma-ink-2); cursor: pointer; }
.ma-what__back svg { width: 16px; height: 16px; }

/* The settle strip over the Organise card once a What-is-this tap lands. */
.ma-org-settle { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px 10px; margin-bottom: 10px; padding: 10px 12px; border-radius: 10px; background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-org-settle > svg { width: 18px; height: 18px; flex: 0 0 auto; }
.ma-org-settle__t { flex: 1 1 200px; font-size: 12px; line-height: 1.4; font-weight: 600; }
.ma-org-settle .ma-tell__putback { flex: 0 0 auto; }
/* The two ways into a re-read on this line, and the note that is a third
   (OCR-19). The Zuzo icon and the pencil are one row of two ghost buttons, so
   neither reads as the screen's own primary, and they size to their own words
   and wrap rather than shorten one: the labels are the server's, and a
   truncated "Read this li..." is the page editing a sentence it was handed.
   The note sits under them wearing the label the server wrote. Off, none of
   this is drawn. */
.ma-org-ways { display: flex; flex-direction: column; gap: 8px; }
.ma-org-ways__row { display: flex; flex-wrap: wrap; align-items: stretch; gap: 8px; }
.ma-org-ways__row .ma-org-way { flex: 0 1 auto; justify-content: center; gap: 7px; }
.ma-org-way svg { width: 16px; height: 16px; flex: 0 0 auto; }
.ma-org-way span { min-width: 0; }
.ma-org-ways__note { display: flex; flex-direction: column; gap: 3px; }
.ma-org-ways__note > span {
  font-size: 10.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-org-ways__tell { display: block; }
/* A read this card started, or a note that got nowhere: the server's sentence,
   in Zuzo's colour, over the card. */
.ma-org-run {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  margin-bottom: 10px; padding: 10px 12px; border-radius: 10px;
  background: var(--ma-zuzo-soft); color: var(--ma-zuzo-ink);
  border: 1px solid var(--ma-zuzo);
}
.ma-org-run > svg { width: 18px; height: 18px; flex: 0 0 auto; }
.ma-org-run__t { flex: 1 1 200px; font-size: 12px; line-height: 1.4; font-weight: 600; }

.ma-word {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .2px;
  white-space: nowrap;
  border: 1px solid transparent;
}
.ma-word--sure,
.ma-word--looks-right {
  background: var(--ma-forest-soft, #DBE8DC);
  color: var(--ma-forest-ink, #1F4A2E);
  border-color: rgba(46, 94, 62, .28);
}
/* Looks right is settled enough to act on but not decided, same family,
   quieter, so a glance can tell the two apart without reading. */
.ma-word--looks-right {
  background: transparent;
}
.ma-word--not-sure {
  background: var(--ma-gold-soft, #FBE9B8);
  color: var(--ma-gold-ink, #7A5610);
  border-color: rgba(232, 179, 62, .38);
}
.ma-word--never-seen {
  background: var(--ma-brick-soft, #F6E3DB);
  color: var(--ma-brick-ink, #6E1E0F);
  border-color: rgba(168, 51, 31, .28);
}

/* Resolver "line_rate", the slip's own arithmetic (cost divided by the rate
   printed beside it). Forest, because both numbers came off the document:
   this is read, not inferred, and it outranks every derived tier below. */
.ma-prov-chip--line-rate {
  background: rgba(46, 94, 62, 0.14);
  color: var(--ma-forest-ink, #1F4A2E);
  border-color: rgba(46, 94, 62, 0.32);
}
/* Resolver "parsed from raw name", amber (heuristic, not authoritative). */
.ma-prov-chip--raw-parsed {
  background: rgba(245, 158, 11, 0.12);
  color: var(--ma-gold-ink);
  border-color: rgba(245, 158, 11, 0.28);
}
/* Needs human input, red, draws the eye. */
.ma-prov-chip--needs-review,
.ma-prov-chip--needs-capture {
  background: rgba(239, 68, 68, 0.14);
  color: #991b1b;
  border-color: rgba(239, 68, 68, 0.32);
}
/* No data, pale. */
.ma-prov-chip--none {
  background: rgba(127, 127, 127, 0.08);
  color: var(--ma-ink-3);
  border-color: rgba(127, 127, 127, 0.18);
}

/* ============================================================
 * Plain-language summary redesign, score-card grid +
 * per-category palette for the bar chart.
 * ============================================================ */

/* Per-category palette, six accessible-contrast hues. The frontend
   picks one deterministically per category_key via maCategoryColor()
   so meat is the same colour every page load even if categories
   change rank between periods. */
:root {
  --ma-cat-1: #2481cc;  /* blue  , Telegram accent family */
  --ma-cat-2: #f59e0b;  /* amber  */
  --ma-cat-3: #10b981;  /* green  */
  --ma-cat-4: #8b5cf6;  /* violet */
  --ma-cat-5: #ec4899;  /* pink   */
  --ma-cat-6: var(--ma-brick);  /* red   , Other / uncategorised gets this */
}

/* Score-card grid, 2×2 on phone, scales naturally on tablets.
   Canonical KPI tile pattern. Use for Total Sales / Net Payout /
   Tips / Orders style 4-up rows, or Money in / Money out / Delta
   style summary cards. */
.ma-scorecard-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ma-sp-2);
  margin-bottom: var(--ma-sp-4);
}
.ma-scorecard {
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  padding: var(--ma-sp-4) var(--ma-sp-5);
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Without this the floor below is a floor on the CONTENT box, so it
     means 110px rather than the 84px it says, and a card of a head, a
     number and one line (59px of content) carried 26px of dead space
     under it on every screen in the app. */
  box-sizing: border-box;
  min-height: 84px;
}
.ma-scorecard__head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-scorecard__icon {
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ma-ink-3);
}
.ma-scorecard__icon svg { width: 16px; height: 16px; }
.ma-scorecard__value {
  font-family: var(--ma-font-num);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.4px;
  color: var(--ma-ink);
  line-height: 1.15;
}
/* Accent colour variants for the value, used when a KPI tile wants
   to highlight a particular metric (e.g. Net Payout in forest,
   Tips in gold). Subtler than a coloured tile background; the
   surrounding card stays neutral. */
.ma-scorecard__value--brick   { color: var(--ma-brick); }
.ma-scorecard__value--forest  { color: var(--ma-forest); }
.ma-scorecard__value--gold    { color: var(--ma-gold); }
.ma-scorecard__value--zuzo    { color: var(--ma-zuzo); }
.ma-scorecard__sub {
  font-size: 11px;
  color: var(--ma-ink-3);
}
/* Delta colouring, semantic. `--up` is the direction whose meaning
   the page assigns; for *expenses* up = bad (brick), for *sales* up
   = good (forest). Pages pick the modifier that fits their domain. */
.ma-scorecard__delta--up   { color: var(--ma-brick); }
.ma-scorecard__delta--down { color: var(--ma-forest); }
.ma-scorecard__delta--up   .ma-scorecard__icon { color: var(--ma-brick); }
.ma-scorecard__delta--down .ma-scorecard__icon { color: var(--ma-forest); }
/* Positive / negative aliases, for pages where `--up` semantics
   don't fit (e.g. revenue up = good). Identical to the inverse pair
   above; use whichever reads cleaner at the call site. */
.ma-scorecard__delta--positive { color: var(--ma-forest); }
.ma-scorecard__delta--negative { color: var(--ma-brick); }
.ma-scorecard__delta--positive .ma-scorecard__icon { color: var(--ma-forest); }
.ma-scorecard__delta--negative .ma-scorecard__icon { color: var(--ma-brick); }

/* Card title, small UPPERCASE heading inside a .ma-card.
   The standard sub-heading inside a chart / summary card.
   Pairs naturally with Plotly chart cards and KPI groupings. */
.ma-card-title {
  font: 700 13px var(--ma-font-body);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ma-ink-3);
  margin: 0 0 var(--ma-sp-3);
}

/* Caption, small grey text used for footnotes, compare strings,
   period labels, secondary metadata. */
.ma-caption {
  font-size: 11px;
  color: var(--ma-ink-3);
}
.ma-caption--sm { font-size: 10px; }

/* Plain-language takeaway sentence, the chat-with-bookkeeper line
   above the score cards. Sales mini-app reuses this atom as the
   "InsightStrip" per design_handoff_sales_redesign/README.md, the
   three direction modifiers (--up / --down / --neutral) carry the
   tint, the optional __body / __head / __sub slots carry a stacked
   headline + supporting sub-line. Pages that pre-date Sales use the
   plain shape (icon + single text span as a direct child) and stay
   unchanged. */
.ma-takeaway {
  background: var(--ma-surface);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 12px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ma-ink);
  display: flex;
  gap: 10px;
  align-items: flex-start;
  border: 1px solid transparent;
}
.ma-takeaway__icon {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  color: var(--ma-accent);
}
.ma-takeaway__icon svg { width: 18px; height: 18px; }
.ma-takeaway--upgrading { opacity: 0.7; }
/* Zuzo saying something, rather than the page captioning itself. The
   strip is white by default because on most screens it is a summary the
   page wrote; where the sentence is a *finding*, "Chicken is R 1 840
   over its usual month, three slips priced by weight where you normally
   buy by the piece", it is Zuzo talking, and it wears Zuzo's colour so
   the operator can tell the two apart without reading. */
.ma-takeaway--zuzo {
  background: var(--ma-zuzo-soft);
  border-color: var(--ma-zuzo-line);
  color: var(--ma-zuzo-ink);
}
.ma-takeaway--zuzo .ma-takeaway__icon { color: var(--ma-zuzo-ink); }

/* Optional stacked body, headline + faint sub. */
.ma-takeaway__body { min-width: 0; flex: 1; }
.ma-takeaway__head {
  font-weight: 700;
  color: inherit;
  line-height: 1.35;
}
.ma-takeaway__sub {
  margin-top: 2px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.4;
  opacity: 0.78;
}

/* Tinted direction variants, neutral=gold (status), up=forest (wins),
   down=brick (drops). The icon-square scopes the accent colour. */
.ma-takeaway--neutral {
  background: var(--ma-gold-soft);
  border-color: color-mix(in srgb, var(--ma-gold) 33%, transparent);
  color: var(--ma-gold-ink);
}
.ma-takeaway--neutral .ma-takeaway__icon-square { background: color-mix(in srgb, var(--ma-gold) 22%, transparent); color: var(--ma-gold); }
.ma-takeaway--up {
  background: var(--ma-forest-soft);
  border-color: color-mix(in srgb, var(--ma-forest) 33%, transparent);
  color: var(--ma-forest-ink);
}
.ma-takeaway--up .ma-takeaway__icon-square { background: color-mix(in srgb, var(--ma-forest) 22%, transparent); color: var(--ma-forest); }
.ma-takeaway--down {
  background: var(--ma-brick-soft);
  border-color: color-mix(in srgb, var(--ma-brick) 33%, transparent);
  color: var(--ma-brick-ink);
}
.ma-takeaway--down .ma-takeaway__icon-square { background: color-mix(in srgb, var(--ma-brick) 22%, transparent); color: var(--ma-brick); }

/* --warn, the one red strip: days on screen have no Uber Eats in
   them yet, so the numbers under it are one till short. Louder than
   --down (which grades a real drop): full brick border and a filled
   triangle, because this is "do not trust what is below", not "this
   went badly". Rendered by MAFreshness, which also hides the insight
   strip it stands in for (.ma-freshness-yield). */
.ma-takeaway--warn {
  background: var(--ma-brick-soft);
  border-color: var(--ma-brick);
  color: var(--ma-brick-ink);
}
.ma-takeaway--warn .ma-takeaway__icon-square { background: var(--ma-brick-solid); color: #fff; }
.ma-takeaway--warn .ma-takeaway__sub { opacity: 0.86; }
/* Own top margin: the strip is inserted by the component, above whatever
   the page put first, so it cannot borrow that element's spacing. */
.ma-freshness { margin-top: var(--ma-sp-2); }
.ma-freshness__tri svg { width: 17px; height: 17px; }
/* The strip stands in for the insight, the page keeps writing the
   insight's own display, so this has to win. */
.ma-freshness-yield { display: none !important; }

/* --quiet, the third tier (CMD-20): a figure still being added up says
   "Counted up to 14:05" on itself. The stillest thing in the family:
   no fill, no border, no triangle, so it reads as provenance, never a
   warning, and never beside the red strip. */
.ma-takeaway--quiet {
  background: transparent;
  border-color: transparent;
  padding: 0 var(--ma-sp-2);
  color: var(--ma-ink-3);
}
.ma-takeaway--quiet .ma-takeaway__sub { opacity: 1; font-size: 12px; }

/* The same warning as a mark on a channel tile: a big triangle where
   the channel's colour swatch normally sits, so a glance at the Uber
   Eats box says "this number is short" without reading a word. */
.ma-warn-tri {
  color: var(--ma-brick);
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.ma-warn-tri svg { width: 15px; height: 15px; }
.ma-warn-tri--lg svg { width: 22px; height: 22px; }

/* Icon square, 26x26 tinted tile that holds the sparkle. Used by
   the direction variants above; ungraded variants just use the plain
   __icon shape. */
.ma-takeaway__icon-square {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 0;
}
.ma-takeaway__icon-square svg { width: 15px; height: 15px; }

/* Segmented control, the four-segment slicing toggle the Sales "When"
   tab uses to flip Hours / Days / Periods / Monthly. Pill+inset shell;
   active segment fills brick. Per design_handoff_sales_redesign/sales-ia.jsx.
   Reusable wherever a 2–4 mutually exclusive sub-views need a one-tap flip
   without leaving the screen. */
.ma-segmented {
  display: flex;
  gap: 2px;
  padding: 3px;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: 11px;
  margin: 8px 0 12px;
}
.ma-segmented__btn {
  flex: 1;
  padding: 7px 4px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--ma-ink-2);
  font: 700 12px var(--ma-font-body);
  cursor: pointer;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  -webkit-tap-highlight-color: transparent;
}
.ma-segmented__btn:focus { outline: none; }
.ma-segmented__btn--active {
  background: var(--ma-brick-solid);
  color: #fff;
}
.ma-segmented__btn--active svg { color: #fff; }

/* Customer paid / You got, MAMoneyBasis.toggleHtml() draws the shared
   segmented control with this modifier; the note under it names the cut
   that was taken. Per shared/money_basis.py. */
.ma-basis { margin: var(--ma-sp-3) 0 0; }
.ma-basis__note {
  font-size: 11.5px; color: var(--ma-ink-3);
  margin-top: 5px; line-height: 1.4;
}

/* MANewIngredient, the one New-ingredient sheet (mini_app_ui.js).
   Name · Count it in · What it costs you · Ask Zuzo, and the card of
   what Zuzo found. Same shapes as the Recipes sheets it was lifted from. */
.ma-ni-field { margin-bottom: var(--ma-sp-4); }
.ma-ni-label {
  display: block; font: 800 10.5px var(--ma-font-body); letter-spacing: .08em;
  text-transform: uppercase; color: var(--ma-ink-3); margin-bottom: 5px;
}
.ma-ni-label__opt { font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ma-ink-4); }
.ma-ni-input {
  width: 100%; box-sizing: border-box; padding: 10px 12px;
  border: 1px solid var(--ma-border-strong); border-radius: var(--ma-radius-md);
  background: var(--ma-surface); color: var(--ma-ink);
  font: 600 14px var(--ma-font-body);
}
.ma-ni-input--num { font-family: var(--ma-font-num); }
.ma-ni-money { display: flex; align-items: center; gap: 8px; }
.ma-ni-money__r { font-family: var(--ma-font-num); font-size: 16px; font-weight: 700; color: var(--ma-ink-3); }
.ma-ni-units { display: flex; flex-wrap: wrap; gap: 6px; }
.ma-ni-unit {
  min-height: 36px; padding: 0 14px; border-radius: var(--ma-radius-pill);
  border: 1px solid var(--ma-border-strong); background: var(--ma-surface);
  color: var(--ma-ink-2); font: 700 12.5px var(--ma-font-body); cursor: pointer;
}
.ma-ni-unit[aria-pressed="true"] { background: var(--ma-ink); border-color: var(--ma-ink); color: var(--ma-cream); }
.ma-ni-card {
  width: 100%; text-align: left; padding: var(--ma-sp-4);
  background: var(--ma-ink); border: 1px solid var(--ma-ink); color: var(--ma-cream);
  border-radius: var(--ma-radius-xl); cursor: pointer; margin-bottom: var(--ma-sp-2);
}
.ma-ni-card__t { font: 800 14px var(--ma-font-body); }
.ma-ni-card__s { font: 400 11.5px var(--ma-font-body); color: var(--ma-ink-3); margin-top: 2px; }
.ma-ni-found {
  margin-top: 10px; padding: 10px 12px;
  background: var(--ma-gold-soft); border: 1px solid var(--ma-gold);
  border-radius: var(--ma-radius-lg);
}
.ma-ni-found__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.ma-ni-found__k {
  font: 800 10px var(--ma-font-body); letter-spacing: .08em;
  text-transform: uppercase; color: var(--ma-ink-3); min-width: 0;
}
/* The Zuzo mark, how it worked it out. */
.ma-ni-why {
  width: 30px; height: 30px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid var(--ma-zuzo); background: var(--ma-surface); color: var(--ma-zuzo);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.ma-ni-why svg { width: 16px; height: 16px; }
.ma-ni-why:active { transform: scale(var(--ma-press)); }
.ma-ni-why-sec { margin-top: var(--ma-sp-4); }
.ma-ni-why-sec > b {
  display: block; font: 800 10.5px var(--ma-font-body); letter-spacing: .08em;
  text-transform: uppercase; color: var(--ma-ink-3); margin-bottom: 5px;
}
.ma-ni-why-p { font: 400 13px var(--ma-font-body); color: var(--ma-ink-2); line-height: 1.45; }
.ma-ni-chip {
  display: inline-block; padding: 4px 10px; margin: 0 6px 6px 0;
  border-radius: var(--ma-radius-pill); background: var(--ma-cream); border: 1px solid var(--ma-border);
  font: 600 12px var(--ma-font-body); color: var(--ma-ink-2);
}
.ma-ni-why-link {
  display: block; margin: 4px 0; font: 600 12.5px var(--ma-font-body);
  color: var(--ma-ink); text-decoration: underline; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-ni-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; margin-top: 8px; }
.ma-ni-grid__c { font: 400 11.5px var(--ma-font-body); color: var(--ma-ink-3); line-height: 1.35; }
.ma-ni-rules { margin-top: 10px; font: 400 11.5px var(--ma-font-body); color: var(--ma-ink-3); line-height: 1.45; }
.ma-ni-rule { font: 600 12px var(--ma-font-num); color: var(--ma-ink); }
.ma-ni-rule--none { font: 400 11.5px var(--ma-font-body); color: var(--ma-ink-3); }
/* The tray, what Zuzo is still out looking for, on a screen the
   operator is using for something else. Zuzo's purple, like every
   other surface where Zuzo is working; it disappears the moment the
   queue empties rather than sitting there saying "done". */
.ma-tray {
  margin-bottom: 10px; padding: 10px 12px;
  background: var(--ma-zuzo-soft); border: 1px solid var(--ma-zuzo);
  border-radius: var(--ma-radius-lg);
}
.ma-tray__t { font: 600 13px var(--ma-font-body); color: var(--ma-ink); line-height: 1.4; }
.ma-tray__t b { font-weight: 800; }
.ma-tray .ma-thinking { padding: 2px 0 0; }
.ma-tray__s { font: 400 11.5px var(--ma-font-body); color: var(--ma-ink-3); }

/* ── MABatchCard, a set of decisions made once (Set up tabs) ───────── */
.ma-batch {
  margin-top: var(--ma-sp-3);
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  overflow: hidden;
}
.ma-batch__hdr {
  display: flex; align-items: center; gap: var(--ma-sp-2);
  padding: 10px 14px;
  background: var(--ma-ink-card); color: var(--ma-ink-card-fg);
  border-left: 4px solid var(--ma-gold);
  font: 700 12.5px var(--ma-font-body);
}
.ma-batch__hdr svg { width: 14px; height: 14px; flex: 0 0 auto; color: var(--ma-gold); }
.ma-batch__kicker { flex: 0 0 auto; white-space: nowrap; }
.ma-batch__meta { margin-left: auto; font-weight: 600; opacity: .8; text-align: right; }
.ma-batch__body { padding: 12px 14px 14px; }
.ma-batch__counts { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; }
.ma-batch__n { font: 800 26px var(--ma-font-num); font-variant-numeric: tabular-nums; color: var(--ma-ink); line-height: 1; }
.ma-batch__n--sure { color: var(--ma-zuzo-ink); }
.ma-batch__n--need { color: var(--ma-brick-ink); }
.ma-batch__n--saved { color: var(--ma-forest-ink); }
.ma-batch__n--open { color: var(--ma-ink-3); }
.ma-batch__w { font: 800 10.5px var(--ma-font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--ma-ink-3); margin-right: 4px; }
.ma-batch__bar { display: flex; height: 8px; margin: 10px 0 8px; border-radius: 4px; overflow: hidden; background: var(--ma-border); }
.ma-batch__bar span { display: block; height: 100%; transition: width var(--ma-dur-grow) var(--ma-ease); }
.ma-batch__bar--applied { background: var(--ma-forest); }
.ma-batch__bar--sure { background: var(--ma-zuzo); }
.ma-batch__bar--need { background: var(--ma-brick); }
.ma-batch__bar--open { background: var(--ma-ink-3); opacity: .4; }
.ma-batch__note { font: 400 12px/1.45 var(--ma-font-body); color: var(--ma-ink-2); margin-bottom: 10px; }
.ma-batch__btns { display: flex; flex-wrap: wrap; gap: var(--ma-sp-2); }
.ma-batch__rows { margin-top: var(--ma-sp-3); }
.ma-batch__rows-icon { display: inline-flex; width: 14px; height: 14px; margin-right: 6px; vertical-align: -2px; color: var(--ma-ink-3); }
.ma-batch__rows-icon svg { width: 14px; height: 14px; }
.ma-batch__row {
  display: flex; align-items: center; gap: var(--ma-sp-3);
  width: 100%; text-align: left; padding: 10px 0;
  border: 0; border-bottom: 1px solid var(--ma-border); background: none;
  color: var(--ma-ink); font: inherit; cursor: pointer;
}
.ma-batch__row:last-child { border-bottom: 0; }
.ma-batch__icon { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; background: var(--ma-cream); color: var(--ma-ink-3); }
.ma-batch__icon svg { width: 15px; height: 15px; }
.ma-batch__icon--zuzo   { background: var(--ma-zuzo-soft);   color: var(--ma-zuzo-ink); }
.ma-batch__icon--gold   { background: var(--ma-gold-soft);   color: var(--ma-gold-ink); }
.ma-batch__icon--forest { background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-batch__icon--brick  { background: var(--ma-brick-soft);  color: var(--ma-brick-ink); }
.ma-batch__b { flex: 1 1 auto; min-width: 0; }
.ma-batch__t { display: block; font: 700 14px var(--ma-font-body); color: var(--ma-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ma-batch__s { display: block; font: 400 12px var(--ma-font-body); color: var(--ma-ink-3); margin-top: 2px; }
.ma-batch__right { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; max-width: 46%; }
/* `display: block` is load-bearing, not a tidy-up. `.ma-pill` is
   `inline-flex`, and `text-overflow: ellipsis` never applies to a flex
   container's anonymous text child, so this rule was inert and a row
   whose pill carried more than a couple of words ("need you · a reused
   name") simply ran off the card, cut mid-word with no ellipsis. A block
   makes the same rule work, and the pill keeps its padding and ground. */
.ma-batch__right .ma-pill {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The live strip, a lookup as it happens. It wears **Zuzo's purple**,
   not the gold of the finished card: gold is the Rough ladder word,
   which a number only earns once it is the operator's. A working card
   in the finished card's colour is what made this read as done.

   Every line in here is something that actually happened: a shop
   searched, a thing the kitchen already knew. Never a percentage.
   The .ma-thinking line at the top moves on its own clock, so the
   twenty seconds between two shop searches still look alive. */
.ma-ni-live {
  margin-top: 10px; padding: 10px 12px;
  background: var(--ma-zuzo-soft); border: 1px solid var(--ma-zuzo);
  border-radius: var(--ma-radius-lg);
}
.ma-ni-live__head { display: flex; align-items: center; gap: 8px; }
.ma-ni-live__step { font: 800 13px var(--ma-font-body); color: var(--ma-ink); flex: 1 1 auto; min-width: 0; }
.ma-ni-live__t { font: 600 12px var(--ma-font-num); color: var(--ma-ink-3); font-variant-numeric: tabular-nums; }
.ma-ni-live .ma-thinking { padding: 4px 0 0; }
.ma-ni-live__chips { margin-top: 8px; }
/* On purple, the chips take the Zuzo ink so they are not cream-on-lilac. */
.ma-ni-live .ma-ni-chip {
  background: var(--ma-surface); border-color: var(--ma-zuzo);
  color: var(--ma-zuzo-ink);
}
.ma-ni-live__k { margin-top: 4px; font: 400 11.5px var(--ma-font-body); color: var(--ma-ink-3); line-height: 1.45; }
/* What the model has decided so far, darker than the rest of the
   strip, because it is the answer arriving, not commentary. */
.ma-ni-live__got { font: 600 12px var(--ma-font-body); color: var(--ma-ink); }
/* Steering, what the operator has told it mid-run, and the box they
   tell it in. Both live for the length of the run only; once the card
   is theirs there is nothing left to steer. */
.ma-ni-live__said {
  margin-top: 6px; padding: 5px 9px; border-radius: var(--ma-radius-md);
  background: var(--ma-surface); border: 1px solid var(--ma-zuzo);
  font: 600 12px var(--ma-font-body); color: var(--ma-zuzo-ink);
}
.ma-ni-live__say { display: flex; gap: 6px; margin-top: 8px; }
.ma-ni-live__say .ma-ni-input { flex: 1 1 auto; min-width: 0; font-size: 13px; }
.ma-ni-live__send {
  flex: 0 0 auto; min-height: 36px; padding: 0 14px;
  border-radius: var(--ma-radius-md); border: 1px solid var(--ma-zuzo);
  background: var(--ma-zuzo); color: #fff;
  font: 700 13px var(--ma-font-body); cursor: pointer;
}
.ma-ni-live__send:active { transform: scale(var(--ma-press)); }

/* "Use what you have", live from the moment the quick look lands, so a
   deep look that runs long is a choice rather than a wait. */
.ma-ni-live__use {
  margin-top: 10px; width: 100%; min-height: 36px; padding: 8px 12px;
  border-radius: var(--ma-radius-md); border: 1px solid var(--ma-zuzo);
  background: var(--ma-surface); color: var(--ma-zuzo-ink);
  font: 700 13px var(--ma-font-body); cursor: pointer;
}
.ma-ni-live__use:active { transform: scale(var(--ma-press)); }
/* A second look. What moved is marked where it moved, a card rebuilt
   from scratch makes the operator hold the old numbers in their head to
   see what changed. The wash runs once and settles; a field that stayed
   gold would read as a warning rather than as news. */
.ma-ni-again {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: -2px 0 8px; padding: 8px 10px;
  border-radius: var(--ma-radius-md);
  background: var(--ma-surface); border: 1px solid var(--ma-gold);
  font: 400 12px var(--ma-font-body); color: var(--ma-ink-2); line-height: 1.4;
}
.ma-ni-again__t { flex: 1 1 auto; min-width: 0; }
.ma-ni-again b { font-weight: 800; color: var(--ma-ink); }
.ma-ni-undo {
  margin-left: auto; flex: 0 0 auto; min-height: 30px; padding: 0 12px;
  border-radius: var(--ma-radius-md); border: 1px solid var(--ma-border-strong);
  background: var(--ma-surface); color: var(--ma-ink);
  font: 700 12px var(--ma-font-body); cursor: pointer;
}
.ma-ni-undo:active { transform: scale(var(--ma-press)); }
/* "was 190 g", the old number, next to the new one. */
.ma-ni-was {
  display: block; margin-top: 1px;
  font: 600 11px var(--ma-font-num); color: var(--ma-gold-ink);
}
.ma-ni-plate .ma-ni-was, .ma-ni-blend .ma-ni-was { display: inline-block; margin-left: 6px; }
.ma-ni-moved { animation: ma-ni-wash var(--ma-dur-grow, 1.4s) ease-out 1; border-radius: var(--ma-radius-sm); }
@keyframes ma-ni-wash {
  0%   { background: var(--ma-gold); }
  100% { background: transparent; }
}

/* ═════════════════════════════════════════════════════════════
   MALineTell, Tell Zuzo about this line
   ─────────────────────────────────────────────────────────────
   The field and Send to Zuzo, the working strip in Zuzo's colour
   (never the finished gold), "was …" beside the new line, and the
   Put it back button counting down. One control, on a draft line
   and on a saved line.
   ═════════════════════════════════════════════════════════════ */
.ma-tell { display: block; margin: 6px 0 2px; }
.ma-tell__row { display: flex; align-items: center; gap: 6px; }
.ma-tell__in {
  flex: 1 1 auto; min-width: 0; min-height: 40px; padding: 0 12px;
  border-radius: var(--ma-radius-md); border: 1px solid var(--ma-border-strong);
  background: var(--ma-surface); color: var(--ma-ink);
  font: 500 13.5px var(--ma-font-body);
}
.ma-tell__in:focus { outline: none; border-color: var(--ma-zuzo); }
.ma-tell__in:disabled { opacity: .6; }
.ma-tell__mic-slot { display: inline-flex; flex: 0 0 auto; }
.ma-tell__send {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  min-height: 40px; padding: 0 12px; border-radius: var(--ma-radius-pill);
  border: none; background: var(--ma-zuzo); color: #fff;
  font: 800 12.5px var(--ma-font-body); cursor: pointer;
}
.ma-tell__send svg { width: 15px; height: 15px; }
.ma-tell__send:disabled { opacity: .55; cursor: default; }
.ma-tell__work {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 8px; padding: 8px 10px; border-radius: var(--ma-radius-md);
  background: var(--ma-zuzo-soft); color: var(--ma-zuzo-ink);
  font: 700 12px var(--ma-font-body); line-height: 1.4;
}
.ma-tell__work-think { display: inline-flex; }
.ma-tell__said {
  display: flex; align-items: flex-start; gap: 6px; margin-top: 8px;
  font: 600 12.5px var(--ma-font-body); color: var(--ma-ink-2); line-height: 1.4;
}
.ma-tell__said svg { width: 14px; height: 14px; flex: 0 0 auto; color: var(--ma-zuzo); margin-top: 2px; }
/* "was Wellington’s chutney", the old fact, next to the new one. */
.ma-tell__was {
  display: block; margin-top: 1px;
  font: 600 11px var(--ma-font-num); color: var(--ma-gold-ink);
}
.ma-tell__putback {
  display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto;
  min-height: 30px; padding: 0 10px; border-radius: var(--ma-radius-md);
  border: 1px solid var(--ma-border-strong); background: var(--ma-surface); color: var(--ma-ink);
  font: 700 12px var(--ma-font-body); cursor: pointer;
}
.ma-tell__putback svg { width: 14px; height: 14px; }
/* The line that just changed: the same wash the New-ingredient card uses. */
.ma-tell-moved { animation: ma-ni-wash var(--ma-dur-grow, 1.4s) ease-out 1; }
/* Spoken in another language: the chef's own words under the field. */
.ma-tell__heard {
  margin-top: 6px;
  font: 600 12px var(--ma-font-body); color: var(--ma-ink-3); line-height: 1.4;
}
.ma-tell__heard i { font-style: italic; color: var(--ma-ink-2); }

/* ═════════════════════════════════════════════════════════════
   MAVoice, the microphone. Zuzo's colour while it listens, a
   pulse on its own clock (a still button reads as a dead one), the
   square while it can be stopped, dimmed while the words come back.
   ═════════════════════════════════════════════════════════════ */
.ma-voice {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: 0 0 auto; padding: 0;
  border-radius: 50%; border: 1px solid var(--ma-border-strong);
  background: var(--ma-surface); color: var(--ma-ink-2); cursor: pointer;
}
.ma-voice svg { width: 18px; height: 18px; }
.ma-voice.is-live {
  background: var(--ma-zuzo); border-color: var(--ma-zuzo); color: #fff;
  animation: ma-voice-pulse 1.4s ease-out infinite;
}
.ma-voice.is-busy { opacity: .55; cursor: default; }
@keyframes ma-voice-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(139, 123, 232, 0.45); }
  100% { box-shadow: 0 0 0 12px rgba(139, 123, 232, 0); }
}

/* The asks a tap puts into the Tell-Zuzo field; the chips wear the unit-chip look. */
.ma-ni-asks { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
/* The Unfinished row, a draft of the sheet, wherever the sheet can open. */
.ma-ni-draft {
  margin-bottom: var(--ma-sp-2); padding: 10px 12px;
  background: var(--ma-surface); border: 1px solid var(--ma-gold);
  border-radius: var(--ma-radius-lg);
}
.ma-ni-draft__t { display: flex; align-items: center; gap: 8px; font: 800 14px var(--ma-font-body); color: var(--ma-ink); }
.ma-ni-draft__t i { width: 8px; height: 8px; border-radius: 50%; background: var(--ma-gold); flex: 0 0 auto; }
.ma-ni-draft__s { margin-top: 2px; font: 400 12px var(--ma-font-body); color: var(--ma-ink-2); }
.ma-ni-draft__a { display: flex; gap: 6px; margin-top: 8px; }
.ma-ni-draft__b {
  min-height: 32px; padding: 0 14px; border-radius: var(--ma-radius-pill);
  border: 1px solid var(--ma-border-strong); background: transparent; color: var(--ma-ink);
  font: 700 12.5px var(--ma-font-body); cursor: pointer;
}
.ma-ni-draft__b--go { background: var(--ma-brick-solid); border-color: var(--ma-brick-solid); color: #fff; }
/* "One plate gets …", the line that leads the card. */
.ma-ni-plate { margin-top: 8px; font: 600 13px var(--ma-font-body); color: var(--ma-ink); line-height: 1.45; }
.ma-ni-plate b { font-weight: 800; }
.ma-ni-plate__r { font-family: var(--ma-font-num); font-weight: 700; }
/* One card per shop, scrolled sideways; a ticked card is in the blend. */
.ma-ni-shops {
  display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x mandatory;
  margin: 6px -12px 0; padding: 2px 12px 6px; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ma-ni-shops::-webkit-scrollbar { display: none; }
.ma-ni-shop {
  flex: 0 0 74%; min-width: 0; scroll-snap-align: start; padding: 10px 12px;
  background: var(--ma-surface); border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg); cursor: pointer; color: inherit;
}
.ma-ni-shop--off { opacity: .55; }
.ma-ni-shop--odd { border-color: var(--ma-gold); }
.ma-ni-shop__warn { color: var(--ma-gold-ink); font-weight: 600; white-space: normal; }
.ma-ni-shop__t { display: flex; align-items: center; gap: 8px; font: 800 13px var(--ma-font-body); }
.ma-ni-shop__tick {
  width: 20px; height: 20px; border-radius: 6px; flex: 0 0 auto;
  border: 1px solid var(--ma-border-strong); background: var(--ma-ink); color: var(--ma-cream);
  font: 700 12px var(--ma-font-body); display: inline-flex; align-items: center; justify-content: center;
}
.ma-ni-shop--off .ma-ni-shop__tick { background: transparent; }
.ma-ni-shop__p { font: 700 14px var(--ma-font-num); margin-top: 4px; }
.ma-ni-shop__s {
  display: block; font: 400 11.5px var(--ma-font-body); color: var(--ma-ink-3);
  margin-top: 2px; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-ni-shop__link { color: var(--ma-ink-2); text-decoration: underline; }
.ma-ni-shop__only {
  margin-top: 8px; min-height: 30px; padding: 0 12px; border-radius: var(--ma-radius-pill);
  border: 1px solid var(--ma-border-strong); background: transparent; color: var(--ma-ink-2);
  font: 700 11.5px var(--ma-font-body); cursor: pointer;
}
.ma-ni-blend { margin-top: 10px; font: 400 12px var(--ma-font-body); color: var(--ma-ink-2); line-height: 1.45; }
.ma-ni-found__btns { display: flex; gap: var(--ma-sp-2); margin-top: 10px; }
.ma-ni-tile {
  flex: 1 1 0; min-width: 0; padding: 10px 12px; text-align: left;
  background: var(--ma-cream); border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg); cursor: pointer; color: inherit;
  font: 600 13px var(--ma-font-body);
}
.ma-ni-foot {
  margin-top: var(--ma-sp-4); font: 400 11.5px var(--ma-font-body);
  color: var(--ma-ink-3); line-height: 1.5;
}

/* ─────────────────────────────────────────────────────────────
   Page insight strip, MAInsight (mini_app_ui.js) drives these.

   A page-level insight reads on arrival and then costs a whole card of
   screen for the rest of the visit. So it stays in the flow while the
   operator is at the top, and once the shell collapses it tucks away
   behind a Zuzo pill in the chip row, the one row that survives
   scrolling. Tapping the pill docks the strip under that row; tapping
   again tucks it back. Returning to the top always restores the flow
   state, so scroll position and strip state can never disagree.
   ───────────────────────────────────────────────────────────── */

/* Tucked: the strip has scrolled away under the chip row and the pill
   carries it. It stays in the flow, hiding it here reflowed the page
   by its own height (~100 px) the moment the shell collapsed, see the
   Shell collapse rule above. The class is a state marker for the
   pill and for pages; it must never gain a layout property. */
.ma-insight--tucked { }

/* Docked: floating under the chip row. `top` is set inline by
   MAInsight from the bar's measured bottom edge, because the bar sits
   at a different height on a merged page than on a --no-tabs one. */
.ma-insight--docked {
  position: fixed;
  left: var(--ma-sp-5);
  right: var(--ma-sp-5);
  z-index: 825;
  margin: 0;
  max-height: 42vh;
  overflow-y: auto;
  box-shadow: 0 6px 22px rgb(0 0 0 / 14%);
  animation: ma-insight-drop var(--ma-dur-grow, 220ms) var(--ma-ease, ease) both;
}
@keyframes ma-insight-drop {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* A merged bar is already a flex row; a --no-tabs one is not, so the
   pill wraps onto its own line there. Laying the bar out is this class's
   job only when a pill is actually in it. */
.ma-page-filter-bar--with-insight {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-3);
  box-sizing: border-box;
}
.ma-page-filter-bar--with-insight > * { flex: 1 1 auto; min-width: 0; }
.ma-page-filter-bar--with-insight > .ma-insight-pill,
.ma-page-filter-bar--with-insight > .ma-app-header__btn { flex: 0 0 auto; }

/* The pill. Sits in the chip row beside the gear, so a page never gains
   a second floating circle competing with Ask Zuzo. */
.ma-insight-pill {
  width: 32px; height: 32px;
  flex: 0 0 auto;
  position: relative;
  border-radius: var(--ma-radius-md);
  border: none;
  background: var(--ma-gold-soft);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ma-gold) 45%, transparent);
  color: var(--ma-gold-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  overflow: hidden;
  transition: background 0.12s ease,
              opacity var(--ma-dur-swap) var(--ma-ease),
              transform var(--ma-dur-swap) var(--ma-ease),
              width var(--ma-dur-swap) var(--ma-ease),
              margin-left var(--ma-dur-swap) var(--ma-ease);
}
.ma-insight-pill:active { transform: scale(var(--ma-press-sm)); }
.ma-insight-pill svg { width: 18px; height: 18px; flex: 0 0 auto; }
/* Out: the pill fades and shrinks away instead of popping. It stays in
   the row at zero width (the negative margin swallows the row's gap)
   so the chip's width settles over the same 180 ms rather than
   snapping, a change inside the pinned row only; the row's height,
   and so the page, never move. */
.ma-insight-pill--out {
  width: 0;
  margin-left: calc(var(--ma-sp-3) * -1);
  opacity: 0;
  transform: scale(0.6);
  pointer-events: none;
}
/* Open: filled, so the pill reads as the thing holding the panel. */
.ma-insight-pill--on {
  background: var(--ma-gold);
  color: #fff;
  box-shadow: none;
}
/* Unread dot, drops the moment the operator opens it once. */
.ma-insight-pill--unread::after {
  content: '';
  position: absolute;
  top: 4px; right: 4px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ma-brick);
  box-shadow: 0 0 0 2px var(--ma-cream);
}

/* Missing-labels callout, orange-tinted nudge at bottom of summary. */
.ma-callout {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 12px;
  background: rgba(245, 158, 11, 0.10);
  border: 1px solid rgba(245, 158, 11, 0.28);
  color: var(--ma-ink);
  font-size: 13px;
  text-decoration: none;
  margin-bottom: 12px;
}
.ma-callout__icon {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  color: var(--ma-gold-ink);
}
.ma-callout__icon svg { width: 22px; height: 22px; }
.ma-callout__title {
  font-weight: 600;
  display: block;
}
.ma-callout__sub {
  font-size: 12px;
  color: var(--ma-ink-3);
  display: block;
}
/* The exposed card, "R 6 210 exposed · 40 lines unresolved", with the
   way to clear it on the card. Brick, not gold: gold is "wants a word
   sometime", and this is the money that is not in the number the
   operator is reading directly above it. The one callout that carries an
   action rather than a chevron, because the whole card is not a link -
   `design_handoff_expenses_money_out/README.md` § 3.1. */
/* What is not in the number. It is an *outline*, not a fill: the design
   draws it on the page's own surface with a brick edge, because a solid
   brick-tinted block sits directly under the money and reads as a
   warning about the money rather than a note about what is missing from
   it. The edge and the icon carry the alarm; the ground stays calm. */
.ma-callout--exposed {
  background: var(--ma-surface);
  border: 1.5px solid var(--ma-brick);
  align-items: flex-start;
}
.ma-callout--exposed .ma-callout__icon { color: var(--ma-brick); }
/* Money you'll get back. Calm, not alarmed, nothing is wrong here: the
   shop's own money is with somebody else and will come home. So the
   outline is the page's own line rather than the brick edge above, and
   the icon takes the ink colour. It sits beside Money out's number and
   must never read as a second total. */
.ma-callout--owed {
  background: var(--ma-surface);
  border: 1.5px solid var(--ma-border-strong);
  align-items: flex-start;
}
.ma-callout--owed .ma-callout__icon { color: var(--ma-ink-3); }
/* The qualifier under the shop's name, quieter than the sub above it,
   because it is a caveat about the number rather than a fact about the
   money. Its own line: joined to the sub with a middot it reads as one
   long run, and the half that stops an operator adding this figure to
   what they spent is the half that gets skimmed. */
.ma-callout__note { margin-top: 2px; font-size: 11px; opacity: 0.85; }
.ma-callout__act {
  margin-left: auto;
  flex-shrink: 0;
  align-self: center;
}
.ma-callout__chev {
  margin-left: auto;
  flex-shrink: 0;
  color: var(--ma-ink-3);
}
.ma-callout__chev svg { width: 18px; height: 18px; }


/* ============================================================
 * MABottomNav, "More" sheet rows.
 * Modal shell uses the existing .modal-overlay / .modal-sheet
 * pattern; only the row layout is new.
 * ============================================================ */
.ma-more-sheet__row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border-bottom: 1px solid rgba(127, 127, 127, 0.16);
  color: var(--ma-ink);
  text-decoration: none;
  font-size: 15px;
  font-weight: 500;
}
.ma-more-sheet__row:last-child { border-bottom: none; }
.ma-more-sheet__icon {
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ma-accent);
}
.ma-more-sheet__icon svg { width: 22px; height: 22px; }
.ma-more-sheet__label { flex: 1; }
.ma-more-sheet__chev {
  width: 18px;
  height: 18px;
  color: var(--ma-ink-3);
}
.ma-more-sheet__chev svg { width: 18px; height: 18px; }


/* ─────────────────────────────────────────────────────────────
   MAAskZuzo, floating chip + slide-up chat panel.
   ───────────────────────────────────────────────────────────── */

.ma-ask-zuzo-chip {
  position: fixed;
  right: 16px;
  bottom: calc(76px + var(--ma-inset-bottom));   /* clears the bottom nav */
  z-index: 9500;            /* above bottom nav (9000), below modals (9999) */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px 10px 12px;
  background: linear-gradient(135deg, var(--ma-zuzo) 0%, var(--ma-zuzo-ink) 100%);
  color: #fff;
  border: none;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  box-shadow: 0 6px 16px rgba(79, 70, 229, 0.4);
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.ma-ask-zuzo-chip:active {
  transform: scale(var(--ma-press));
  box-shadow: 0 3px 10px rgba(79, 70, 229, 0.35);
}
.ma-ask-zuzo-chip__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
}
.ma-ask-zuzo-chip__icon svg { width: 18px; height: 18px; }
.ma-ask-zuzo-chip__label { white-space: nowrap; }

.ma-ask-zuzo-panel {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  padding-bottom: var(--ma-inset-bottom);   /* full-screen home indicator */
  height: 75vh;
  max-height: 720px;
  background: var(--ma-cream);
  color: var(--ma-ink);
  border-top-left-radius: 18px;
  border-top-right-radius: 18px;
  box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.18);
  z-index: 9700;
  display: flex;
  flex-direction: column;
  transform: translateY(100%);
  transition: transform 0.25s ease;
  pointer-events: none;
}
.ma-ask-zuzo-panel--open {
  transform: translateY(0);
  pointer-events: auto;
}
.ma-ask-zuzo-panel__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid var(--ma-surface);
}
.ma-ask-zuzo-panel__title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 15px;
}
.ma-ask-zuzo-panel__title-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  color: var(--ma-zuzo);
}
.ma-ask-zuzo-panel__title-icon svg { width: 18px; height: 18px; }
.ma-ask-zuzo-panel__actions { display: flex; gap: 4px; }
.ma-ask-zuzo-icon-btn {
  background: transparent;
  border: none;
  color: var(--ma-ink-3);
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-family: inherit;
}
.ma-ask-zuzo-icon-btn:hover {
  background: var(--ma-surface);
}
.ma-ask-zuzo-icon-btn svg { width: 18px; height: 18px; }

.ma-ask-zuzo-panel__messages {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ma-ask-zuzo-empty {
  text-align: center;
  margin: auto;
  padding: 20px;
  color: var(--ma-ink-3);
}
.ma-ask-zuzo-empty__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin: 0 auto 10px;
  border-radius: 50%;
  background: linear-gradient(135deg, rgba(99,102,241,0.12), rgba(79,70,229,0.18));
  color: var(--ma-zuzo-ink);
}
.ma-ask-zuzo-empty__icon svg { width: 28px; height: 28px; }
.ma-ask-zuzo-empty__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--ma-ink);
  margin-bottom: 6px;
}
.ma-ask-zuzo-empty__hint {
  font-size: 13px;
  line-height: 1.5;
  max-width: 320px;
  margin: 0 auto;
}
/* Page-scoped conversation starters, chips below the empty-state
   copy. Tapping a chip fires sendMessage with the chip's text. */
.ma-ask-zuzo-empty__starters {
  margin: 16px auto 0;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: stretch;
}
.ma-ask-zuzo-empty__starters-title {
  font: 700 10.5px var(--ma-font-body);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--ma-zuzo);
  margin-bottom: 2px;
  text-align: left;
}
.ma-ask-zuzo-empty__starter {
  padding: 9px 12px;
  border-radius: 999px;
  background: var(--ma-zuzo-soft);
  color: var(--ma-zuzo-ink);
  border: 1px solid color-mix(in srgb, var(--ma-zuzo) 40%, var(--ma-border));
  font: 700 12.5px var(--ma-font-body);
  cursor: pointer;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s ease, transform 0.08s ease;
}
.ma-ask-zuzo-empty__starter:hover { background: color-mix(in srgb, var(--ma-zuzo) 22%, var(--ma-zuzo-soft)); }
.ma-ask-zuzo-empty__starter:active { transform: scale(var(--ma-press)); }

.ma-ask-zuzo-msg { display: flex; flex-direction: column; }
.ma-ask-zuzo-msg--user { align-items: flex-end; }
.ma-ask-zuzo-msg--assistant { align-items: flex-start; }
.ma-ask-zuzo-msg__bubble {
  max-width: 80%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.55;
  word-wrap: break-word;
  /* Hairline shadow, subtle depth, doesn't fight the outlined look. */
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.02);
}
/* A bubble carrying a surface goes full width.
   .ma-ask-zuzo-msg--assistant is align-items: flex-start and the bubble
   is max-width: 80% with no width of its own, so it shrinks to fit its
   text. A chart's columns are flex: 1 1 0, with no width to share they
   collapse to nothing, and the card reads as broken. The class is set
   by the chat when it mounts the surface; the :has() rule is the same
   decision for a holder appended some other way. Two rules, not one
   list: an engine without :has() would throw the whole list away.
   .ma-ask-zuzo-msg--thinking (below) is the existing precedent. */
.ma-ask-zuzo-msg__bubble--wide { width: 100%; }
.ma-ask-zuzo-msg--assistant:has(.ma-ask-zuzo-msg__surface) .ma-ask-zuzo-msg__bubble {
  width: 100%;
}
.ma-ask-zuzo-msg__surface { margin: var(--ma-sp-1) 0; }
.ma-ask-zuzo-msg__surface .ma-surface__foot { margin-top: var(--ma-sp-3); }

/* User bubble, outlined card tinted with the Zuzo accent (indigo,
   matches the send button gradient at .ma-ask-zuzo-send). Low-alpha
   fill + 1px border keeps it readable in both light and dark Telegram
   themes without falling back to the heavy blue chat-bubble look. */
.ma-ask-zuzo-msg--user .ma-ask-zuzo-msg__bubble {
  background: rgba(99, 102, 241, 0.10);
  color: var(--ma-ink);
  border: 1px solid rgba(99, 102, 241, 0.40);
  border-bottom-right-radius: 4px;
}
/* AI bubble, neutral outlined card, no tint, slightly warmer than
   pure transparent so it visually pairs with the tinted user bubble.
   The rgba neutral works in both light and dark Telegram themes. */
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble {
  background: rgba(127, 127, 127, 0.05);
  color: var(--ma-ink);
  border: 1px solid rgba(127, 127, 127, 0.22);
  border-bottom-left-radius: 4px;
}

/* Unified "process" block, the single home for thinking + tool calls.
   Mirrors the pattern across ChatGPT/Claude/Gemini:
   - Collapsed pill at the top of the assistant message: "Thought · 4 steps".
   - Click expands a tree-line timeline with reasoning interleaved with
     each tool call, in the order they happened.
   - Auto-collapses when streaming completes; user can re-expand. */
.ma-process {
  margin: 0 0 6px;
  font-size: 12.5px;
  max-width: 100%;
}
.ma-process__header {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  /* Process pill needs more presence than the bubbles around it -
     bubbles dropped to ~5–10% tint, so the pill steps up to a
     14% indigo fill + a 60% indigo border. Reads as "this is where
     the AI's reasoning lives" without competing with the answer. */
  background: rgba(99, 102, 241, 0.14);
  border: 1px solid rgba(99, 102, 241, 0.55);
  color: var(--ma-ink);
  cursor: pointer;
  user-select: none;
  list-style: none;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.ma-process__header:hover {
  background: rgba(99, 102, 241, 0.20);
  border-color: rgba(99, 102, 241, 0.70);
}
.ma-process__header::-webkit-details-marker { display: none; }
.ma-process__header::marker { content: ''; }
.ma-process[open] .ma-process__header {
  background: rgba(99, 102, 241, 0.20);
  border-color: rgba(99, 102, 241, 0.65);
  margin-bottom: 6px;
}
.ma-process__star {
  color: var(--ma-brick);
  font-size: 13px;
  line-height: 1;
}
.ma-process__star--active {
  animation: ma-process-star-pulse 2s ease-in-out infinite;
}
@keyframes ma-process-star-pulse {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50%      { opacity: 1;   transform: scale(1.15); }
}
.ma-process__title {
  font-weight: 500;
  letter-spacing: 0.01em;
}
.ma-process__chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 2px;
  opacity: 0.55;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.ma-process__chevron svg {
  width: 14px;
  height: 14px;
  display: block;
}
.ma-process__header:hover .ma-process__chevron { opacity: 0.85; }
.ma-process[open] .ma-process__chevron { transform: rotate(180deg); }
.ma-process__body {
  /* Tree-line on the left, Claude-Code style: a single 1px rail with
     dots on the steps. Thinner + softer than a 2px line. */
  border-left: 1px solid rgba(99, 102, 241, 0.30);
  margin: 0 0 0 12px;
  padding: 4px 0 4px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* While the answer is streaming, cap the body to ~6 lines and pin
   newest steps to the bottom, older content scrolls out of the top
   like a marquee. Top edge fades so the cut-off looks intentional. */
.ma-process--live .ma-process__body {
  max-height: 7.5em;
  overflow: hidden;
  justify-content: flex-end;
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 1.2em,
    #000 100%
  );
          mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 1.2em,
    #000 100%
  );
}
.ma-process__step {
  display: flex;
  align-items: baseline;
  gap: 8px;
  line-height: 1.45;
  color: var(--ma-ink);
}
.ma-process__step__dot {
  flex-shrink: 0;
  width: 12px;
  font-weight: 700;
  text-align: center;
  font-size: 11px;
  line-height: 1.45;
}
.ma-process__step--active .ma-process__step__dot {
  color: var(--ma-brick);
  animation: ma-process-pulse 1.1s ease-in-out infinite;
}
.ma-process__step--active .ma-process__step__dot::before { content: '●'; }
.ma-process__step--done .ma-process__step__dot {
  color: var(--ma-forest);
}
.ma-process__step--done .ma-process__step__dot::before { content: '✓'; }
.ma-process__step--failed .ma-process__step__dot {
  color: var(--ma-brick);
}
.ma-process__step--failed .ma-process__step__dot::before { content: '×'; }
/* Day 7, ask-mode gated tool. Distinct dot (⏸) and dim text so the
   operator sees this row is waiting for them, not running. */
.ma-process__step--gated .ma-process__step__dot {
  background: var(--ma-zuzo, #8B7BE8);
  border-color: var(--ma-zuzo, #8B7BE8);
}
.ma-process__step--gated .ma-process__step__dot::before { content: '⏸'; color: #fff; font-size: 9px; }
.ma-process__step--gated .ma-process__step__label {
  opacity: 0.7; font-style: italic;
}
.ma-process__step--thinking {
  color: var(--ma-ink-3);
  font-style: italic;
}
.ma-process__step--thinking .ma-process__step__dot { visibility: hidden; }
@keyframes ma-process-pulse {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: 0.4; transform: scale(0.85); }
}

/* Verifier warning callout, Pattern D. Renders below the assistant
   bubble when the check comes back unsure about the answer. Non-fatal;
   the answer still stands. The visual is intentionally muted so it
   doesn't override the answer the operator needs to read first, and
   the head carries Zuzo's own spark rather than a warning triangle:
   this is Zuzo saying something about its own answer, not a subsystem
   reporting on it. */
.ma-ask-zuzo-verifier-warning {
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(217, 119, 6, 0.10);
  border: 1px solid rgba(217, 119, 6, 0.35);
  font-size: 12px;
  line-height: 1.4;
  color: var(--ma-ink);
}
.ma-ask-zuzo-verifier-warning__head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  color: var(--ma-gold-ink);
  margin-bottom: 4px;
}
/* Icons are sized on the wrapper, never on the svg's own attributes. */
.ma-ask-zuzo-verifier-warning__head svg {
  width: 14px;
  height: 14px;
  flex: none;
}
.ma-ask-zuzo-verifier-warning ul {
  margin: 4px 0 0 16px;
  padding: 0;
}
.ma-ask-zuzo-verifier-warning li {
  margin: 2px 0;
}

/* Day 8, superseded bubble note. Sits at the bottom of the original
   draft bubble when the verifier triggers a re-loop. Dimmer styling
   than the verifier_warning callout because the operator is about to
   see the corrected version below, and one line only, because the
   faults belong to the draft they are about to stop reading. */
.ma-ask-zuzo-msg__superseded {
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: 8px;
  background: rgba(139, 123, 232, 0.10);
  border: 1px dashed rgba(139, 123, 232, 0.40);
  font-size: 12px;
  line-height: 1.4;
  color: var(--ma-ink);
  opacity: 0.85;
}
.ma-ask-zuzo-msg__superseded-head {
  font-weight: 600;
  color: var(--ma-zuzo-ink, #3D3287);
  margin-bottom: 4px;
}

/* "Simmering..." shimmer line, Claude-Code-style thinking indicator.
   Calmer than bouncing dots, reads as natural language, and the verb
   cycles so the line never feels stuck. */
.ma-ask-zuzo-msg--thinking { width: 100%; }
/* ── MAThinking, "Zuzo is thinking", the one way we say it ────────
   A pulsing star, a verb that shimmers through a gradient, three
   cycling dots. It was Ask Zuzo's, hand-built inside that widget;
   the New-ingredient sheet needed the same thing and a second copy
   would have drifted, so it is an atom now and both use it.

   The point of the shimmer is that it moves whether or not anything
   has happened. A strip that only redraws when an event lands looks
   frozen through the twenty seconds between two shop searches, and
   a frozen strip reads as a finished one. Guarded by
   tests/unit/test_motion_signature.py. */
.ma-thinking {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 8px 4px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--ma-ink-3);
  font-style: italic;
}
.ma-thinking__star {
  color: var(--ma-zuzo);
  font-style: normal;
  font-size: 14px;
  animation: ma-thinking-star 2.4s ease-in-out infinite;
}
@keyframes ma-thinking-star {
  0%, 100% { opacity: 0.55; transform: scale(1); }
  50%      { opacity: 1;    transform: scale(1.15); }
}
.ma-thinking__verb {
  background: linear-gradient(
    90deg,
    var(--ma-ink-3) 0%,
    var(--ma-ink) 50%,
    var(--ma-ink-3) 100%
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
          color: transparent;
  animation: ma-thinking-shimmer 2.4s linear infinite;
}
@keyframes ma-thinking-shimmer {
  0%   { background-position:  100% 0; }
  100% { background-position: -100% 0; }
}
.ma-thinking__dots span {
  display: inline-block;
  opacity: 0;
  animation: ma-thinking-dot 1.4s infinite;
}
.ma-thinking__dots span:nth-child(1) { animation-delay: 0s; }
.ma-thinking__dots span:nth-child(2) { animation-delay: 0.2s; }
.ma-thinking__dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes ma-thinking-dot {
  0%, 100% { opacity: 0; }
  40%      { opacity: 1; }
}

/* (Old .ma-ask-zuzo-progress strip retired, superseded by .ma-process,
   which unifies tool-call timeline with reasoning trace in one
   collapsible component.) */

/* Markdown rendering inside assistant bubbles. Tight spacing, these
   render in a chat bubble, not a document. Tables scroll horizontally
   on narrow phones rather than overflowing the panel. */
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-h {
  margin: 6px 0 4px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-list {
  margin: 4px 0 4px 18px;
  padding: 0;
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-list li {
  margin: 2px 0;
  line-height: 1.4;
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-code {
  background: rgba(0, 0, 0, 0.06);
  border-radius: 4px;
  padding: 1px 5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
}
/* A link in an answer, usually the way into the app. Zuzo's own
   colour, underlined, and it breaks rather than pushing the bubble
   sideways on a long path. */
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-link {
  color: var(--ma-zuzo);
  text-decoration: underline;
  text-underline-offset: 2px;
  word-break: break-word;
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble strong { font-weight: 600; }
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble em { font-style: italic; }
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-table {
  display: block;
  width: 100%;
  margin: 6px 0;
  border-collapse: collapse;
  overflow-x: auto;
  font-size: 12.5px;
  -webkit-overflow-scrolling: touch;
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-table thead {
  background: rgba(0, 0, 0, 0.05);
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-table th,
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-table td {
  padding: 6px 8px;
  text-align: left;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  white-space: nowrap;
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-table th {
  font-weight: 600;
}
.ma-ask-zuzo-msg--assistant .ma-ask-zuzo-msg__bubble .ma-md-table tr:last-child td {
  border-bottom: 0;
}

.ma-ask-zuzo-panel__input {
  display: flex;
  gap: 8px;
  padding: 10px 12px 14px;
  border-top: 1px solid var(--ma-surface);
  background: var(--ma-cream);
}
.ma-ask-zuzo-input {
  flex: 1;
  resize: none;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.4;
  padding: 8px 12px;
  border-radius: 18px;
  border: 1px solid var(--ma-border-strong);
  background: var(--ma-cream);
  color: var(--ma-ink);
  outline: none;
  max-height: 120px;
}
.ma-ask-zuzo-input:focus {
  border-color: var(--ma-zuzo);
}
.ma-ask-zuzo-send {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: none;
  background: linear-gradient(135deg, var(--ma-zuzo) 0%, var(--ma-zuzo-ink) 100%);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  align-self: flex-end;
}
.ma-ask-zuzo-send:active { transform: scale(var(--ma-press-sm)); }
.ma-ask-zuzo-send svg { width: 18px; height: 18px; }


/* ─────────────────────────────────────────────────────────
   MAFieldFixSheet, AI-first tap-to-correct modal
   See lambdas/shared/mini_app_ui.js + docs/PLAN-ai-first-correction-ux.md
   ─────────────────────────────────────────────────────── */
.mafs-overlay { z-index: 9999; }
.mafs-sheet { max-width: 540px; }

.mafs-photo-strip {
  margin-bottom: 14px;
  border-radius: 10px;
  overflow: hidden;
  background: rgba(127,127,127,0.10);
  min-height: 64px;
  display: flex; align-items: center; justify-content: center;
}
.mafs-photo-img { width: 100%; height: auto; display: block; object-fit: cover; }
.mafs-photo-loading { font-size: 12px; color: var(--ma-ink-3); padding: 18px; }

.mafs-label {
  display: block;
  font-size: 12px; font-weight: 600;
  color: var(--ma-ink-3);
  text-transform: uppercase; letter-spacing: 0.05em;
  margin-bottom: 4px;
}
.mafs-input {
  width: 100%;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1.5px solid rgba(127,127,127,0.20);
  background: var(--bg, #fff); color: var(--text, #111);
  font-size: 16px; font-weight: 500;
  margin-top: 4px;
  box-sizing: border-box;
}
.mafs-input:focus { outline: none; border-color: var(--ma-accent); }

.mafs-ai {
  margin-top: 14px;
  padding: 12px 14px;
  border-radius: 10px;
  background: rgba(99,102,241,0.08);
  border-left: 3px solid var(--ma-zuzo);
}
.mafs-ai-loading, .mafs-ai-error { font-size: 13px; color: var(--ma-ink-3); }
.mafs-ai-error { color: var(--ma-gold-ink); }
.mafs-ai-headline { font-size: 14px; line-height: 1.4; }
.mafs-ai-headline strong { color: var(--text, #111); }
.mafs-ai-conf {
  display: inline-block; margin-left: 8px;
  padding: 1px 7px; border-radius: 999px;
  font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.mafs-ai-conf--high   { background: rgba(34,197,94,0.18); color: var(--ma-forest); }
.mafs-ai-conf--medium { background: rgba(250,204,21,0.22); color: var(--ma-gold-ink); }
.mafs-ai-conf--low    { background: rgba(239,68,68,0.18);  color: var(--ma-brick); }
.mafs-ai-reason {
  font-size: 13px; line-height: 1.45;
  color: var(--ma-ink-3);
  margin-top: 6px; font-style: italic;
}
.mafs-ai-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.mafs-chip {
  padding: 8px 14px; border-radius: 999px;
  border: 1.5px solid rgba(127,127,127,0.25);
  background: var(--bg, #fff); color: var(--text, #111);
  font-size: 14px; font-weight: 600;
  cursor: pointer; min-height: 36px; white-space: nowrap;
}
.mafs-chip:active { transform: scale(var(--ma-press)); }
.mafs-chip--primary {
  background: var(--ma-zuzo); color: #fff;
  border-color: var(--ma-zuzo); font-weight: 700;
}
/* Allow chips to wrap their secondary meta line onto a second row */
.mafs-chip { display: inline-flex; flex-direction: column; align-items: center; line-height: 1.2; gap: 2px; }
.mafs-chip-meta {
  font-size: 11px; font-weight: 500; opacity: 0.7;
}
.mafs-chip--primary .mafs-chip-meta { opacity: 0.85; }
/* "New" chips, dashed border + sparkle badge so the operator sees they'll
   create a new registry entry on save, not pick an existing one. */
.mafs-chip--new {
  border-style: dashed;
  border-color: rgba(250,204,21,0.65);
  background: rgba(250,204,21,0.08);
  color: var(--text, #111);
}
.mafs-chip--new.mafs-chip--primary {
  background: rgba(250,204,21,0.18);
  border-color: rgba(217,119,6,0.65);
  color: var(--ma-gold-ink);
}
.mafs-chip-new-badge {
  font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
  padding: 2px 6px; border-radius: 999px;
  background: rgba(250,204,21,0.25); color: var(--ma-gold-ink);
}
.mafs-chip--primary .mafs-chip-new-badge { background: rgba(250,204,21,0.45); }

/* Two ways into the same read: the whole row again, or a box drawn on the
   slip itself (RCP-1). Side by side, one row, wrapping on a narrow phone. */
.mafs-ways { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.mafs-reread, .mafs-point {
  display: block; margin: 12px 0 0;
  padding: 8px 14px; border-radius: 999px;
  border: 1.5px solid rgba(99,102,241,0.40);
  background: transparent; color: var(--ma-zuzo);
  font-size: 13px; font-weight: 600; cursor: pointer;
}
.mafs-point { border-color: var(--ma-forest); color: var(--ma-forest); }
.mafs-reread:disabled, .mafs-point:disabled { opacity: 0.4; cursor: default; }
.mafs-reread:active, .mafs-point:active { transform: scale(var(--ma-press)); }
/* Said once, over the read that came out of it: which crop this is. */
.mafs-pointed {
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--ma-zuzo); margin-bottom: 6px;
}

.mafs-reason {
  margin-top: 14px; padding: 12px 0 0;
  border-top: 1px solid rgba(127,127,127,0.20);
}
.mafs-reason-label {
  font-size: 12px; font-weight: 600;
  color: var(--ma-ink-3); margin-bottom: 8px;
}
.mafs-reason-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.mafs-reason-chip {
  padding: 6px 12px; border-radius: 999px;
  border: 1.5px solid rgba(127,127,127,0.25);
  background: var(--bg, #fff); color: var(--text, #111);
  font-size: 12px; font-weight: 600; cursor: pointer;
}
.mafs-reason-chip.is-selected {
  background: var(--ma-accent); color: #fff;
  border-color: var(--ma-accent);
}

/* ─────────────────────────────────────────────────────────
   MAProposalCard, generic Zuzo proposal bubble
   ───────────────────────────────────────────────────────── */
.ma-prop {
  border-radius: 14px;
  border: 1px solid rgba(127,127,127,0.18);
  background: var(--ma-surface);
  padding: 12px 14px;
  margin: 10px 0;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ma-ink);
}
.ma-prop--applied  { border-color: rgba(34,197,94,0.45); background: rgba(34,197,94,0.06); }
.ma-prop--reverted { opacity: 0.7; }
.ma-prop--failed   { border-color: rgba(239,68,68,0.45); background: rgba(239,68,68,0.06); }
.ma-prop--expired  { opacity: 0.55; }

.ma-prop__hdr {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ma-accent);
  margin-bottom: 8px;
}
.ma-prop__hdr--applied  { color: var(--ma-forest); }
.ma-prop__hdr--reverted { color: var(--ma-ink-3); }
.ma-prop__hdr--failed   { color: var(--ma-brick); }
.ma-prop__hdr--expired  { color: var(--ma-ink-3); }
.ma-prop__hdr-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px;
}
.ma-prop__hdr-icon svg { width: 16px; height: 16px; }

.ma-prop__rb {
  font-size: 15px; font-weight: 600;
  margin-bottom: 8px;
}

.ma-prop__body { margin: 8px 0 10px; font-size: 13px; }
.ma-prop__body--err { color: var(--ma-brick); font-style: italic; }

/* Day 7, inline_tool_call card's tool-input preview. Compact dl
   showing key=value pairs so the operator sees exactly what the
   agent is about to run before approving. */
.ma-prop__tool-input {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 12px;
  font-size: 12px;
}
.ma-prop__tool-input dt {
  font-weight: 600;
  color: var(--ma-ink-3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 11px;
}
.ma-prop__tool-input dd {
  margin: 0;
  color: var(--ma-ink);
  word-break: break-word;
}

.ma-prop__err {
  margin: 6px 0 10px; padding: 8px 10px;
  border-radius: 8px;
  background: rgba(239,68,68,0.10);
  color: var(--ma-brick); font-size: 13px;
}

/* ─── Clarification question ─── */
.ma-prop__q { margin-top: 6px; }
.ma-prop__q-text {
  font-size: 14px; font-weight: 600;
  margin-bottom: 8px;
}
.ma-prop__q-opts {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.ma-prop__q-opt {
  padding: 8px 12px; min-height: 36px;
  border-radius: 999px;
  border: 1.5px solid rgba(127,127,127,0.30);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 13px; font-weight: 600; cursor: pointer;
}
.ma-prop__q-opt:active { transform: scale(var(--ma-press)); }
.ma-prop__q-opt:hover  { border-color: var(--ma-accent); }

.ma-prop__q-form {
  display: flex; gap: 6px; align-items: center;
}
.ma-prop__q-input {
  flex: 1; min-height: 36px; padding: 6px 10px;
  border-radius: 8px;
  border: 1.5px solid rgba(127,127,127,0.30);
  background: var(--ma-cream);
  color: var(--ma-ink);
  font-size: 14px;
}
.ma-prop__q-send {
  padding: 8px 14px; min-height: 36px;
  border: none; border-radius: 8px;
  background: var(--ma-accent); color: #fff;
  font-size: 13px; font-weight: 600; cursor: pointer;
}

/* ─── Actions row ─── */
.ma-prop__actions {
  display: flex; flex-wrap: wrap; gap: 8px;
  align-items: center; justify-content: flex-end;
  margin-top: 8px;
}
.ma-prop__btn {
  padding: 8px 14px; min-height: 36px;
  border-radius: 8px;
  font-size: 13px; font-weight: 600; cursor: pointer;
  border: 1.5px solid transparent;
}
.ma-prop__btn--primary {
  background: var(--ma-accent);
  color: #fff;
}
.ma-prop__btn--primary:active { transform: scale(var(--ma-press)); }
.ma-prop__btn--ghost {
  background: transparent;
  border-color: rgba(127,127,127,0.30);
  color: var(--ma-ink);
}
.ma-prop__win {
  flex: 1; font-size: 12px;
  color: var(--ma-ink-3);
}

/* roster.v1 renderer for MAProposalCard */
.ma-prop-roster { font-size: 13px; }
.ma-prop-roster__totals { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.ma-prop-roster__total {
  font-size: 12px;
  padding: 4px 8px; border-radius: 999px;
  background: rgba(127,127,127,0.10);
  color: var(--ma-ink);
  font-weight: 600;
}
.ma-prop-roster__table { width: 100%; border-collapse: collapse; font-size: 12px; }
.ma-prop-roster__table th {
  text-align: left; padding: 4px 6px;
  font-weight: 700; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ma-ink-3);
  border-bottom: 1px solid rgba(127,127,127,0.15);
}
.ma-prop-roster__table td {
  padding: 6px 6px; vertical-align: top;
  border-bottom: 1px solid rgba(127,127,127,0.08);
}
.ma-prop-roster__row--quiet { color: var(--ma-ink-3); }
.ma-prop-roster__row--added .ma-prop-roster__c-name { font-weight: 600; }
.ma-prop-roster__row--changed .ma-prop-roster__c-name { font-weight: 600; }
.ma-prop-roster__row--conflict .ma-prop-roster__c-day,
.ma-prop-roster__row--conflict .ma-prop-roster__c-shift {
  color: var(--ma-gold-ink);  /* amber for conflict cells */
}
.ma-prop-roster__c-name { font-weight: 600; }
.ma-prop-roster__c-hours { text-align: right; font-variant-numeric: tabular-nums; }
.ma-prop-roster__c-diff { text-align: right; }
.ma-prop-roster__diff {
  display: inline-block;
  padding: 2px 6px; border-radius: 4px;
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em;
}
.ma-prop-roster__diff--added {
  background: rgba(34,197,94,0.15); color: #166534;
}
.ma-prop-roster__diff--changed {
  background: rgba(217,119,6,0.18); color: var(--ma-gold-ink);
}
.ma-prop-roster__row-aux td { border-bottom: 1px solid rgba(127,127,127,0.08); padding-top: 0; padding-bottom: 6px; }
.ma-prop-roster__conflict {
  display: inline-block;
  padding: 4px 8px; border-radius: 6px;
  background: rgba(217,119,6,0.10);
  color: var(--ma-gold-ink); font-size: 11px;
}
.ma-prop-roster__hint { color: var(--ma-ink-3); font-style: italic; }
.ma-prop-roster__warnings { margin: 8px 0 0; padding-left: 18px; font-size: 12px; color: var(--ma-gold-ink); }

/* Proposal before/after (CMD-15). */
.ma-prop__diff { margin-top: 6px; font-size: 12px; color: var(--ma-ink-3); }
.ma-prop__diff-row { display: flex; flex-wrap: wrap; gap: 6px; }
.ma-prop__diff-old { text-decoration: line-through; }
.ma-prop__diff-new { color: var(--ma-ink); font-weight: 600; }
.ma-prop-roster__warnings li { margin: 2px 0; }
.ma-prop-roster__empty { font-style: italic; color: var(--ma-ink-3); }

/* ─────────────────────────────────────────────────────────────
   MAStaffHeader, per-mini-app top header (back · title · LangPill · gear)
   Phase 2 of UI revamp. Mounts above MAStaffTabs in any mini-app
   that lives inside the More sheet (Staff today; Stocks/Offerings
   when they migrate). Theme-driven via the warm-earth tokens.
   ───────────────────────────────────────────────────────────── */
.ma-app-header {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-3);
  padding: var(--ma-sp-4) var(--ma-sp-5) var(--ma-sp-3);
  background: var(--ma-cream);
  border-bottom: 1px solid var(--ma-border);
  /* Flow content, not sticky. The title row leaves the screen by
     scrolling, the way a large title does on iOS, never by
     `display: none` on a threshold, which reflowed the whole page
     (~66 px cut in one frame) and, in Chromium, made scroll anchoring
     throw the operator back to the top. See § Shell collapse below
     and tests/e2e/test_shell_collapse.py. (Its old `position: sticky`
     never engaged anyway: MAHeader renders the row inside its
     placeholder div, which is exactly the row's height.) */
  position: relative;
  z-index: 850;
  box-sizing: border-box;
  min-height: var(--ma-content-inset-top);   /* never shorter than Telegram's control strip */
}
.ma-app-header__btn {
  width: 32px; height: 32px;
  flex: 0 0 auto;
  border-radius: var(--ma-radius-md);
  border: none;
  background: var(--ma-white);
  box-shadow: inset 0 0 0 1px var(--ma-border);
  color: var(--ma-ink-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s ease, transform 0.08s ease;
}
.ma-app-header__btn:active { transform: scale(var(--ma-press-sm)); }
.ma-app-header__btn svg { width: 18px; height: 18px; }
.ma-app-header__title {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--ma-sp-1);
  min-width: 0;
}
.ma-app-header__title-icon {
  flex: 0 0 auto;
  color: var(--ma-brick);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ma-app-header__title-icon svg { width: 18px; height: 18px; }
.ma-app-header__title-text {
  font-size: 17px;
  font-weight: 800;
  color: var(--ma-ink);
  letter-spacing: -0.3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ─────────────────────────────────────────────────────────────
   MAStaffTabs, in-app top tab strip (pills, brick when active)
   Drop <div data-staff-tabs></div> after the header. The component
   reads `data-tabs` (JSON) for custom tabs; default is the staff
   redesign set (Today · Week · Pay · Team).
   ───────────────────────────────────────────────────────────── */
.ma-app-tabs {
  display: flex;
  gap: var(--ma-sp-1);
  align-items: center;
  padding: var(--ma-sp-3) var(--ma-sp-4);
  background: var(--ma-cream);
  border-bottom: 1px solid var(--ma-border);
  /* Flow content, like the title row: the strip scrolls away under
     the bar and comes back the same way. It was never sticky in
     practice, MAStaffTabs renders the nav inside its placeholder,
     which is the nav's own height, but with z-index 840 it painted
     OVER the pinned bar on its way out, which read as the date row
     vanishing. */
  position: relative;
  z-index: 820;
}

/* ─────────────────────────────────────────────────────────────
   Shell collapse, the one rule

   The shell's collapse never changes the document's layout. What
   leaves (title row, tabs, insight strip) is flow content and scrolls
   away. What stays (the filter bar) keeps one height in every state.
   What changes between the two states is opacity, transform, surface
   and shadow, never `display`, `height`, `padding-top/bottom`,
   `margin` or `top`.

   Why: `body[data-shell-collapsed]` used to set `display: none` on
   the tabs, the title row and the insight strip and re-pad the body,
   the bar and the chip. Every one of those reflowed the page, so the
   content under the finger cut upward ~190 px in one frame; and in
   Chromium (Android, Telegram Desktop on Windows) scroll anchoring
   then undid the shift, which dropped the position under the
   threshold, which re-expanded the shell, a slow drag could not get
   past 60 px. tests/e2e/test_shell_collapse.py measures a probe under
   the shell at every scroll position and fails on any movement that
   is not the scroll step; tests/unit/test_motion_signature.py fails
   the build if a layout property appears under the attribute again.
   ───────────────────────────────────────────────────────────── */

/* Filter bar, wrapper around a page's MAFacetSheet chip. The one
   sticky row of the shell, pinned at the inset in every state; the
   title row and the tabs above it are flow content and simply scroll
   under it. */
.ma-page-filter-bar {
  position: sticky;
  top: var(--ma-top-inset);
  z-index: 830;
  background: var(--ma-cream);
  padding: var(--ma-sp-3) var(--ma-sp-5);
  /* The row's box is the SLIM chip's height; the large chip overflows
     below it at rest, into this margin, so the page reads exactly as it
     did, and the box never changes size when the operator scrolls. */
  margin: 0 calc(var(--ma-sp-5) * -1) var(--ma-shell-chip-overflow);
  /* Never the browser's scroll-anchor node: a pinned row that moves
     with the viewport is the wrong thing to keep still. */
  overflow-anchor: none;
  transition: background var(--ma-dur-swap) var(--ma-ease),
              box-shadow var(--ma-dur-swap) var(--ma-ease);
}
/* The hairline under the row. Drawn as a pseudo so it can sit under the
   large chip at rest (translated down by the overflow) and ride up to
   the row's own edge as the chip fades, transform only, no layout. */
.ma-page-filter-bar::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: var(--ma-border);
  pointer-events: none;
  transform: translateY(calc(var(--ma-shell-chip-overflow) * (1 - var(--ma-scroll-p))));
}
body[data-shell-collapsed] .ma-page-filter-bar {
  /* Surface only. Semi-transparent frosted glass so the page content
     scrolling underneath shows through (per user 2026-05-21).
     color-mix() works across all themes without a per-theme RGBA. */
  background: color-mix(in srgb, var(--ma-cream) 72%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: 0 2px 10px rgb(0 0 0 / 6%);
}
body[data-shell-collapsed] .ma-page-filter-bar::after {
  background: color-mix(in srgb, var(--ma-border) 60%, transparent);
}

/* The facet chip pair, MAFacetSheet.mountChip renders a SLIM twin
   (kicker + range, one line) in the flow and the LARGE chip absolute
   over it. Scroll progress cross-fades them: the large one lifts and
   fades out over the first 48 px, the slim one settles in. Only
   opacity and transform move. Where the engine has a CSS scroll
   timeline the @supports block below drives the same keyframes off the
   compositor; elsewhere --ma-scroll-p does. */
.ma-ff-chip-host { position: relative; }
/* Dip to transparent, not a straight cross-fade: the large chip is gone
   by half-way and the slim one only then arrives, so the two never
   ghost over each other (opacity clamps the out-of-range halves). */
.ma-ff-chip-host .ma-ff-chip--large {
  position: absolute;
  top: 0; left: 0; right: 0;
  width: auto;
  z-index: 1;
  opacity: calc(1 - 2 * var(--ma-scroll-p));
  transform: translateY(calc(var(--ma-scroll-p) * -6px));
}
.ma-ff-chip-host .ma-ff-chip--slim {
  padding: var(--ma-sp-2) var(--ma-sp-3);
  opacity: calc(2 * var(--ma-scroll-p) - 1);
  transform: translateY(calc((1 - var(--ma-scroll-p)) * 4px));
}
.ma-ff-chip-host .ma-ff-chip--slim .ma-ff-chip__body {
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: baseline;
  gap: var(--ma-sp-2);
}
.ma-ff-chip-host .ma-ff-chip--slim .ma-ff-chip__kicker,
.ma-ff-chip-host .ma-ff-chip--slim .ma-ff-chip__range { flex: 0 1 auto; min-width: 0; }
/* Whichever chip is faded out must not catch the tap: the large one
   would otherwise sit invisibly over the first rows of content, the
   slim one under the large at rest. */
body[data-shell-collapsed] .ma-ff-chip-host .ma-ff-chip--large { visibility: hidden; pointer-events: none; }
body:not([data-shell-collapsed]) .ma-ff-chip-host .ma-ff-chip--slim { pointer-events: none; }
@supports (animation-timeline: scroll()) {
  .ma-ff-chip-host .ma-ff-chip--large,
  .ma-ff-chip-host .ma-ff-chip--slim,
  .ma-page-filter-bar::after {
    animation-timeline: scroll(root);
    animation-range: 0 48px;
    animation-fill-mode: both;
    animation-timing-function: linear;
  }
  .ma-ff-chip-host .ma-ff-chip--large { animation-name: ma-shell-leave; }
  .ma-ff-chip-host .ma-ff-chip--slim  { animation-name: ma-shell-arrive; }
  .ma-page-filter-bar::after          { animation-name: ma-shell-rule; }
}
/* Discrete swaps (MAMotion.swap → View Transitions where supported):
   the same 180 ms opacity-only signature as a tab or sheet swap. */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--ma-dur-swap);
  animation-timing-function: var(--ma-ease);
}

@keyframes ma-shell-leave  { from { opacity: 1; transform: none; } 50% { opacity: 0; } to { opacity: 0; transform: translateY(-6px); } }
@keyframes ma-shell-arrive { from { opacity: 0; transform: translateY(4px); } 50% { opacity: 0; } to { opacity: 1; transform: none; } }
@keyframes ma-shell-rule   { from { transform: translateY(var(--ma-shell-chip-overflow)); } to { transform: none; } }
/* Pages without a tab strip, same row, same pin. Kept as a class so
   pages keep their markup. */
.ma-page-filter-bar--no-tabs {
  top: var(--ma-top-inset);
}

/* Pinned, the MAFacetSheet chip merges into the filter bar, no inner
   border or chip background; the frosted wrapper is the only visible
   surface. Colour only: the chip keeps its shape so the bar keeps its
   height. */
body[data-shell-collapsed] .ma-ff-chip {
  background: transparent;
  border-color: transparent;
}
/* Swipe-to-dismiss (MASheetDrag): the sheet follows the finger, and the
   backdrop fades with it. `--ma-drag` is set inline while dragging. */
.ma-sheet-dragging { transition: none !important; }
.ma-sheet-settling { transition: transform 0.22s cubic-bezier(0.2, 0, 0, 1) !important; }
/* The grab handle is a real affordance now, give it a target to hit. */
.ma-sheet-handle, .ma-confirm__handle, .pm-handle { cursor: grab; touch-action: none; }

/* ── MAShell, spacers, merged utility row, full-screen clearance ──
   .ma-shell-inset / .ma-shell-inset-bottom are inserted by MAShell as
   the first / last child of <body> so content clears the insets
   without every page knowing about them. */
.ma-shell-inset { height: var(--ma-top-inset); background: var(--ma-cream); flex: 0 0 auto; }
.ma-shell-inset-bottom { height: var(--ma-inset-bottom); flex: 0 0 auto; }
.ma-sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Merged utility row, MAHeader folds the back chevron + gear into the
   filter bar on pages that have BOTH a tab strip and a MAFacetSheet
   chip (top-chrome consolidation, Sept 2026). The title row goes away
   (the active tab + bottom nav say where you are) and the chip
   flattens to one line, so the row is the only chrome between the
   tabs and the content. */
.ma-page-filter-bar--merged {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-3);
  box-sizing: border-box;
  min-height: var(--ma-content-inset-top);   /* never shorter than Telegram's control strip */
}
.ma-page-filter-bar--merged > * { flex: 1 1 auto; min-width: 0; }
.ma-page-filter-bar--merged > .ma-app-header__btn { flex: 0 0 auto; }
body[data-shell-merged] .ma-ff-chip {
  padding: var(--ma-sp-2) var(--ma-sp-3);
  border-radius: var(--ma-radius-lg);
}
/* No calendar tile in the slim chip, the sliders glyph is the
   affordance. Kicker + range sit on one line and the range WRAPS under
   the kicker when the row is narrow (never clips); the facet summary
   takes its own line with an ellipsis. */
body[data-shell-merged] .ma-ff-chip__icon { display: none; }
body[data-shell-merged] .ma-ff-chip__body {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--ma-sp-2);
  row-gap: 1px;
}
body[data-shell-merged] .ma-ff-chip__kicker {
  font-size: 10px; letter-spacing: 0.8px;
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body[data-shell-merged] .ma-ff-chip__range  {
  font-size: 13.5px; margin-top: 0;
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body[data-shell-merged] .ma-ff-chip__sub    { font-size: 10.5px; margin-top: 0; flex: 1 0 100%; }
body[data-shell-merged] .ma-ff-chip__chev svg { width: 16px; height: 16px; }
/* The dual Wages · Tips chip (Staff › Pay) keeps its two stacked rows. */
body[data-shell-merged] .ma-ff-chip--dual .ma-ff-chip__body {
  flex-direction: column; align-items: stretch; gap: 2px;
}

/* Full-screen (Bot API 8.0): Telegram's collapse + menu controls float
   over the top-right corner of OUR page. Every row that shares that
   strip keeps the corner clear. */
html[data-fullscreen] .ma-app-header,
html[data-fullscreen] .ma-page-filter-bar--merged,
html[data-fullscreen] body[data-shell-collapsed] .ma-page-filter-bar {
  padding-left: calc(var(--ma-sp-5) + var(--ma-tg-back-w));
  padding-right: calc(var(--ma-sp-5) + var(--ma-tg-controls-w));
}
/* Telegram's own pill already reads "< Back" (it drives the same
   Telegram.WebApp.BackButton MAHeader wires up), so our in-page chevron
   is duplication in full-screen, and it lands underneath that pill.
   Hide it; MAHeader shows the native button instead. */
html[data-fullscreen] [data-app-header-back] { display: none; }
/* The merged row loses ~190px to the two pills, so drop the facet
   summary line and keep kicker + range + gear. */
html[data-fullscreen] body[data-shell-merged] .ma-ff-chip__sub,
html[data-fullscreen] body[data-shell-merged] .ma-ff-chip__kicker { display: none; }

/* Merged pages read date chip + gear, THEN the tabs, then the content -
   in every mode, so the top of the app looks the same whether or not
   Telegram is in full-screen (MAHeader reorders the DOM; pages still
   write tabs-then-filter-bar). The chip row is the sticky top row and
   the tabs pin under it. In full-screen this also puts the narrow row
   in Telegram's control strip, where the full-width tabs would not
   fit between the two floating pills. */
body[data-shell-merged] .ma-page-filter-bar--merged {
  top: var(--ma-top-inset);
}

.ma-app-tabs__btn {
  flex: 1;
  padding: var(--ma-sp-3) var(--ma-sp-2);
  border-radius: var(--ma-radius-lg);
  background: var(--ma-white);
  border: 1px solid var(--ma-border);
  color: var(--ma-ink-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  font-family: inherit;
  font-weight: 700;
  font-size: 13px;
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s ease, color 0.12s ease;
}
.ma-app-tabs__btn svg { width: 15px; height: 15px; stroke-width: 1.9; }
.ma-app-tabs__btn--active {
  /* White on red, the way every other brick-filled control in the app
     already does it (.ma-btn--primary, .ma-segmented__btn--active, the
     warn takeaway square). The label used to be --ma-surface, which is
     near-white in light mode but CHARCOAL in dark, so the one pill in
     the app that reads red-on-white by day read red-on-black by night.
     Keeping #fff in both themes is what needs --ma-brick-solid: the
     mark-brick is deliberately light in dark mode to clear the dark
     ground, and #fff on it is 3.6:1. The solid one stays deep (5.1:1),
     so the label can be white everywhere with no theme block. */
  background: var(--ma-brick-solid);
  border-color: var(--ma-brick-solid);
  color: #fff;
}
.ma-app-tabs__btn--active svg { stroke-width: 2.2; }

/* Five-up compact strip, the four-tab pill row scaled one step down
   (icon over label, 10.5px) so Today · Plan · Hours · Pay · Team fit
   at 360px with no scroll. Applied automatically by MAStaffTabs when
   a mount has 5+ tabs. */
.ma-app-tabs--five { gap: 4px; }
.ma-app-tabs--five .ma-app-tabs__btn {
  flex-direction: column;
  gap: 2px;
  padding: 6px 2px 5px;
  font-size: 10.5px;
  letter-spacing: 0.1px;
}
.ma-app-tabs--five .ma-app-tabs__btn svg { width: 15px; height: 15px; }

/* Words-only strip, Money out's six. Applied by MAStaffTabs when no
   tab in the set names an icon.

   The icon-over-label box is what broke this strip: "Money out" wrapped
   under its icon while "Today" did not, so the chips came out unequal
   heights and unequal widths and the row overflowed at 360px. Labels
   alone, one line each, equal shares of the row, and if a label ever
   would not fit, the LABEL gets shorter. That rule still holds. What
   follows is how it is kept, after two things quietly broke it.

   `min-height` was being read against a CONTENT box, nothing in this
   file resets `box-sizing`, so a 34px chip came out 56px (34 + 20
   padding + 2 border) and the strip 77px, around a word 10.5px tall.
   Over a third of the strip's height was arithmetic nobody chose.
   `border-box` makes 34px mean 34px.

   And the size steps were keyed on how MANY tabs there are rather than
   on how much room they have, so a sixth chip forced 10.5px, 9.5px
   with the Set up pill, at 390px, where 13px fits with 8px to spare.
   Six words need 294px at 13px and the row offers 302px. A step keyed
   on the count fires on a phone that had the room; keyed on the width,
   it fires when the room actually runs out, which is 360px and below.
   tests/e2e/tabs_screens.py measures every width and fails on a word
   that does not fit its chip, so the numbers above cannot go stale.

   `min-width: min-content` is the floor under all of it: a word is
   never cut, because "a clipped tab is a bug, not a fallback". Where
   even min-content will not fit, a 320px phone, or a seventh tab, the
   strip scrolls rather than shaving another half point off the type.
   That is the last resort and not the layout: a scroll hides a tab the
   operator has not learned yet, so it may only happen where the
   alternative is a cut word or type too small to read. */
.ma-app-tabs--words {
  gap: 4px;
  /* The last resort. Never reached at 340px and up with six words. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* 6px, not 10px: the chip carries its own height honestly now, so the
     row no longer pads around a box that was already too tall. */
  padding-top: var(--ma-sp-1);
  padding-bottom: var(--ma-sp-1);
}
.ma-app-tabs--words::-webkit-scrollbar { display: none; }
.ma-app-tabs--words .ma-app-tabs__btn {
  /* 34px of chip, not 34px of content inside a 56px chip. */
  box-sizing: border-box;
  flex: 1 1 0;
  /* Equal shares while they fit, never narrower than the word itself.
     No `overflow: hidden` here on purpose: with this floor there is
     nothing to hide, and an ellipsis would let the strip go back to
     overflowing quietly. */
  min-width: min-content;
  padding: 0 2px;
  font-size: 13px;
  letter-spacing: 0;
  line-height: 1;
  min-height: 34px;
  white-space: nowrap;
}
.ma-app-tabs--words .ma-app-tabs__btn svg { display: none; }
/* … except the Gcina Set up pill, which is the one chip in a word strip
   that keeps its icon and drops its LABEL: a wrench at the end of six
   words reads as the tool, not as another peer. Icon-only it costs
   ~30px rather than a seventh equal share, which is also what keeps the
   operator's own six off the scroll. The label stays in the DOM for the
   screen reader. */
.ma-app-tabs--words .ma-app-tabs__btn--setup {
  flex: 0 0 auto;
  min-width: 30px;
  padding-left: 7px;
  padding-right: 7px;
}
.ma-app-tabs--words .ma-app-tabs__btn--setup svg { display: block; width: 15px; height: 15px; }
.ma-app-tabs--words .ma-app-tabs__btn--setup > span {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* 360px and under, where six words at 13px stop fitting (274px needed
   at 12px against 272px of row). One step, on the width. */
@media (max-width: 360px) {
  .ma-app-tabs--words .ma-app-tabs__btn { font-size: 11.5px; }
}
/* 340px and under, the smallest phone we see. 10.5px is the last size
   at which all six still fit without a scroll (244px of 252px). */
@media (max-width: 340px) {
  .ma-app-tabs--words .ma-app-tabs__btn { font-size: 10.5px; }
}

/* ─────────────────────────────────────────────────────────────
   MAAskZuzoFAB retheme, replace the old indigo chip with the
   handoff's zuzo purple pill. Same `.ma-ask-zuzo-chip` selector
   so the existing JS component (MAAskZuzo) picks it up, only
   styling changes.
   ───────────────────────────────────────────────────────────── */
.ma-ask-zuzo-chip {
  background: var(--ma-zuzo) !important;
  box-shadow:
    0 10px 24px rgba(139, 123, 232, 0.45),
    0 0 0 4px var(--ma-zuzo-halo) !important;
  font-weight: 700 !important;
  font-size: 13px !important;
  padding: var(--ma-sp-3) var(--ma-sp-5) !important;
}
.ma-ask-zuzo-chip:active {
  box-shadow:
    0 4px 14px rgba(139, 123, 232, 0.4),
    0 0 0 4px rgba(255, 255, 255, 0.55) !important;
}

/* ─────────────────────────────────────────────────────────────
   MABottomNav retheme, brick active, cream surface, ink-3 inactive,
   no shadow. Pure colour overrides; layout untouched.
   ───────────────────────────────────────────────────────────── */
.bottom-nav {
  background: var(--ma-white);
  border-top: 1px solid var(--ma-border);
  /* DO NOT add position here, the base rule above sets
     position: fixed; bottom: 0; and we want to keep that. Adding
     position: relative regressed the nav onto the page flow on
     2026-05-19 (Home Hub roll-out). Slot positioning is scoped to
     .bottom-nav__item below, which is the right granularity. */
}
.bottom-nav__item {
  color: var(--ma-ink-3);
  font-size: 10.5px;
  font-weight: 600;
  position: relative;
  /* Kill the 300ms double-tap-zoom delay on iOS for instant feel. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform 80ms ease-out, background-color 80ms ease-out;
}
.bottom-nav__item:active {
  transform: scale(var(--ma-press-sm));
  background: var(--ma-border);
}
.bottom-nav__item--active {
  color: var(--ma-brick);
  font-weight: 700;
}
.bottom-nav__item--active .bottom-nav__icon svg { stroke-width: 2.1; }

/* ── 28×3 active-slot pill at the top edge (Home Hub spec) ── */
.bottom-nav__accent {
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: 28px; height: 3px;
  background: transparent;
  border-radius: 0 0 4px 4px;
  pointer-events: none;
}
.bottom-nav__item--active .bottom-nav__accent {
  background: var(--ma-brick);
}
.bottom-nav__item--accent-gold.bottom-nav__item--active   { color: var(--ma-gold-ink); }
.bottom-nav__item--accent-gold.bottom-nav__item--active   .bottom-nav__accent { background: var(--ma-gold); }
.bottom-nav__item--accent-forest.bottom-nav__item--active { color: var(--ma-forest-ink); }
.bottom-nav__item--accent-forest.bottom-nav__item--active .bottom-nav__accent { background: var(--ma-forest); }
.bottom-nav__item--accent-brick.bottom-nav__item--active  { color: var(--ma-brick); }
.bottom-nav__item--accent-brick.bottom-nav__item--active  .bottom-nav__accent { background: var(--ma-brick); }

/* ─────────────────────────────────────────────────────────────
   Gcina mode, the second face of the app while a shop is being
   set up (MAGcina in mini_app_ui.js; docs/PLAN-gcina-mode.md).
   Three pieces, all keyed off html[data-gcina] by the shared JS:
   the band above the app header, the Set up pill at the end of
   every tab strip, and the Board · Sales · Expenses · Recipes ·
   Staff bar. The band is flow content that scrolls away with the
   title row, never sticky, and body[data-shell-collapsed] never
   touches it (hard rule 13); the chip row stays the shell's one
   sticky row.
   ───────────────────────────────────────────────────────────── */
.ma-gcina-band {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-3);
  box-sizing: border-box;
  padding: var(--ma-sp-2) var(--ma-sp-5);
  background: var(--ma-ink-card);
  color: var(--ma-ink-card-fg);
  border-left: 4px solid var(--ma-gold);
  cursor: pointer;
  position: relative;
  z-index: 850;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}
.ma-gcina-band__dot {
  flex: 0 0 auto;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--ma-gold);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ma-gold) 28%, transparent);
}
.ma-gcina-band__k {
  flex: 0 0 auto;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ma-gold);
}
.ma-gcina-band__t {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 700;
  /* Never an ellipsis. This is the one element on the screen that tells
     the operator nothing here is live yet, and "Setting up · Ivy's
     Kitc…" identifies no shop. Two lines is the right answer; the row
     is flow content in the shell, so growing it moves nothing that was
     pinned. */
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.3;
}
/* Full-screen: the band is now the first row, so it is the one that
   shares Telegram's control strip, the same reservation the title
   row makes, which the title row no longer needs (it sits under the
   band). The kicker goes the way the merged row's does: the words
   are what has to fit between the two pills. */
html[data-fullscreen] .ma-gcina-band {
  padding-left: calc(var(--ma-sp-5) + var(--ma-tg-back-w));
  padding-right: calc(var(--ma-sp-5) + var(--ma-tg-controls-w));
  min-height: var(--ma-content-inset-top);
}
html[data-fullscreen] .ma-gcina-band__k { display: none; }
html[data-fullscreen][data-gcina] .ma-app-header {
  padding-left: var(--ma-sp-5);
  padding-right: var(--ma-sp-5);
  min-height: 0;
}

/* The Set up pill, dashed gold at rest, solid gold when it is the
   page. Ink on gold in both themes: light ink is dark, and in dark
   mode --ma-cream is the dark one. */
.ma-app-tabs__btn--setup {
  border-style: dashed;
  border-color: var(--ma-gold);
  color: var(--ma-gold-ink);
}
.ma-app-tabs__btn--setup.ma-app-tabs__btn--active {
  background: var(--ma-gold);
  border-color: var(--ma-gold);
  border-style: solid;
  color: var(--ma-ink);
}
[data-theme="dark"] .ma-app-tabs__btn--setup.ma-app-tabs__btn--active { color: var(--ma-cream); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ma-app-tabs__btn--setup.ma-app-tabs__btn--active { color: var(--ma-cream); }
}

/* The Board bar, every active slot lights gold, whatever the app's
   own accent, so the bar reads as one mode. */
.bottom-nav--gcina .bottom-nav__item--active { color: var(--ma-gold-ink); }
.bottom-nav--gcina .bottom-nav__item--active .bottom-nav__accent { background: var(--ma-gold); }

/* ─────────────────────────────────────────────────────────────
   MAZuzoDock, floating "Ask Zuzo" pill anchored bottom-right
   ABOVE the bottom nav. Replaces the legacy floating chip that
   overlapped content arbitrarily; this one's anchored above the
   60px nav and adds just ~52px of safe-area padding (vs the old
   full-width band that ate 44px × 100% width).

   The dock container itself is invisible, only the pill renders.
   Tap → opens MAAskZuzo chat panel.
   ───────────────────────────────────────────────────────────── */
.ma-zuzo-dock {
  position: fixed;
  right: 12px;
  bottom: calc(68px + var(--ma-inset-bottom));   /* 60px nav + 8px breathing room */
  z-index: 9001;
  pointer-events: none;     /* let the page receive taps in the gap */
}
.ma-zuzo-dock__pill {
  pointer-events: auto;
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--ma-zuzo-soft, #E5E1F8);
  color: var(--ma-zuzo-ink, #3D3287);
  border: none;
  padding: 8px 14px;
  border-radius: var(--ma-radius-pill);
  font: 700 13px var(--ma-font-body);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18), 0 2px 4px rgba(0, 0, 0, 0.08);
  transition: transform 80ms ease-out;
}
.ma-zuzo-dock__pill:active { transform: scale(var(--ma-press-sm)); }
.ma-zuzo-dock__icon {
  display: inline-flex; width: 14px; height: 14px;
}
.ma-zuzo-dock__icon svg { width: 14px; height: 14px; stroke-width: 1.9; }

/* When the dock is present, hide the legacy floating chip so they
   don't both render. The chat panel still mounts via MAAskZuzo. */
body:has([data-zuzo-dock]) .ma-ask-zuzo-chip { display: none !important; }

/* Pages with a Zuzo dock need extra bottom padding so the floating
   pill doesn't sit over the last list item. 60px nav + 44px dock
   safe-area = 104px; round up to 120px for a comfortable margin. */
body:has([data-zuzo-dock]) { padding-bottom: 120px; }

/* ─────────────────────────────────────────────────────────────
   MA_APPS shared tiles, used by:
   • MABottomNav More sheet (.ma-app-tile.ma-app-tile--big)
   • Home Hub app rail (.ma-app-tile)
   • Edit Favourites screen
   Accent tint comes from .ma-app-tile__icon--{brick|gold|forest}.
   ───────────────────────────────────────────────────────────── */
.ma-app-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 12px 8px 10px;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: 14px;
  text-decoration: none;
  color: var(--ma-ink);
  font: 800 12px var(--ma-font-body);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  position: relative;
  transition: transform 80ms ease-out, background-color 80ms ease-out;
}
.ma-app-tile:active {
  transform: scale(var(--ma-press));
  background: var(--ma-cream);
}
.ma-app-tile__icon {
  width: 38px; height: 38px; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
}
.ma-app-tile__icon svg { width: 22px; height: 22px; stroke-width: 1.8; }
.ma-app-tile__icon--brick  { background: var(--ma-brick-soft);  color: var(--ma-brick-ink); }
.ma-app-tile__icon--gold   { background: var(--ma-gold-soft);   color: var(--ma-gold-ink); }
.ma-app-tile__icon--forest { background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-app-tile__label { font-size: 12px; font-weight: 800; color: var(--ma-ink); }
.ma-app-tile__blurb { display: none; }                /* hidden in compact tiles */
.ma-app-tile__star {
  position: absolute; top: 8px; right: 8px;
  width: 11px; height: 11px; color: var(--ma-gold);
}

/* ── Big tile variant (More sheet, EditFavourites grid) ── */
.ma-app-tile--big {
  flex-direction: row;
  align-items: flex-start;
  gap: 10px;
  padding: 14px 12px;
  text-align: left;
}
.ma-app-tile--big .ma-app-tile__icon { width: 44px; height: 44px; border-radius: 12px; }
.ma-app-tile--big .ma-app-tile__icon svg { width: 22px; height: 22px; }
.ma-app-tile--big .ma-app-tile__label { font-size: 14px; }
.ma-app-tile--big .ma-app-tile__blurb {
  display: block;
  font-size: 11px; font-weight: 600; color: var(--ma-ink-3);
  margin-top: 2px;
}
.ma-app-tile--big .ma-app-tile__body { flex: 1; min-width: 0; }

/* ── Replacement More sheet (3-col grid) ───────────────── */
.ma-more-overlay { align-items: flex-end; }
.ma-more-sheet {
  background: var(--ma-surface);
  border-radius: 18px 18px 0 0;
  padding: 12px 14px calc(16px + var(--ma-inset-bottom));
  width: 100%;
  max-width: 100%;
  border-top: 1px solid var(--ma-border);
  animation: ma-slide-up 180ms ease-out;
}
.ma-sheet-handle {
  width: 36px; height: 4px;
  background: var(--ma-border-strong);
  border-radius: 999px;
  margin: 0 auto 12px;
}
.ma-more-sheet__header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px;
}
.ma-more-sheet__header h2 {
  margin: 0; font: 800 17px var(--ma-font-body); letter-spacing: -0.3px;
}
.ma-more-sheet__close {
  width: 30px; height: 30px;
  background: transparent; border: none;
  color: var(--ma-ink-2); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.ma-more-sheet__close svg { width: 18px; height: 18px; }
.ma-more-sheet__grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.ma-more-sheet__grid .ma-app-tile {
  flex-direction: column; align-items: flex-start; text-align: left;
  padding: 12px 10px;
}
.ma-more-sheet__grid .ma-app-tile__blurb { display: block; }
.ma-more-sheet__tip {
  margin-top: 12px;
  font: italic 600 11px var(--ma-font-body);
  color: var(--ma-ink-3);
  text-align: center;
}
@keyframes ma-slide-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

/* ─────────────────────────────────────────────────────────────
   Phase 3 atoms, Btn / Pill / Avatar / NumberBig / SectionTitle / ScreenH.
   Pure CSS so any page can drop the markup without JS.
   The deterministic-color cases (Avatar palette, money formatter)
   live as JS helpers in mini_app_ui.js (maAvatarHtml, maFmtR,
   maNumberBigHtml). Dark mode just works, every value below maps
   to a `--ma-*` token.
   ───────────────────────────────────────────────────────────── */

/* ── Btn, 5 variants × 3 sizes ───────────────────────────── */
.ma-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ma-sp-2);
  background: transparent;
  color: var(--ma-ink);
  border: 1.5px solid transparent;
  font-family: var(--ma-font-body);
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform 100ms ease-out, opacity 100ms ease-out;
}
.ma-btn:active { transform: scale(var(--ma-press)); }
.ma-btn:disabled,
.ma-btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
.ma-btn svg { width: 16px; height: 16px; flex: 0 0 auto; stroke-width: 2.2; }

/* sizes */
.ma-btn--sm { padding: 8px 12px;  font-size: 12px; border-radius: 10px; }
.ma-btn--md { padding: 11px 16px; font-size: 14px; border-radius: 12px; }
.ma-btn--lg { padding: 14px 18px; font-size: 16px; border-radius: 14px; }
.ma-btn--lg svg { width: 18px; height: 18px; }

/* variants */
.ma-btn--primary {
  background: var(--ma-brick-solid);
  color: #fff;
  border-color: var(--ma-brick-solid);
}
.ma-btn--gold {
  background: var(--ma-gold);
  color: #3A2A05;
  border-color: var(--ma-gold);
}
.ma-btn--forest {
  background: var(--ma-forest);
  color: #fff;
  border-color: var(--ma-forest);
}
.ma-btn--dark {
  background: var(--ma-ink);
  color: var(--ma-cream);
  border-color: var(--ma-ink);
}
.ma-btn--ghost {
  background: transparent;
  color: var(--ma-ink);
  border-color: var(--ma-border-strong);
}
/* Zuzo's own action, Ask Zuzo, Propose it, Send to Zuzo, in Zuzo's colour,
   so the operator can tell a Zuzo step from a save (brick) at a glance. */
.ma-btn--zuzo {
  background: var(--ma-zuzo);
  color: #fff;
  border-color: var(--ma-zuzo);
}
/* The door into Recipes. An anchor styled as a button needs the two
   things .ma-btn does not give a link: it must not stretch, and it must
   not carry a link's underline. */
.mo-plates__cta {
  display: inline-flex; align-items: center; justify-content: center;
  align-self: flex-start; margin-top: 10px; text-decoration: none;
  min-height: 42px; padding: 0 14px;
}
.mo-plates .meta { line-height: 1.5; }

/* Money out's shop list, the last block in the stack, and the only
   route from the front door to a shop. A name, what it is (slips, when
   you were last there) and what it cost, most money first. */
.mo-shop {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; padding: 11px 0; text-decoration: none; color: inherit;
  border-bottom: 1px solid var(--ma-border);
}
.mo-shop:last-child { border-bottom: 0; }
.mo-shop__l { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mo-shop__name {
  font-size: 13.5px; font-weight: 700; color: var(--ma-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mo-shop__sub { font-size: 11.5px; color: var(--ma-ink-3); }
.mo-shop__amt {
  flex: 0 0 auto; font-family: var(--ma-font-num);
  font-size: 14px; font-weight: 700; color: var(--ma-ink);
}

/* Zuzo, offered rather than urged. A filled slab beside the brick one
   puts two equal commitments on a row and the operator has to read both
   to find out which is the point, so where a Zuzo action sits next to
   the screen's own primary, it is an outline. One primary per row. */
.ma-btn--zuzo-ghost {
  background: var(--ma-zuzo-soft);
  color: var(--ma-zuzo-ink);
  border: 1.5px solid var(--ma-zuzo);
}
/* on-dark ghost, sits inside a dark hero card */
.ma-btn--ghost-on-dark {
  background: transparent;
  color: #fff;
  border-color: rgba(255, 255, 255, 0.25);
}
/* full-width modifier, most CTAs in the new design are full-width */
.ma-btn--block { display: flex; width: 100%; }

/* ── Pill, 5 tones × 2 sizes ────────────────────────────── */
.ma-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border-radius: var(--ma-radius-pill);
  font-family: var(--ma-font-body);
  font-weight: 700;
  letter-spacing: 0.2px;
  background: var(--ma-border);
  color: var(--ma-ink-2);
}
.ma-pill--sm { padding: 3px 8px;  font-size: 11px; }
.ma-pill--lg { padding: 5px 10px; font-size: 12px; }
.ma-pill svg { width: 12px; height: 12px; flex: 0 0 auto; }

.ma-pill--neutral   { background: var(--ma-border);       color: var(--ma-ink-2); }
.ma-pill--brick     { background: var(--ma-brick-soft);   color: var(--ma-brick-ink); }
.ma-pill--gold      { background: var(--ma-gold-soft);    color: var(--ma-gold-ink); }
.ma-pill--forest    { background: var(--ma-forest-soft);  color: var(--ma-forest-ink); }
.ma-pill--ink-solid { background: var(--ma-ink);          color: var(--ma-cream); }
.ma-pill--zuzo      { background: rgba(139, 123, 232, 0.18); color: var(--ma-zuzo-ink); }
.ma-pill--blue      { background: var(--ma-blue-soft);    color: var(--ma-blue-ink); }   /* the holiday tone */

/* Toggle, the app-wide on/off switch. Semantic: a <label class="ma-toggle">
   wrapping a hidden checkbox + a slot span, so it's keyboard/screen-reader
   native and pages wire the input's `change` event:

     <label class="ma-toggle">
       <input type="checkbox" checked>
       <span class="ma-toggle__slot"></span>
     </label>

   Lifted from the page-local toggles on the Zuzo Autonomy + Connections
   pages (which had drifted apart), do NOT hand-roll another switch. */
.ma-toggle { position: relative; display: inline-block; width: 44px; height: 26px; flex: 0 0 auto; }
.ma-toggle input { opacity: 0; width: 0; height: 0; position: absolute; }
.ma-toggle__slot {
  position: absolute; inset: 0;
  background: var(--ma-surface);
  border: 1.5px solid var(--ma-border-strong);
  border-radius: 999px;
  transition: 0.18s; cursor: pointer;
}
.ma-toggle__slot::before {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--ma-ink-4); transition: 0.18s;
}
.ma-toggle input:checked + .ma-toggle__slot { border-color: var(--ma-forest); background: var(--ma-forest-soft); }
.ma-toggle input:checked + .ma-toggle__slot::before { transform: translateX(18px); background: var(--ma-forest); }
.ma-toggle input:disabled + .ma-toggle__slot { opacity: 0.5; cursor: default; }
.ma-toggle input:focus-visible + .ma-toggle__slot { outline: 2px solid var(--ma-forest); outline-offset: 2px; }

/* PayChip, payment method pill rendered on receipt L0 cards.
   Slightly tighter padding than the default --sm so the icon, label
   and last4 read as one tight token. last4 is rendered in the num
   font + dimmed colour, matching the design's "Visa ·· 4421". */
.ma-pill--pay { padding: 3px 7px 3px 6px; gap: 5px; }
.ma-pill--pay .ma-pill__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 12px; height: 12px;
}
.ma-pill--pay .ma-pill__svg { width: 12px; height: 12px; }
.ma-pill--pay .ma-pill__last4 {
  font-family: var(--ma-font-num);
  color: var(--ma-ink-3);
  font-weight: 600;
}

/* Rate-type pill, small icon-first variant used next to a person's
   name on Team / Today / Pay. Inline SVGs (no <svg> width set) so the
   span scopes the size via .ma-pill__icon. */
.ma-pill--rate { padding: 2px 7px 2px 5px; font-size: 10.5px; gap: 3px; }
.ma-pill--rate .ma-pill__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 11px; height: 11px;
}
.ma-pill--rate .ma-pill__icon svg { width: 11px; height: 11px; stroke-width: 2.2; }

/* Rate-type picker, 3 cards side-by-side, selected card filled brick.
   Used in Edit Person (How are they paid). */
.ma-rate-pick {
  display: flex; gap: 6px;
}
.ma-rate-card {
  flex: 1;
  display: flex; flex-direction: column; gap: 4px;
  padding: 10px;
  background: var(--ma-surface);
  color: var(--ma-ink);
  border: 1.5px solid var(--ma-border);
  border-radius: 12px;
  text-align: left;
  cursor: pointer;
  font-family: var(--ma-font-body);
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.ma-rate-card:active { transform: scale(var(--ma-press-lg)); }
.ma-rate-card__icon {
  width: 26px; height: 26px;
  border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ma-brick-soft);
  color: var(--ma-brick-ink);
}
.ma-rate-card__icon svg { width: 14px; height: 14px; stroke-width: 2; }
.ma-rate-card__label {
  font-size: 13px; font-weight: 800; color: var(--ma-ink);
}
.ma-rate-card__sub {
  font-size: 10.5px; font-weight: 700; color: var(--ma-ink-3); line-height: 1.3;
}
.ma-rate-card[aria-pressed="true"] {
  background: var(--ma-brick-solid);
  border-color: var(--ma-brick-solid);
  color: #fff;
}
.ma-rate-card[aria-pressed="true"] .ma-rate-card__icon {
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
}
/* #fff, not --ma-white: the card underneath is brick in both themes, and
   --ma-white is charcoal in dark, the selected rate card read its own
   label in near-black on red at night. */
.ma-rate-card[aria-pressed="true"] .ma-rate-card__label,
.ma-rate-card[aria-pressed="true"] .ma-rate-card__sub {
  color: #fff;
}
.ma-rate-card[aria-pressed="true"] .ma-rate-card__sub { opacity: 0.85; }

/* Pill-button row used for the type-specific extras
   ("6h | 8h | 10h | 12h", "1st | 15th | 25th | Last day"). */
.ma-pill-pickrow {
  display: flex; gap: 6px;
}
.ma-pill-pickbtn {
  flex: 1;
  padding: 10px 4px;
  border-radius: 11px;
  background: var(--ma-surface);
  color: var(--ma-ink-2);
  border: 1px solid var(--ma-border);
  font-family: var(--ma-font-num);
  font-weight: 800;
  font-size: 13px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-pill-pickbtn--text {
  font-family: var(--ma-font-body); font-size: 12px;
}
.ma-pill-pickbtn[aria-pressed="true"] {
  background: var(--ma-brick-solid); color: #fff; border-color: var(--ma-brick-solid);
}

/* ── Apply-from row, "Apply X from: Today / This week / Pick date"
   Used when a dirty edit needs an effective-date anchor.
   Spec: design/design_handoff_edit_person.
*/
.ma-apply-from {
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  padding: 11px 12px 12px;
}
.ma-apply-from__kicker {
  display: inline-flex; align-items: center; gap: 6px;
  font: 800 10.5px var(--ma-font-body);
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
  margin-bottom: 8px;
}
.ma-apply-from__kicker svg { width: 12px; height: 12px; stroke-width: 2.1; }
.ma-apply-from__chips { display: flex; gap: 6px; }
.ma-apply-from__chip {
  flex: 1;
  display: flex; flex-direction: column; align-items: flex-start;
  padding: 8px 9px;
  border-radius: var(--ma-radius-md);
  background: var(--ma-surface);
  border: 1.5px solid var(--ma-border);
  color: var(--ma-ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  text-align: left;
  font-family: var(--ma-font-body);
}
.ma-apply-from__chip:active { transform: scale(var(--ma-press)); }
.ma-apply-from__chip-head { font: 800 12px var(--ma-font-body); }
.ma-apply-from__chip-sub  { font: 700 10px var(--ma-font-body); color: var(--ma-ink-3); margin-top: 2px; }
.ma-apply-from__chip[aria-pressed="true"] {
  background: var(--ma-brick-solid);
  border-color: var(--ma-brick-solid);
  color: #fff;
}
.ma-apply-from__chip[aria-pressed="true"] .ma-apply-from__chip-sub { color: rgba(255,255,255,0.85); }

/* ── Impact panel, gold-tinted "this change affects N records" callout.
   Shows the consequence of a dirty edit (e.g. backdated pay change).
   Spec: design/design_handoff_edit_person.
*/
.ma-impact {
  background: var(--ma-gold-soft);
  border: 1px solid var(--ma-gold);
  border-radius: var(--ma-radius-lg);
  padding: 11px 12px;
}
.ma-impact__title {
  display: inline-flex; align-items: center; gap: 8px;
  font: 800 13px var(--ma-font-body);
  color: var(--ma-gold-ink);
  margin-bottom: 6px;
}
.ma-impact__title svg { width: 15px; height: 15px; stroke-width: 2.2; color: var(--ma-gold-ink); }
.ma-impact__body {
  font: 700 12px var(--ma-font-num);
  color: var(--ma-gold-ink);
  letter-spacing: -0.1px;
}
.ma-impact__body .ma-impact__arrow { color: var(--ma-ink-3); margin: 0 6px; }
.ma-impact__body .ma-impact__delta { margin-left: 8px; font-weight: 500; }
.ma-impact__approved {
  display: flex; align-items: center; gap: 6px;
  margin-top: 8px;
  padding: 7px 10px;
  background: rgba(0,0,0,0.06);
  border-radius: var(--ma-radius-sm);
  font: 700 11.5px var(--ma-font-body);
  color: var(--ma-gold-ink);
}
.ma-impact__approved svg { width: 13px; height: 13px; stroke-width: 2.1; flex: 0 0 auto; }

/* ── Public holiday, blue is the holiday colour, and only that.
   `.ma-holiday` is the banner a week with a public holiday carries, on
   Hours first, then Plan, Pay and Today (docs/PLAN-holiday-pay.md § 9):
   the flag, the day, what it pays, and the week's extra on the right.
   `__act` is its one action, full width under it. `--unset` is the same
   banner when holiday pay is not set up for that day, gold, because it
   asks the owner to do something. `.ma-holiday-amt` is the extra's figure
   wherever it sits in a row, so "+ R 280" reads the same on every screen.
*/
.ma-holiday {
  padding: var(--ma-sp-4);
  background: var(--ma-blue-soft);
  border: 1px solid var(--ma-blue);
  border-radius: var(--ma-radius-lg);
  color: var(--ma-blue-ink);
}
.ma-holiday__row { display: flex; align-items: center; gap: var(--ma-sp-3); }
.ma-holiday__icon {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  border-radius: var(--ma-radius-md);
  background: var(--ma-blue);
  color: var(--ma-blue-soft);
  display: inline-flex; align-items: center; justify-content: center;
}
.ma-holiday__icon svg { width: 18px; height: 18px; }
.ma-holiday__body { flex: 1; min-width: 0; }
.ma-holiday__title { font: 800 14px var(--ma-font-body); line-height: 1.25; }
.ma-holiday__sub { margin-top: 2px; font: 600 12px/1.4 var(--ma-font-body); }
.ma-holiday__fig {
  flex: 0 0 auto; text-align: right;
  font: 700 18px var(--ma-font-num);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-holiday__fig-sub { font: 700 11px var(--ma-font-body); }
.ma-holiday__act {
  margin-top: var(--ma-sp-3);
  width: 100%; min-height: 44px;
  box-sizing: border-box;
  text-decoration: none;          /* it is a link when it goes to Settings */
  display: flex; align-items: center; justify-content: center; gap: 6px;
  border-radius: var(--ma-radius-md);
  border: 1px solid var(--ma-blue);
  background: var(--ma-surface);
  color: var(--ma-blue-ink);
  font: 800 13px var(--ma-font-body);
  cursor: pointer;
}
.ma-holiday__act svg { width: 16px; height: 16px; }
.ma-holiday--unset {
  background: var(--ma-gold-soft);
  border-color: var(--ma-gold);
  color: var(--ma-gold-ink);
}
.ma-holiday--unset .ma-holiday__icon { background: var(--ma-gold); color: var(--ma-gold-ink); }
.ma-holiday--unset .ma-holiday__act { border-color: var(--ma-gold); color: var(--ma-gold-ink); }
.ma-holiday-amt {
  color: var(--ma-blue-ink);
  font-family: var(--ma-font-num); font-weight: 700;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* ── Stacked sub-sheet, modal that opens above another modal.
   z-index 10000 sits above the standard .modal-overlay (9999) so the
   parent stays visible behind the backdrop. Use when one bottom sheet
   drills into a related read-only view (e.g. pay history from inside
   Edit Person). Backdrop dismissal should return to the parent
   modal, not close everything, wire the close handler accordingly.
*/
.modal-overlay--stacked { z-index: 10000; }
.modal-overlay--stacked .modal-sheet {
  max-height: 80vh;
  display: flex; flex-direction: column;
}
.modal-overlay--stacked .modal-body { padding: 14px; overflow-y: auto; }

/* ── Log row, effective-dated history entry (pay log, rate log, etc.)
   Renders inside a stacked sub-sheet. The CURRENT pill marks the
   live entry; .ma-log-edit opens beneath when the pencil is tapped.
*/
.ma-log-row {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 12px;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  margin-bottom: 8px;
}
.ma-log-row--current { border-color: var(--ma-brick); }
.ma-log-row--editing { border-color: var(--ma-brick-soft); }
.ma-log-row__icon {
  width: 32px; height: 32px;
  border-radius: 9px;
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ma-cream);
  color: var(--ma-ink-2);
}
.ma-log-row--current .ma-log-row__icon {
  background: var(--ma-brick-soft);
  color: var(--ma-brick-ink);
}
.ma-log-row__icon svg { width: 17px; height: 17px; }
.ma-log-row__body { flex: 1; min-width: 0; }
.ma-log-row__head {
  display: inline-flex; align-items: center; gap: 6px;
  font: 800 13.5px var(--ma-font-body);
  color: var(--ma-ink);
}
.ma-log-row__current-pill {
  padding: 1px 6px;
  border-radius: var(--ma-radius-pill);
  background: var(--ma-brick-soft);
  color: var(--ma-brick-ink);
  font: 800 9.5px var(--ma-font-body);
  letter-spacing: 0.4px;
}
.ma-log-row__sub {
  margin-top: 2px;
  font: 600 11px var(--ma-font-body);
  color: var(--ma-ink-3);
}
.ma-log-row__edit,
.ma-log-row__delete {
  flex: 0 0 auto;
  width: 28px; height: 28px;
  border-radius: var(--ma-radius-sm);
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  color: var(--ma-ink-2);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.ma-log-row__edit:active,
.ma-log-row__delete:active { transform: scale(var(--ma-press-sm)); }
.ma-log-row__edit svg,
.ma-log-row__delete svg { width: 14px; height: 14px; }
/* Delete reads brick to flag the destructive action, the icon is the
   primary signal, the MAConfirm prompt carries the actual warning. */
.ma-log-row__delete { color: var(--ma-brick); }

/* Inline edit panel beneath a log row, opens when the pencil is tapped. */
.ma-log-edit {
  margin: -4px 0 8px;
  padding: 11px 12px 12px 54px;
  background: var(--ma-cream);
  border: 1px dashed var(--ma-border-strong);
  border-top: none;
  border-radius: 0 0 var(--ma-radius-lg) var(--ma-radius-lg);
  display: flex; flex-direction: column; gap: 8px;
}
.ma-log-edit__label {
  font: 800 10.5px var(--ma-font-body);
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-log-edit__input-row {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap;
}
.ma-log-edit input[type="date"] {
  flex: 1; min-width: 130px;
  padding: 8px 10px;
  border-radius: 9px;
  border: 1px solid var(--ma-border-strong);
  background: var(--ma-surface);
  color: var(--ma-ink);
  font: 700 13px var(--ma-font-num);
  letter-spacing: -0.2px;
  box-sizing: border-box;
}
.ma-log-edit__actions { display: inline-flex; gap: 6px; }
.ma-log-edit__btn {
  padding: 7px 12px;
  border-radius: 9px;
  font: 700 12px var(--ma-font-body);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-log-edit__btn--save  { background: var(--ma-brick-solid); color: #fff; border: none; }
.ma-log-edit__btn--ghost { background: transparent; color: var(--ma-ink-2); border: 1px solid var(--ma-border); }
.ma-log-edit__hint  { font: 600 11px var(--ma-font-body); color: var(--ma-ink-3); }
.ma-log-edit__error { font: 700 11px var(--ma-font-body); color: var(--ma-brick); }

/* ── Avatar, colored circle + monogram ──────────────────── */
.ma-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  border-radius: 50%;
  font-family: var(--ma-font-body);
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  user-select: none;
  background: var(--ma-border);
  color: var(--ma-ink-2);
}
/* default size; pages can override with inline style or a size modifier */
.ma-avatar { width: 36px; height: 36px; font-size: 15px; }
.ma-avatar--sm { width: 30px; height: 30px; font-size: 12.5px; }
.ma-avatar--md { width: 38px; height: 38px; font-size: 16px; }
.ma-avatar--lg { width: 42px; height: 42px; font-size: 18px; }
.ma-avatar--xl { width: 56px; height: 56px; font-size: 24px; }
/* presence indicator, small green dot, bottom-right; wrap in a positioned shell */
.ma-avatar-wrap { position: relative; display: inline-flex; flex: 0 0 auto; }
.ma-avatar-wrap__dot {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--ma-forest);
  box-shadow: 0 0 0 2px var(--ma-surface);
}

/* ─────────────────────────────────────────────────────────────
   The four words, Measured · From the batch · Worked out · Rough

   docs/PLAN-recipes-dishes-production.md §4. Every cost in the
   Recipes and Kitchen apps carries exactly one of these. Never a
   score, never a percentage badge: an operator can act on "Rough"
   and cannot act on "0.62".

   Three atoms, because three surfaces need the same vocabulary and
   two of them had already hand-rolled it:

     .ma-ladder    the word itself, as a pill
     .ma-costbar   one bar split by word, how much of a total is
                   known, guessed, or somewhere between
     .ma-sharebar  one line's share of a total, coloured by its word

   Colours are the semantic tokens, so dark mode comes free:
   forest = Measured, blue = From the batch, ink-3 = Worked out,
   gold = Rough.
   ───────────────────────────────────────────────────────────── */

.ma-ladder {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: var(--ma-radius-pill);
  font: 800 10px var(--ma-font-body); letter-spacing: .05em;
  text-transform: uppercase; white-space: nowrap;
}
.ma-ladder__dot { width: 6px; height: 6px; border-radius: 99px; flex: 0 0 auto; }
.ma-ladder--measured    { background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-ladder--measured .ma-ladder__dot    { background: var(--ma-forest); }
.ma-ladder--from_batch  { background: var(--ma-blue-soft); color: var(--ma-blue-ink); }
.ma-ladder--from_batch .ma-ladder__dot  { background: var(--ma-blue); }
.ma-ladder--worked_out  { background: var(--ma-cream); color: var(--ma-ink-3); }
.ma-ladder--worked_out .ma-ladder__dot  { background: var(--ma-ink-3); }
.ma-ladder--rough       { background: var(--ma-gold-soft); color: var(--ma-gold-ink); }
.ma-ladder--rough .ma-ladder__dot       { background: var(--ma-gold); }
/* Not one of the four, a cost nobody can work out at all. */
.ma-ladder--none        { background: var(--ma-brick-soft); color: var(--ma-brick-ink); }
.ma-ladder--none .ma-ladder__dot        { background: var(--ma-brick); }

.ma-costbar {
  display: flex; height: 8px; border-radius: 99px; overflow: hidden;
  background: var(--ma-border); border: 1px solid var(--ma-border);
}
.ma-costbar__seg { height: 100%; transition: width var(--ma-dur-grow) var(--ma-ease); }
.ma-costbar__seg--measured   { background: var(--ma-forest); }
.ma-costbar__seg--from_batch { background: var(--ma-blue); }
.ma-costbar__seg--worked_out { background: var(--ma-ink-3); }
.ma-costbar__seg--rough      { background: var(--ma-gold); }

.ma-costbar-keys { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 8px; }
.ma-costbar-key {
  display: inline-flex; align-items: center; gap: 5px;
  font: 700 11px var(--ma-font-body); color: var(--ma-ink-3);
}
.ma-costbar-key__dot { width: 8px; height: 8px; border-radius: 99px; flex: 0 0 auto; }
.ma-costbar-key b { font-family: var(--ma-font-num); color: var(--ma-ink-2); }

/* One line's share of the total. Beef being three quarters of a pot
   should be seen, not computed. */
.ma-sharebar {
  height: 6px; border-radius: 99px;
  background: var(--ma-border); overflow: hidden;
}
.ma-sharebar__fill {
  display: block; height: 100%; border-radius: 99px;
  transition: width var(--ma-dur-grow) var(--ma-ease);
}
.ma-sharebar__fill--measured   { background: var(--ma-forest); }
.ma-sharebar__fill--from_batch { background: var(--ma-blue); }
.ma-sharebar__fill--worked_out { background: var(--ma-ink-3); }
.ma-sharebar__fill--rough      { background: var(--ma-gold); }
/* Food-cost bands, for the share of a dish that is food. */
.ma-sharebar__fill--good { background: var(--ma-forest); }
.ma-sharebar__fill--warn { background: var(--ma-gold); }
.ma-sharebar__fill--bad  { background: var(--ma-brick); }

/* ── Tape, the two characters a cook writes on the pot ───────────
   Real monospace, not the numeric display face: a tape code is read
   at arm's length across a hot kitchen and must not be mistaken for
   a word. Tabular by construction, so 11 and 1 line up. */
.ma-tape {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; height: 44px; padding: 0 var(--ma-sp-2);
  border-radius: var(--ma-radius-lg);
  background: var(--ma-ink); color: var(--ma-cream);
  font-family: var(--ma-font-mono); font-size: 22px; font-weight: 700;
  letter-spacing: .02em; line-height: 1;
}
.ma-tape--lg { min-width: 56px; height: 56px; font-size: 30px; }
.ma-tape--chip {
  min-width: 30px; height: 26px; font-size: 15px;
  background: var(--ma-cream); color: var(--ma-ink);
  border: 1px solid var(--ma-border-strong);
}

/* ── NumberBig, Space Grotesk big number + UPPERCASE label ── */
.ma-num-big { display: inline-block; }
.ma-num-big__value {
  font-family: var(--ma-font-num);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.6px;
  color: var(--ma-ink);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-num-big__label {
  margin-top: var(--ma-sp-1);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
/* size tokens, mirrors handoff §Typography */
.ma-num-big--lg .ma-num-big__value { font-size: 28px; font-family: var(--ma-font-display); }  /* hero stats */
.ma-num-big--md .ma-num-big__value { font-size: 24px; }  /* Week "You owe" */
.ma-num-big--sm .ma-num-big__value { font-size: 20px; }  /* mini stat cards */
.ma-num-big--display .ma-num-big__value { font-size: 38px; letter-spacing: -1px; font-family: var(--ma-font-display); }
/* A page's own hero figure, sized at 38px locally (Money out's summary),
   reads the display token too (FE-13): below 28px it never would. */
.ma-num-big__value.mo-hero__value { font-family: var(--ma-font-display); }
/* accent variants, change colour without changing size */
.ma-num-big--gold     .ma-num-big__value { color: var(--ma-gold); }
.ma-num-big--gold-ink .ma-num-big__value { color: var(--ma-gold-ink); }
.ma-num-big--forest   .ma-num-big__value { color: var(--ma-forest); }
.ma-num-big--brick    .ma-num-big__value { color: var(--ma-brick); }
.ma-num-big--cream    .ma-num-big__value { color: var(--ma-cream); }

/* ── SectionTitle, eyebrow uppercase title + optional colour dot ── */
.ma-section-title {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-2);
  margin: var(--ma-sp-5) 0 var(--ma-sp-3);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-section-title__dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.ma-section-title--brick  .ma-section-title__dot { background: var(--ma-brick); }
.ma-section-title--gold   .ma-section-title__dot { background: var(--ma-gold); }
.ma-section-title--forest .ma-section-title__dot { background: var(--ma-forest); }
.ma-section-title--zuzo   .ma-section-title__dot { background: var(--ma-zuzo); }

/* ── ScreenH, in-screen header (kicker + title + right-slot) ── */
.ma-screen-h {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ma-sp-3);
  margin-bottom: var(--ma-sp-5);
}
.ma-screen-h__kicker {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
  margin: 0 0 4px;
}
.ma-screen-h__title {
  margin: 0;
  font-size: 26px;
  line-height: 1.05;
  font-weight: 800;
  letter-spacing: -0.6px;
  color: var(--ma-ink);
}
.ma-screen-h__right { flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--ma-sp-2); }

/* ── Card surfaces ───────────────────────────────────────── */
.ma-card {
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg);
  padding: var(--ma-sp-4);
}
.ma-card--tight { padding: var(--ma-sp-3); }
.ma-card--loose { padding: var(--ma-sp-5); }
.ma-card--hero  { padding: var(--ma-sp-6); border-radius: var(--ma-radius-2xl); }
/* dark hero, the "Today so far" / "You owe right now" pattern */
.ma-card--dark {
  background: var(--ma-ink-card);
  color: var(--ma-ink-card-fg);
  border: 1px solid var(--ma-ink-card);
  border-left: 4px solid var(--ma-brick);
}
/* A skeleton on a dark card. The light token disappears into a dark
   surface as a pale slab, which is why Marketing carried the same
   rgba(255,255,255,.18) override hand-written three times. Once, here,
   so every dark hero gets it, including the ones that had no idea they
   needed it. */
.ma-card--dark .ma-skel { background: var(--ma-ink-card-line); border-color: transparent; }

.ma-card--dark .ma-num-big__value { color: var(--ma-ink-card-fg); }
.ma-card--dark .ma-num-big__label { color: var(--ma-ink-card-fg); opacity: .7; }
/* The brick rail is what the dark-mode handoff added when solid #1F2D2A
   merged into the charcoal ground (§"Hero 'dark card' doesn't work in
   dark mode"). It kept the rail and gave up the fill, swapping to
   --ma-elevated, which put the hero one step ABOVE the page in dark
   and one step below it in light: the same card, two mechanics, and in
   dark it stopped reading as a hero at all. The rail stays; the fill is
   now --ma-ink-card, which goes darker in dark instead of lighter, so
   the hero is the same idea in both themes. */

/* ─────────────────────────────────────────────────────────────
   MAConfirm, app-wide standard confirm/alert bottom-sheet.
   Per design/design_handoff_confirm_dialog/README.md. Replaces every
   native window.confirm(), that one paints the API hostname as a
   title and is completely off-brand. The look matches the rest of
   our bottom-sheet modals (Record-payment, Adjust-day, Appearance):
   drag handle → tinted icon circle → centered title → optional
   info card → centered body → stacked primary + cancel buttons.

   Intents, `destructive` / `positive` / `info`, differ only in
   the icon-circle tint. The primary CTA stays brick filled for all
   three; destructiveness is communicated via icon + verb-led title
   + button copy ("Yes, undo payment"), not button colour.
   ───────────────────────────────────────────────────────────── */
.ma-confirm { z-index: 10000; }   /* sits above other modals */
.ma-confirm .modal-sheet {
  background: var(--ma-surface);
  color: var(--ma-ink);
  padding: 10px 18px calc(18px + var(--ma-inset-bottom));
}
.ma-confirm__handle {
  width: 38px; height: 4px;
  border-radius: 999px;
  background: var(--ma-border-strong);
  margin: 0 auto var(--ma-sp-5);
}
.ma-confirm__icon-wrap {
  display: flex; justify-content: center;
  margin-bottom: var(--ma-sp-4);
}
.ma-confirm__icon {
  width: 56px; height: 56px;
  border-radius: 16px;
  display: inline-flex; align-items: center; justify-content: center;
  /* Subtle scale-in for that "intentional, not stamped" feel
     called out in the handoff §Animations. */
  animation: ma-confirm-icon-in 200ms ease-out 0s 1 both;
}
.ma-confirm__icon svg { width: 26px; height: 26px; }
@keyframes ma-confirm-icon-in {
  from { transform: scale(0.9); opacity: 0.4; }
  to   { transform: scale(1);   opacity: 1; }
}
.ma-confirm__icon--destructive {
  background: var(--ma-brick-soft);
  color: var(--ma-brick-ink);
}
.ma-confirm__icon--positive {
  background: var(--ma-forest-soft);
  color: var(--ma-forest-ink);
}
.ma-confirm__icon--info {
  background: var(--ma-gold-soft);
  color: var(--ma-gold-ink);
}
.ma-confirm__title {
  margin: 0 0 var(--ma-sp-4);
  font: 800 20px var(--ma-font-body);
  letter-spacing: -0.4px;
  color: var(--ma-ink);
  text-align: center;
  line-height: 1.2;
}
.ma-confirm__title--with-sub { margin-bottom: 4px; }
.ma-confirm__subtitle {
  font: 600 13px var(--ma-font-body);
  color: var(--ma-ink-2);
  text-align: center;
  margin: 0 0 var(--ma-sp-4);
  line-height: 1.4;
}
.ma-confirm__info {
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  border-radius: 12px;
  padding: 12px;
  margin-bottom: var(--ma-sp-4);
}
.ma-confirm__body {
  font: 600 13px var(--ma-font-body);
  color: var(--ma-ink-2);
  text-align: center;
  line-height: 1.5;
  margin: 0 4px var(--ma-sp-5);
}
.ma-confirm__body b,
.ma-confirm__body strong {
  color: var(--ma-ink);
  font-family: var(--ma-font-num);
  font-weight: 800;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-confirm__buttons {
  display: flex; flex-direction: column;
  gap: var(--ma-sp-2);
}
.ma-confirm__btn {
  padding: 14px 16px;
  border-radius: 14px;
  font-family: var(--ma-font-body);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  border: none;
  -webkit-tap-highlight-color: transparent;
}
.ma-confirm__btn:active { transform: scale(var(--ma-press)); }
.ma-confirm__btn--primary {
  background: var(--ma-brick-solid);
  color: #fff;
  font-weight: 800;
  font-size: 15px;
}
.ma-confirm__btn--primary svg { width: 17px; height: 17px; stroke-width: 2.4; }
.ma-confirm__btn--cancel {
  background: transparent;
  color: var(--ma-ink);
  border: 1.5px solid var(--ma-border-strong);
  font-weight: 700;
  font-size: 14px;
  padding: 13px 16px;
}

/* ─────────────────────────────────────────────────────────────
   MAFacetSheet, app-wide filter standard.
   Per design/design_handoff_facet_filter/README.md. Bottom-sheet
   model (not an inline-collapsible) so the underlying screen stays
   uncluttered. The summary chip in the toolbar is the headline
   (resolved date range, big), preset name secondary; the SHOWING
   banner at the top of the sheet is brick-filled so the user
   always knows what they're looking at; every date-preset chip has
   a resolved-range subline so options can be compared side-by-side
   before commit. Edits stage until Apply.

   Selectors prefixed .ma-ff-* (facet filter). Existing
   .ma-facet-filter (inline collapsible) is unchanged, pages
   migrate to MAFacetSheet over time. Both coexist.
   ───────────────────────────────────────────────────────────── */

/* Summary chip, full-width tappable card that opens MAFacetSheet.
   Three-line layout per design_handoff_sales_redesign:
     KICKER (uppercase preset label, e.g. "THIS MONTH · SO FAR")
     Range  (Space Grotesk headline, e.g. "May 1–20, 2026")
     Facets (small grey summary of non-date facets, optional) */
.ma-ff-chip {
  width: 100%;
  display: inline-flex;
  align-items: center;
  gap: var(--ma-sp-3);
  padding: var(--ma-sp-4) var(--ma-sp-5);
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-xl);
  color: var(--ma-ink);
  cursor: pointer;
  font-family: var(--ma-font-body);
  min-width: 0;
  max-width: 100%;
  text-align: left;
}
.ma-ff-chip:active { transform: scale(var(--ma-press-lg)); }
.ma-ff-chip__icon {
  width: 38px; height: 38px;
  border-radius: var(--ma-radius-md);
  background: var(--ma-brick-soft);
  color: var(--ma-brick);
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.ma-ff-chip__icon svg { width: 18px; height: 18px; stroke-width: 2; }
.ma-ff-chip__body {
  flex: 1; min-width: 0;
  text-align: left;
  /* Stack the kicker above the range per the chip design, two
     lines, kicker as eyebrow + range as headline. Without this
     they render as touching inline spans (no whitespace between
     them in the source HTML). */
  display: flex;
  flex-direction: column;
}
.ma-ff-chip__kicker {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ma-ff-chip__range {
  font-family: var(--ma-font-num);
  font-size: 15.5px;
  font-weight: 800;
  color: var(--ma-ink);
  letter-spacing: -0.3px;
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-ff-chip__sub {
  font-size: 11px;
  font-weight: 600;
  color: var(--ma-ink-3);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ma-ff-chip__chev {
  color: var(--ma-ink-3);
  flex: 0 0 auto;
}
.ma-ff-chip__chev svg { width: 18px; height: 18px; stroke-width: 2; }

/* Ultra-collapsed pill, opt-in via MAFacetSheet.mountChip({compact:true}).
   Slim label-only chevron pill for corner-of-page slots (Home / Today,
   top-right toolbars). Visual matches the legacy .hub-greet__chip so
   pages migrating to MAFacetSheet keep their layout. */
.ma-ff-chip--compact {
  width: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px 4px 10px;
  border: 1px solid var(--ma-border);
  background: var(--ma-surface);
  border-radius: 999px;
  font: 700 11px var(--ma-font-body);
  color: var(--ma-ink-3);
  cursor: pointer;
  white-space: nowrap;
}
.ma-ff-chip--compact .ma-ff-chip__compact-label {
  text-transform: capitalize;
  letter-spacing: 0.2px;
}
.ma-ff-chip--compact .ma-ff-chip__compact-chev {
  display: inline-flex;
  align-items: center;
  color: var(--ma-ink-3);
}
.ma-ff-chip--compact .ma-ff-chip__compact-chev svg {
  width: 11px;
  height: 11px;
  stroke-width: 2.4;
}

/* Dual-window chip, MAFacetSheet.mountDualChip. Two stacked window
   rows (WAGES locked Mon–Sun · TIPS the open tip cycle) so a one-week
   wage figure and a fortnight tips figure can never be mistaken for
   the same period. Per design/handover_tips_recon HANDOVER §Task 2. */
.ma-ff-chip--dual .ma-ff-chip__body { gap: 2px; }
.ma-dwc-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.ma-dwc-row__kicker {
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
  flex: 0 0 auto;
  min-width: 46px;
}
.ma-dwc-row__kicker::before {
  content: '';
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 999px;
  background: var(--ma-ink-3);
  margin-right: 5px;
  vertical-align: middle;
}
.ma-dwc-row--tips .ma-dwc-row__kicker { color: var(--ma-gold-ink); }
.ma-dwc-row--tips .ma-dwc-row__kicker::before { background: var(--ma-gold); }
.ma-dwc-row__range {
  font-family: var(--ma-font-num);
  font-size: 13px;
  font-weight: 800;
  color: var(--ma-ink);
  letter-spacing: -0.2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-dwc-row__len {
  font-size: 10px;
  font-weight: 600;
  color: var(--ma-ink-3);
  white-space: nowrap;
  flex: 0 0 auto;
}

/* Dual-window sheet pieces (wage-week stepper + tip-cycle day strip). */
.ma-dws-weekrow {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-2);
}
.ma-dws-weekbtn {
  width: 36px; height: 36px;
  border-radius: 999px;
  border: 1px solid var(--ma-border);
  background: var(--ma-white);
  color: var(--ma-ink-2);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.ma-dws-weekbtn:active { transform: scale(var(--ma-press-sm)); }
.ma-dws-weekbtn svg { width: 15px; height: 15px; }
.ma-dws-weeklabel {
  flex: 1;
  text-align: center;
  min-width: 0;
}
.ma-dws-weeklabel__range {
  font-family: var(--ma-font-num);
  font-size: 15px;
  font-weight: 800;
  color: var(--ma-ink);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-dws-weeklabel__sub {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--ma-ink-3);
  margin-top: 1px;
}
/* Tips range, one MARangeCal inline plus the two boundary labels.
   Replaces the old 28-day strip + First/Last day tabs (2026-10-06):
   one picker, both ends, the period read back in words. */
.ma-tips-range__labels {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-bottom: 10px;
}
.ma-tips-range__label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border-radius: var(--ma-radius-md);
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  min-width: 0;
}
.ma-tips-range__label--end {
  border-color: var(--ma-gold-ink);
  background: var(--ma-gold-soft, rgba(212, 168, 68, 0.14));
}
.ma-tips-range__k {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-tips-range__label--end .ma-tips-range__k { color: var(--ma-gold-ink); }
.ma-tips-range__v {
  font-family: var(--ma-font-num);
  font-size: 14px;
  font-weight: 800;
  color: var(--ma-ink);
  letter-spacing: -0.2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-tips-range__cal {
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-md);
  background: var(--ma-white);
  padding: 10px;
  margin-top: 0;
}

.ma-dws-strip {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: 4px 2px 6px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ma-dws-strip::-webkit-scrollbar { display: none; }
.ma-dws-day {
  flex: 0 0 auto;
  width: 46px;
  padding: 7px 0 6px;
  border-radius: 12px;
  border: 1px solid var(--ma-border);
  background: var(--ma-white);
  color: var(--ma-ink);
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  font-family: var(--ma-font-body);
}
.ma-dws-day:active { transform: scale(var(--ma-press)); }
.ma-dws-day__dow {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-dws-day__num {
  font-family: var(--ma-font-num);
  font-size: 15px;
  font-weight: 800;
  line-height: 1.1;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-dws-day__mon {
  font-size: 8.5px;
  font-weight: 700;
  color: var(--ma-ink-3);
}
.ma-dws-day--sunday { border-color: var(--ma-gold-ink); }
.ma-dws-day[aria-pressed="true"] {
  background: var(--ma-brick-solid);
  border-color: var(--ma-brick-solid);
  color: #fff;
}
.ma-dws-day[aria-pressed="true"] .ma-dws-day__dow,
.ma-dws-day[aria-pressed="true"] .ma-dws-day__mon { color: rgba(255,255,255,0.75); }
.ma-dws-warn {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin-top: var(--ma-sp-2);
  padding: 9px 11px;
  border-radius: var(--ma-radius-md);
  background: var(--ma-gold-soft, rgba(212, 168, 68, 0.14));
  border: 1px solid rgba(170, 130, 40, 0.35);
  color: var(--ma-gold-ink);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.35;
}
.ma-dws-warn svg { width: 15px; height: 15px; flex: 0 0 auto; margin-top: 1px; }

/* Sheet shell, reuses .ma-picker-overlay scrim. */
.ma-ff-sheet {
  background: var(--ma-surface);
  color: var(--ma-ink);
  width: 100%;
  max-width: 560px;
  max-height: min(92vh, var(--ma-sheet-max-h));
  border-radius: 20px 20px 0 0;
  border-top: 1px solid var(--ma-border);
  box-shadow: 0 -10px 40px rgba(20, 18, 14, 0.25);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: var(--ma-font-body);
  padding-bottom: var(--ma-inset-bottom);   /* full-screen home indicator */
}
[data-theme="dark"] .ma-ff-sheet {
  box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.6);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ma-ff-sheet {
    box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.6);
  }
}
.ma-ff-handle {
  width: 38px; height: 4px;
  border-radius: 999px;
  background: var(--ma-border-strong);
  margin: 10px auto 4px;
  flex: 0 0 auto;
}
.ma-ff-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 16px 12px;
  flex: 0 0 auto;
}
.ma-ff-head__eyebrow {
  font: 800 11px var(--ma-font-body);
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-ff-head__title {
  font: 800 18px var(--ma-font-body);
  letter-spacing: -0.3px;
  color: var(--ma-ink);
  margin-top: 1px;
}
.ma-ff-head__close {
  width: 30px; height: 30px;
  border-radius: 999px;
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  color: var(--ma-ink-3);
  cursor: pointer; padding: 0;
  font-size: 14px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.ma-ff-head__close:active { transform: scale(var(--ma-press-sm)); }

/* SHOWING banner, the brick-filled card that names what the user is
   currently looking at. Updates live as the operator stages edits. */
.ma-ff-banner {
  flex: 0 0 auto;
  margin: 0 16px 12px;
  padding: 14px;
  background: var(--ma-brick-solid);
  color: #fff;
  border-radius: 14px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.ma-ff-banner__icon {
  width: 38px; height: 38px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.18);
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.ma-ff-banner__icon svg { width: 18px; height: 18px; }
.ma-ff-banner__body {
  flex: 1; min-width: 0;
}
.ma-ff-banner__eyebrow {
  font-size: 10.5px; font-weight: 800;
  letter-spacing: 1px; opacity: 0.8;
  text-transform: uppercase;
}
.ma-ff-banner__range {
  font-family: var(--ma-font-num);
  font-size: 16px; font-weight: 800;
  margin-top: 2px;
  letter-spacing: -0.3px;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-ff-banner__label {
  font-size: 11px; font-weight: 700;
  opacity: 0.85; margin-top: 1px;
}

/* Scrolling list of facet groups. */
.ma-ff-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 16px 8px;
}
.ma-ff-group {
  margin-bottom: 14px;
}
.ma-ff-group__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: 8px;
}
.ma-ff-group__label {
  font: 800 10.5px var(--ma-font-body);
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
}
.ma-ff-group__label--wages::before,
.ma-ff-group__label--tips::before {
  content: '';
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 999px;
  margin-right: 5px;
  vertical-align: middle;
}
.ma-ff-group__label--wages::before { background: var(--ma-ink-3); }
.ma-ff-group__label--tips::before { background: var(--ma-gold); }
.ma-ff-group__summary {
  font-family: var(--ma-font-num);
  font-size: 13px; font-weight: 800;
  color: var(--ma-ink);
  margin-top: 3px;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-ff-group__clear {
  background: transparent;
  border: none;
  color: var(--ma-ink-3);
  font: 700 11px var(--ma-font-body);
  cursor: pointer; padding: 0;
}
.ma-ff-group__clear:hover { color: var(--ma-brick-ink); }
.ma-ff-chips {
  display: flex; flex-wrap: wrap; gap: 6px;
}

/* Entity-picker button, full-width tappable row that opens
   MAEntityPicker. Used inside .ma-ff-group when f.type === 'entity'. */
.ma-ff-entity-btn {
  width: 100%;
  display: flex; align-items: center; gap: var(--ma-sp-3);
  padding: var(--ma-sp-3) var(--ma-sp-4);
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-md);
  color: var(--ma-ink);
  font: 700 13px var(--ma-font-body);
  cursor: pointer;
  text-align: left;
}
.ma-ff-entity-btn:active { transform: scale(var(--ma-press-lg)); }
.ma-ff-entity-btn__summary {
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ma-ff-entity-btn__chev {
  flex: 0 0 auto;
  color: var(--ma-ink-3);
  display: inline-flex; align-items: center; justify-content: center;
}
.ma-ff-entity-btn__chev svg { width: 16px; height: 16px; }

/* Chip, both single-select (date presets etc.) and multi-select. */
.ma-ff-c-chip {
  padding: 7px 13px;
  border-radius: 999px;
  background: var(--ma-cream);
  color: var(--ma-ink-2);
  border: 1px solid var(--ma-border);
  font: 700 12px var(--ma-font-body);
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.15;
  white-space: nowrap;
  text-align: left;
}
.ma-ff-c-chip--has-sub { padding: 6px 11px; }
.ma-ff-c-chip:active { transform: scale(var(--ma-press)); }
.ma-ff-c-chip[aria-pressed="true"] {
  background: var(--ma-brick-solid);
  color: #fff;
  border-color: var(--ma-brick-solid);
}
.ma-ff-c-chip__sub {
  font-family: var(--ma-font-num);
  font-size: 9.5px;
  font-weight: 600;
  margin-top: 1px;
  color: var(--ma-ink-3);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-ff-c-chip[aria-pressed="true"] .ma-ff-c-chip__sub {
  color: rgba(255, 255, 255, 0.85);
}

/* Inline custom date range, mini calendar with month/year quick-pick. */
.ma-ff-cal {
  background: var(--ma-elevated);
  border: 1px solid var(--ma-border);
  border-radius: 12px;
  padding: 12px;
  margin-top: 6px;
  width: 100%;
  box-sizing: border-box;
}
.ma-ff-cal__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
}
.ma-ff-cal__nav {
  width: 30px; height: 30px;
  border-radius: 9px;
  background: var(--ma-surface);
  color: var(--ma-ink-2);
  border: 1px solid var(--ma-border);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
}
.ma-ff-cal__nav:active { transform: scale(var(--ma-press-sm)); }
.ma-ff-cal__nav svg { width: 15px; height: 15px; }
.ma-ff-cal__pills {
  display: flex; gap: 6px; flex: 1; justify-content: center;
}
.ma-ff-cal__pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px;
  background: var(--ma-surface);
  color: var(--ma-ink);
  border: 1px solid var(--ma-border);
  border-radius: 10px;
  font: 800 13px var(--ma-font-body);
  cursor: pointer;
  min-width: 90px;
  justify-content: center;
}
.ma-ff-cal__pill--year {
  font-family: var(--ma-font-num);
  min-width: 78px;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-ff-cal__pill[aria-expanded="true"] {
  background: var(--ma-brick-solid);
  color: #fff;
  border-color: var(--ma-brick-solid);
}
.ma-ff-cal__pill svg { width: 12px; height: 12px; }
.ma-ff-cal__quick {
  display: none;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: 10px;
  padding: 6px;
  margin-bottom: 12px;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
}
.ma-ff-cal__quick.is-open { display: grid; }
.ma-ff-cal__quick-btn {
  padding: 8px 0;
  border-radius: 7px;
  background: transparent;
  color: var(--ma-ink-2);
  border: none;
  font: 700 12px var(--ma-font-body);
  cursor: pointer;
}
.ma-ff-cal__quick--year .ma-ff-cal__quick-btn {
  font-family: var(--ma-font-num);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-ff-cal__quick-btn[aria-pressed="true"] {
  background: var(--ma-brick-solid);
  color: #fff;
}
.ma-ff-cal__dow {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  margin-bottom: 4px;
}
.ma-ff-cal__dow span {
  text-align: center;
  font: 800 10px var(--ma-font-body);
  color: var(--ma-ink-3);
  letter-spacing: 0.5px;
  padding: 4px 0;
}
.ma-ff-cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  font: 700 12px var(--ma-font-num);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
.ma-ff-cal__day {
  padding: 8px 0;
  border-radius: 8px;
  text-align: center;
  color: var(--ma-ink);
  background: transparent;
  border: none;
  cursor: pointer;
  min-height: 30px;
  font-weight: 600;
  font-family: inherit;
}
.ma-ff-cal__day--out { color: var(--ma-ink-3); cursor: default; pointer-events: none; }
.ma-ff-cal__day--today { border: 1.5px solid var(--ma-brick); }
.ma-ff-cal__day--in-range {
  background: var(--ma-brick-soft);
  color: var(--ma-brick-ink);
}
.ma-ff-cal__day--start,
.ma-ff-cal__day--end {
  background: var(--ma-brick-solid);
  color: #fff;
  font-weight: 800;
}
.ma-ff-cal__summary {
  margin-top: 10px;
  padding: 8px 12px;
  border-radius: 8px;
  background: var(--ma-brick-soft);
  color: var(--ma-brick-ink);
  font: 800 12px var(--ma-font-body);
  text-align: center;
}

/* Sticky bottom, Reset + Apply (with pending-count). */
.ma-ff-footer {
  flex: 0 0 auto;
  padding: 10px 16px 14px;
  display: flex; gap: 8px;
  border-top: 1px solid var(--ma-border);
  background: var(--ma-surface);
}
.ma-ff-btn {
  padding: 13px 16px;
  border-radius: 14px;
  border: none;
  font-family: var(--ma-font-body);
  cursor: pointer;
}
.ma-ff-btn:active { transform: scale(var(--ma-press)); }
.ma-ff-btn--reset {
  background: transparent;
  color: var(--ma-ink-2);
  border: 1.5px solid var(--ma-border-strong);
  font: 700 14px var(--ma-font-body);
}
.ma-ff-btn--apply {
  flex: 1;
  background: var(--ma-brick-solid);
  color: #fff;
  font-weight: 800;
  font-size: 14px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
.ma-ff-btn--apply svg { width: 16px; height: 16px; }
.ma-ff-btn--apply__badge {
  font-family: var(--ma-font-num);
  font-size: 11px;
  font-weight: 800;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.2);
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* ============================================================
   Expenses redesign atoms (design/design_handoff_expenses_redesign).
   Same atoms apply to Sales, sister redesign sharing the shell.
   Each tab leads with an .ma-insight ("plain-English sentence"), and
   AI/Zuzo surfaces are woven into the actions via .ma-ai-* atoms.
   ============================================================ */

/* ── InsightStrip, the "one plain-English sentence at the top of every
   tab" pattern. Headline carries the number/verb; sub-line the so-what.
   ─────────────────────────────────────────────────────────────── */
.ma-insight {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: 12px;
  margin: 8px 0 12px;
}
/* Shared close button, small X top-right on insight + AI atoms. */
.ma-insight__close,
.ma-ai-actionbar__close,
.ma-ai-suggestion__close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--ma-ink-3);
  border-radius: var(--ma-radius-sm);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.ma-insight__close:hover,
.ma-ai-actionbar__close:hover,
.ma-ai-suggestion__close:hover { color: var(--ma-ink); background: rgba(127,127,127,0.10); }
.ma-insight__close:active,
.ma-ai-actionbar__close:active,
.ma-ai-suggestion__close:active { transform: scale(var(--ma-press-sm)); }
.ma-insight__close svg,
.ma-ai-actionbar__close svg,
.ma-ai-suggestion__close svg { width: 12px; height: 12px; }
/* Pad right so the headline doesn't run under the X. */
.ma-insight:has(.ma-insight__close) .ma-insight__body,
.ma-ai-actionbar:has(.ma-ai-actionbar__close) .ma-ai-actionbar__body { padding-right: 22px; }
.ma-insight__dot {
  flex: 0 0 auto;
  width: 8px; height: 8px;
  margin-top: 7px;
  border-radius: 999px;
  background: var(--ma-forest);
}
.ma-insight--neutral .ma-insight__dot { background: var(--ma-ink-3); }
.ma-insight--down    .ma-insight__dot { background: var(--ma-brick); }
.ma-insight--warn    .ma-insight__dot { background: var(--ma-gold); }
.ma-insight__body { flex: 1; min-width: 0; }
.ma-insight__head {
  font: 700 13.5px var(--ma-font-body);
  color: var(--ma-ink);
  letter-spacing: -0.1px;
  line-height: 1.35;
}
.ma-insight__sub {
  margin-top: 4px;
  font: 600 12px var(--ma-font-body);
  color: var(--ma-ink-2);
  line-height: 1.4;
}

/* ── AIChip, compact lavender pill with a sparkle. One-tap commits an
   AI suggestion (placeholder for now). Variants: --solid (filled),
   --ghost (default, soft tint), --sm. Never use lavender / sparkle
   for non-AI surfaces, that's the brand's promise of provenance.
   ─────────────────────────────────────────────────────────────── */
.ma-ai-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px 5px 8px;
  border-radius: 999px;
  background: var(--ma-zuzo-soft);
  color: var(--ma-zuzo-ink);
  border: 1px solid var(--ma-zuzo);
  font: 700 11.5px var(--ma-font-body);
  letter-spacing: -0.1px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.ma-ai-chip:active { transform: scale(var(--ma-press)); }
.ma-ai-chip__spark {
  display: inline-flex;
  width: 12px; height: 12px;
  color: var(--ma-zuzo);
}
.ma-ai-chip__spark svg { width: 12px; height: 12px; }
.ma-ai-chip__sure {
  font-family: var(--ma-font-num);
  font-weight: 800;
  font-size: 10.5px;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--ma-zuzo);
  color: #fff;
  margin-left: 4px;
}
.ma-ai-chip--solid {
  background: var(--ma-zuzo);
  color: #0E0B26;
  border-color: var(--ma-zuzo);
}
.ma-ai-chip--solid .ma-ai-chip__spark { color: #0E0B26; }
.ma-ai-chip--sm { padding: 3px 8px 3px 6px; font-size: 10.5px; }

/* ── AIActionBar, full-width strip at the top of a list. Headline
   + Preview + Apply. Tap targets are full-height; on small screens
   the buttons drop to a second row gracefully. ─────────────── */
.ma-ai-actionbar {
  position: relative;        /* anchor for .ma-ai-actionbar__close */
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  background: var(--ma-zuzo-soft);
  border: 1px dashed var(--ma-zuzo);
  border-radius: 12px;
  margin: 8px 0;
}
.ma-ai-actionbar__spark {
  flex: 0 0 auto;
  display: inline-flex;
  width: 28px; height: 28px;
  border-radius: 8px;
  background: var(--ma-zuzo);
  color: #0E0B26;
  align-items: center; justify-content: center;
}
.ma-ai-actionbar__spark svg { width: 16px; height: 16px; }
.ma-ai-actionbar__body { flex: 1; min-width: 0; }
.ma-ai-actionbar__head {
  font: 700 12.5px var(--ma-font-body);
  color: var(--ma-zuzo-ink);
  letter-spacing: -0.1px;
}
.ma-ai-actionbar__sub {
  margin-top: 2px;
  font: 600 11px var(--ma-font-body);
  color: var(--ma-ink-2);
}
.ma-ai-actionbar__actions {
  display: inline-flex; gap: 6px; flex: 0 0 auto;
}
.ma-ai-actionbar__btn {
  padding: 6px 12px;
  border-radius: 999px;
  font: 700 11.5px var(--ma-font-body);
  border: 1px solid var(--ma-zuzo);
  background: transparent;
  color: var(--ma-zuzo-ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-ai-actionbar__btn--primary {
  background: var(--ma-zuzo);
  color: #0E0B26;
}
.ma-ai-actionbar__btn:active { transform: scale(var(--ma-press)); }
@media (max-width: 380px) {
  .ma-ai-actionbar { flex-wrap: wrap; }
  .ma-ai-actionbar__actions { width: 100%; justify-content: flex-end; }
}

/* ── AISuggestionRow, inline "✦ Map to X · Apply · ✕" used INSTEAD
   of opening a modal for ≥75% confidence single-line suggestions.
   ─────────────────────────────────────────────────────────────── */
.ma-ai-suggestion {
  position: relative;        /* anchor for .ma-ai-suggestion__close */
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--ma-zuzo-soft);
  border: 1px dashed var(--ma-zuzo);
  border-radius: 10px;
  margin: 6px 0;
}
.ma-ai-suggestion__spark {
  flex: 0 0 auto;
  display: inline-flex;
  width: 18px; height: 18px;
  color: var(--ma-zuzo);
}
.ma-ai-suggestion__spark svg { width: 14px; height: 14px; }
.ma-ai-suggestion__body { flex: 1; min-width: 0; }
.ma-ai-suggestion__head {
  font: 700 12px var(--ma-font-body);
  color: var(--ma-zuzo-ink);
  letter-spacing: -0.1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ma-ai-suggestion__sub {
  margin-top: 2px;
  font: 600 11px var(--ma-font-body);
  color: var(--ma-ink-2);
}
.ma-ai-suggestion__actions {
  flex: 0 0 auto;
  display: inline-flex;
  gap: 4px;
}
.ma-ai-suggestion__apply,
.ma-ai-suggestion__dismiss {
  width: 28px; height: 28px;
  border-radius: 8px;
  border: 1px solid var(--ma-zuzo);
  background: transparent;
  color: var(--ma-zuzo);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-ai-suggestion__apply { background: var(--ma-zuzo); color: #0E0B26; }
.ma-ai-suggestion__apply svg,
.ma-ai-suggestion__dismiss svg { width: 14px; height: 14px; }

/* ── + FAB, bottom-left, brick. The Vouchers/Receipts pages only.
   Pairs with the Ask Zuzo dock (which is bottom-right). Splitting
   left/right fixes the collision that lived on Vouchers in the
   pre-redesign app. ─────────────────────────────────────────── */
.ma-fab-plus {
  position: fixed;
  left: 14px;
  bottom: calc(84px + var(--ma-inset-bottom));   /* clears the bottom nav */
  width: 50px; height: 50px;
  border-radius: 999px;
  background: var(--ma-brick-solid);
  color: #fff;
  border: none;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  z-index: 30;
  box-shadow: 0 8px 20px rgba(229, 91, 61, 0.40),
              0 0 0 4px var(--ma-cream);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: transform 80ms ease;
}
.ma-fab-plus:active { transform: scale(var(--ma-press)); }
.ma-fab-plus svg { width: 24px; height: 24px; }

/* ── TriageStrip, 3 progress rows replacing the 6 status cards on the
   Receipt lines tab. Compact, scannable in one glance. Each row is
   tappable to set the filter chip server-side. ───────────────── */
.ma-triage {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  background: var(--ma-surface);
  border: 1px solid var(--ma-border);
  border-radius: 12px;
  margin: 8px 0;
}
.ma-triage__row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-triage__row:active { transform: scale(var(--ma-press-lg)); }
.ma-triage__label {
  font: 700 12px var(--ma-font-body);
  color: var(--ma-ink);
  letter-spacing: -0.1px;
}
.ma-triage__num {
  font-family: var(--ma-font-num);
  font-weight: 800;
  font-size: 13px;
  color: var(--ma-ink);
}
.ma-triage__bar {
  grid-column: 1 / span 2;
  height: 4px;
  border-radius: 999px;
  background: var(--ma-border);
  overflow: hidden;
}
.ma-triage__bar-fill {
  height: 100%;
  background: var(--ma-brick);
  border-radius: 999px;
}
.ma-triage__bar-fill--gold   { background: var(--ma-gold); }
.ma-triage__bar-fill--forest { background: var(--ma-forest); }
.ma-triage__ai {
  display: flex; gap: 6px; flex-wrap: wrap;
  padding-top: 8px;
  border-top: 1px dashed var(--ma-border);
  margin-top: 4px;
}


/* ═════════════════════════════════════════════════════════════
   MACharts, the chart suite
   ─────────────────────────────────────────────────────────────
   Seven forms cover every chart in the app, all drawn with divs. No
   chart library: Plotly was ~1 MB over the wire on a page whose
   best-loved tab (Sales > Items) never used a pixel of it.

   Which form:
     one headline figure      -> .ma-scorecard (already in this file)
     magnitude, in a list     -> .ma-ch-rows
     change over time         -> .ma-ch-cols
     a trend over time        -> .ma-ch-line
     a split that sums to 1   -> .ma-ch-share
     two dimensions at once   -> .ma-ch-heat
     a trend beside a number  -> .ma-ch-spark

   Geometry is one set of numbers everywhere, which is what makes it
   read as one product: 8px bar height, 4px radius on the outer data
   ends only, a 2px surface gap between stacked segments so two
   series read as two marks and not one gradient.

   Colour rules (docs/REFERENCE-design-system.md section "Charts"):
     - series colour comes from a token, never from a payload, or it
       cannot follow the theme;
     - text wears text tokens, never the series colour, the swatch
       beside a value carries identity;
     - two series always ship a legend AND a direct label. Green
       against blue is a tritan collision (separation drops to
       deltaE ~4 for blue-cone-deficient viewers whichever green and
       blue you pick), so the Uber/Yoco pair may never be told apart
       by colour alone. The "U 12 / Y 8" labels are the fix.
   ═════════════════════════════════════════════════════════════ */

/* ── legend ──────────────────────────────────────────────────── */
.ma-ch-legend {
  display: flex; flex-wrap: wrap;
  gap: var(--ma-sp-2) var(--ma-sp-5);
  margin-bottom: var(--ma-sp-4);
}
.ma-ch-legend__item {
  display: inline-flex; align-items: center; gap: 6px;
  font: 700 11.5px var(--ma-font-body);
  color: var(--ma-ink-2);
}
.ma-ch-sw {
  width: 10px; height: 10px; border-radius: 3px;
  flex: 0 0 auto; display: inline-block;
}

/* Empty state, a chart that has no data says so in the chart's
   own box, at the chart's own height, so nothing jumps. */
.ma-ch-empty {
  display: flex; align-items: center; justify-content: center;
  min-height: 96px; padding: var(--ma-sp-5);
  font: 500 13px var(--ma-font-body);
  color: var(--ma-ink-3);
  text-align: center;
}

/* ── Form: split rows ────────────────────────────────────────── */
.ma-ch-rows { display: block; }
.ma-ch-row {
  display: flex; align-items: center; gap: var(--ma-sp-3);
  padding: var(--ma-sp-3) 0;
  width: 100%; text-align: left;
  background: none; border: none;
  border-top: 1px solid var(--ma-border);
  color: inherit; font: inherit;
  -webkit-tap-highlight-color: transparent;
}
.ma-ch-rows > .ma-ch-row:first-child { border-top: none; }
button.ma-ch-row { cursor: pointer; }
button.ma-ch-row:active { opacity: var(--ma-press-fade); }
.ma-ch-row__rank {
  font-family: var(--ma-font-num); font-weight: 700; font-size: 12px;
  color: var(--ma-ink-3); width: 18px; flex: 0 0 auto;
}
.ma-ch-row__mid { flex: 1 1 auto; min-width: 0; }
.ma-ch-row__nm {
  display: block; font: 700 13.5px var(--ma-font-body);
  color: var(--ma-ink); letter-spacing: -0.1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ma-ch-row__track {
  display: flex; gap: 2px; height: 8px; margin-top: 5px;
}
.ma-ch-row__seg {
  height: 8px; min-width: 0;
  transition: width var(--ma-dur-grow) var(--ma-ease);
}
.ma-ch-row__seg:first-child { border-radius: 4px 0 0 4px; }
.ma-ch-row__seg:last-child  { border-radius: 0 4px 4px 0; }
.ma-ch-row__seg:only-child  { border-radius: 4px; }
/* Direct label, the accessibility guarantee, not decoration. */
.ma-ch-row__lab {
  display: block; margin-top: 5px;
  font-family: var(--ma-font-num); font-weight: 700; font-size: 11px;
  color: var(--ma-ink-2);
}
.ma-ch-row__right { flex: 0 0 auto; text-align: right; }
.ma-ch-row__v {
  font-family: var(--ma-font-num); font-variant-numeric: tabular-nums;
  font-weight: 700; font-size: 14px; color: var(--ma-ink);
}
.ma-ch-row__sub {
  display: block; font: 500 10.5px var(--ma-font-body);
  color: var(--ma-ink-3);
}
.ma-ch-row__delta {
  font-family: var(--ma-font-num); font-weight: 800; font-size: 10.5px;
  margin-left: 6px;
}
.ma-ch-row__delta--up   { color: var(--ma-forest); }
.ma-ch-row__delta--down { color: var(--ma-brick); }
.ma-ch-row__delta--flat { color: var(--ma-ink-3); }

/* A row that opens (MACharts.rows `onExpand`). The bar keeps its place
   and what it is made of unfolds under it, so the operator never loses
   the row they tapped. The fold is `grid-template-rows: 0fr → 1fr`,
   which animates to the panel's own height without anyone measuring
   it; where an engine will not animate that, the row still opens and
   closes correctly, it just arrives at once. */
.ma-ch-rowwrap {
  display: block;
  border-top: 1px solid var(--ma-border);
  border-radius: 12px;
  transition: background var(--ma-dur-swap) var(--ma-ease);
}
.ma-ch-rows > .ma-ch-rowwrap:first-child { border-top: none; }
/* The wrapper carries the rule now; a second one on the row inside it
   would draw a line under every open panel. */
.ma-ch-rowwrap > .ma-ch-row { border-top: none; }
.ma-ch-rowwrap[aria-expanded="true"] {
  background: var(--ma-elevated);
  box-shadow: inset 0 0 0 1px var(--ma-border);
}
.ma-ch-row__chev {
  flex: 0 0 auto; width: 16px; height: 16px; color: var(--ma-ink-3);
  transition: transform var(--ma-dur-swap) var(--ma-ease);
}
.ma-ch-row__chev svg { width: 16px; height: 16px; display: block; }
.ma-ch-rowwrap[aria-expanded="true"] .ma-ch-row__chev {
  transform: rotate(90deg); color: var(--ma-ink-2);
}
.ma-ch-row__panel {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--ma-dur-grow) var(--ma-ease);
}
.ma-ch-rowwrap[aria-expanded="true"] > .ma-ch-row__panel { grid-template-rows: 1fr; }
.ma-ch-row__panelin { overflow: hidden; min-height: 0; }

/* ── Form: stacked columns ───────────────────────────────────── */
/* Many columns (a 31-day month) scroll sideways inside the card
   rather than squeezing to slivers. The page body never scrolls. */
.ma-ch-cols-scroll {
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; -ms-overflow-style: none;
}
.ma-ch-cols-scroll::-webkit-scrollbar { display: none; }
.ma-ch-cols {
  display: flex; align-items: flex-end;
  gap: var(--ma-sp-2);
  position: relative;
  /* A value label is wider than its column, so the first and last need
     room or they clip against the card edge; the top pad is headroom for
     a floating flag chip. */
  padding: 14px 14px 0;
}
.ma-ch-col {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: flex-end;
}
/* Scrolling mode: columns get a fixed width instead of sharing. */
.ma-ch-cols--wide .ma-ch-col { flex: 0 0 34px; }
.ma-ch-col__n {
  font-family: var(--ma-font-num); font-variant-numeric: tabular-nums;
  font-weight: 700; font-size: 10.5px;
  color: var(--ma-ink-2); margin-bottom: 4px;
  white-space: nowrap;
  /* An unlabelled column still reserves the line, so every column in a
     sparsely-labelled plot sits on the same baseline. */
  min-height: 13px;
}
.ma-ch-col__stack {
  width: 100%; max-width: 30px;
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: 2px;
}
.ma-ch-col__seg {
  width: 100%;
  transition: height var(--ma-dur-grow) var(--ma-ease);
}
.ma-ch-col__seg--top { border-radius: 4px 4px 0 0; }
.ma-ch-col__seg--btm { border-radius: 0 0 4px 4px; }
.ma-ch-col__seg--solo { border-radius: 4px; }
.ma-ch-col__x {
  font: 700 9.5px var(--ma-font-body);
  color: var(--ma-ink-3); margin-top: 6px;
  letter-spacing: 0.3px; text-align: center;
  /* Wrap by default: a few wide columns can carry a real label
     ("Lunch (11am-3pm)"), and nowrap made neighbours run into each
     other. In wide mode the columns are 34px and every label is short,
     so nowrap is right there. */
  max-width: 100%; line-height: 1.25;
  /* Wrap at spaces only. `anywhere` split "+20d" into "+2 / 0d"; a
     multi-word label like "Lunch (11am-3pm)" still wraps, which is the
     case that needed it. */
  overflow-wrap: normal;
}
.ma-ch-cols--wide .ma-ch-col__x { white-space: nowrap; overflow-wrap: normal; }
.ma-ch-col--on .ma-ch-col__x,
.ma-ch-col--on .ma-ch-col__n { color: var(--ma-brick); }
button.ma-ch-col { background: none; border: none; padding: 0; cursor: pointer; font: inherit; }
button.ma-ch-col:active { opacity: var(--ma-press-fade); }

/* A flag over a column, "Uber sent nothing this day", "best yet".
   Replaces Plotly's annotation markers.

   Absolutely positioned on purpose: in a flex row every column shares
   the tallest one's height, so a flag that took part in layout made
   ALL columns taller, including the ones scrolled off-screen, which
   left visible dead space above the bars for a flag nobody could see. */
.ma-ch-col { position: relative; }
.ma-ch-col__flag {
  position: absolute; top: -2px; left: 50%; transform: translateX(-50%);
  font-size: 9px; font-weight: 800; line-height: 1.15;
  color: var(--ma-gold-ink); background: var(--ma-gold-soft);
  border-radius: 3px; padding: 1px 3px; white-space: nowrap;
  pointer-events: none; z-index: 1;
}

/* Baseline reference line across the plot, the 30d / 90d / YTD
   average. One dashed rule plus a label, no second axis. */
.ma-ch-baseline {
  position: absolute; left: 0; right: 0;
  border-top: 1.5px dashed var(--ma-brick);
  pointer-events: none;
}
.ma-ch-baseline__lab {
  position: absolute; left: 0; top: -14px;
  font: 800 9.5px var(--ma-font-body);
  letter-spacing: 0.4px; text-transform: uppercase;
  color: var(--ma-brick-ink); background: var(--ma-brick-soft);
  border-radius: 3px; padding: 1px 4px; white-space: nowrap;
}

/* ── Form: share bar ─────────────────────────────────────────── */
.ma-ch-share { display: flex; gap: 2px; height: 26px; }
.ma-ch-share__seg {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--ma-font-num); font-weight: 800; font-size: 11px;
  color: #fff; white-space: nowrap; overflow: hidden;
  transition: width var(--ma-dur-grow) var(--ma-ease);
}
.ma-ch-share__seg:first-child { border-radius: 6px 0 0 6px; }
.ma-ch-share__seg:last-child  { border-radius: 0 6px 6px 0; }
.ma-ch-share__seg:only-child  { border-radius: 6px; }
/* Too narrow to hold its label, the label moves to the caption. */
.ma-ch-share__seg--bare { color: transparent; }
.ma-ch-share__cap {
  display: flex; flex-wrap: wrap; gap: var(--ma-sp-2) var(--ma-sp-5);
  margin-top: var(--ma-sp-2);
  font: 700 11px var(--ma-font-body); color: var(--ma-ink-2);
}

/* ── Form: heat grid ─────────────────────────────────────────── */
.ma-ch-heat { display: grid; gap: 3px; }
.ma-ch-heat__hd,
.ma-ch-heat__lab {
  font: 800 9.5px var(--ma-font-body);
  color: var(--ma-ink-3); letter-spacing: 0.4px;
}
.ma-ch-heat__hd { text-align: center; }
.ma-ch-heat__lab { display: flex; align-items: center; }
.ma-ch-heat__c {
  aspect-ratio: 1.5; border-radius: 3px;
  min-height: 18px;
  background: var(--ma-ch-heat-fill, transparent);
}
button.ma-ch-heat__c { border: none; padding: 0; cursor: pointer; }
button.ma-ch-heat__c:active { opacity: var(--ma-press-fade); }
.ma-ch-heat__c--on { box-shadow: 0 0 0 2px var(--ma-brick); }
.ma-ch-heat__scale {
  display: flex; align-items: center; gap: 6px;
  margin-top: var(--ma-sp-3);
  font: 700 10px var(--ma-font-body); color: var(--ma-ink-3);
}
.ma-ch-heat__ramp { display: flex; gap: 2px; }
.ma-ch-heat__step { width: 14px; height: 8px; border-radius: 2px; }

/* ── Form: sparkline ─────────────────────────────────────────── */
.ma-ch-spark { display: flex; align-items: flex-end; gap: 2px; height: 34px; }
.ma-ch-spark__b {
  flex: 1 1 0; min-width: 2px;
  background: var(--ma-border-strong);
  border-radius: 2px 2px 0 0;
  transition: height var(--ma-dur-grow) var(--ma-ease);
}
.ma-ch-spark__b--end { background: var(--ma-brick); }

/* ── Form: line over time ────────────────────────────────────── */
/* A trend drawn from readings that arrive on their own dates:
   cumulative meter counts, a running balance, anything where a gap
   between two points is silence and never zero. `columns` prints a
   zero bar for a missing day, which is a reading of nothing; this
   joins the points it has and names the first and last. */
.ma-ch-line { display: block; width: 100%; height: 132px; }
.ma-ch-line__svg { width: 100%; height: 100%; display: block; overflow: visible; }
.ma-ch-line__grid {
  stroke: var(--ma-border); stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.ma-ch-line__path {
  fill: none; stroke: var(--ma-brick); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.ma-ch-line__dot {
  fill: var(--ma-brick);
  vector-effect: non-scaling-stroke;
}
.ma-ch-line__dot--end {
  stroke: var(--ma-surface); stroke-width: 2;
}
.ma-ch-line__foot {
  display: flex; justify-content: space-between; gap: var(--ma-sp-3);
  margin-top: 2px;
  font: 700 10px var(--ma-font-body); color: var(--ma-ink-3);
}
.ma-ch-line__end {
  font-family: var(--ma-font-num); font-variant-numeric: tabular-nums;
  font-weight: 800; font-size: 12px; color: var(--ma-ink);
}

/* ═════════════════════════════════════════════════════════════
   MASurface, a piece of the app, drawn wherever it is asked for
   ─────────────────────────────────────────────────────────────
   The card chrome MASurface.mount() draws around a surface in
   Zuzo's chat, plus the few things every Sales renderer needs. A
   page draws its own layout and only borrows the inner classes.

   Nothing here measures the screen: no vh, no fixed, no sticky.
   The chat panel is height: 75vh, so a vh inside a surface would
   size against the phone and not against the panel it is in.
   ═════════════════════════════════════════════════════════════ */
.ma-surface {
  display: block;
  width: 100%;
  min-width: 0;
}
.ma-surface .ma-section-title { margin: 0 0 var(--ma-sp-2); }
.ma-surface__body { display: block; min-width: 0; }
.ma-surface__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--ma-sp-2);
}
.ma-surface__foot:empty { display: none; }

/* The stretch the numbers cover, said once, at the top, so no
   figure below it has to repeat the dates. */
.ma-surface__period {
  font-family: var(--ma-font-num);
  font-size: 12px;
  font-weight: 600;
  color: var(--ma-ink-3);
  margin-bottom: var(--ma-sp-2);
}
.ma-surface__dishname {
  font-size: 15px;
  font-weight: 700;
  color: var(--ma-ink);
  line-height: 1.3;
}
/* A caveat, a comparison, a note about the export. Never a finding -
   a finding is the strip at the top. */
.ma-surface__note {
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ma-ink-3);
  margin: var(--ma-sp-2) 0;
}
.ma-surface__empty {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ma-ink-2);
  padding: var(--ma-sp-4) 0;
}
.ma-surface__scorecards { margin: var(--ma-sp-3) 0; }
.ma-surface__chart { margin-top: var(--ma-sp-4); }
.ma-surface__chart-title {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--ma-ink-3);
  margin-bottom: var(--ma-sp-2);
}
/* One dish on its own is not a list, so it carries no divider. */
.ma-surface__dishrow { border-top: none; }

/* pantry, the shelves, folded like the picker's. A row is a name,
   its price · shop · date, and the ladder word. Sized from the
   container: no heights, no viewport units. */
.ma-surface__shelves { margin-top: var(--ma-sp-2); }
.ma-surface__shelf { margin: var(--ma-sp-3) 0 0; }
.ma-surface__shelf:first-child { margin-top: 0; }
.ma-surface__shelf-rows { display: flex; flex-direction: column; }
.ma-surface__pantry-row {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-2);
  padding: 8px 0;
  border-top: 1px solid var(--ma-border);
}
.ma-surface__pantry-row:first-child { border-top: none; }
.ma-surface__pantry-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}
.ma-surface__pantry-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ma-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ma-surface__pantry-meta {
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--ma-ink-3);
}
.ma-surface__more {
  font-size: 11.5px;
  color: var(--ma-ink-3);
  padding: 6px 0 0;
}

/* One printed row that is two things (RCP-9): the card in the chat and the
   sheet on the page draw the same parts under the same title. */
.ma-surface__split,
.ma-ls-split {
  display: flex;
  flex-direction: column;
  gap: var(--ma-sp-2);
}
.ma-surface__split-part,
.ma-ls-split__part {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-2);
  padding: 8px 0;
  border-top: 1px solid var(--ma-border);
}
.ma-surface__split-part:first-child,
.ma-ls-split__part:first-child { border-top: none; }
.ma-surface__split-main,
.ma-ls-split__main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}
.ma-surface__split-name,
.ma-ls-split__name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ma-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ma-surface__split-qty,
.ma-ls-split__qty {
  font-size: 11.5px;
  color: var(--ma-ink-3);
}
.ma-surface__split-share,
.ma-ls-split__share {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ma-ink);
  flex: 0 0 auto;
}
.ma-surface__split-word {
  font-size: 11.5px;
  color: var(--ma-ink-3);
  flex: 0 0 auto;
}

/* The Organise card's split block (RCP-9): the parts under the printed
   row, or the one offer to split a name that joins two things. */
.ma-org-split {
  display: flex;
  flex-direction: column;
  gap: var(--ma-sp-2);
}
.ma-org-split__part {
  display: flex;
  align-items: center;
  gap: var(--ma-sp-2);
  padding: 8px 0;
  border-top: 1px solid var(--ma-border);
}
.ma-org-split__part:first-child { border-top: none; }
.ma-org-split__main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}
.ma-org-split__name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ma-ink);
}
.ma-org-split__qty {
  font-size: 11.5px;
  color: var(--ma-ink-3);
}
.ma-org-split__share {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ma-ink);
  flex: 0 0 auto;
}
.ma-org-split__offer {
  font-size: 13px;
  font-weight: 600;
  color: var(--ma-forest);
  background: none;
  border: none;
  padding: 0;
  text-align: left;
}
.ma-org-split__hint {
  font-size: 11.5px;
  color: var(--ma-ink-3);
  padding-top: 4px;
}

/* ═════════════════════════════════════════════════════════════
   Offerings, the quotes list, the experience cards and the rows
   the app page draws from one payload (lambdas/offerings). The two
   registered surfaces (offerings_quotes, offerings_experiences) use
   these too, so they live here and not on the page. Tokens only;
   nothing measures the screen.
   ═════════════════════════════════════════════════════════════ */
.ma-off-hero {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
  margin-bottom: var(--ma-sp-3);
}
.ma-off-tile {
  background: var(--ma-surface); border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg); padding: 10px 10px 9px;
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
}
.ma-off-tile__l {
  font-size: 10px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase;
  color: var(--ma-ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ma-off-tile__v {
  font-family: var(--ma-font-num); font-size: 21px; font-weight: 700; letter-spacing: -.5px;
  color: var(--ma-ink); font-feature-settings: 'tnum' 1, 'lnum' 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ma-off-tile--gold   .ma-off-tile__v { color: var(--ma-gold-ink); }
.ma-off-tile--forest .ma-off-tile__v { color: var(--ma-forest); }
.ma-off-tile__s { font-size: 10.5px; line-height: 1.3; color: var(--ma-ink-3); }
.ma-off-words {
  font-size: 12.5px; line-height: 1.45; color: var(--ma-ink-2);
  margin: 0 0 var(--ma-sp-3);
}
/* The status chips scroll as one row; nothing wraps under the tiles. */
.ma-off-chips {
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
  margin: 0 0 var(--ma-sp-3); padding-bottom: 2px;
}
.ma-off-chips::-webkit-scrollbar { display: none; }
.ma-off-chips .ma-pill { flex: 0 0 auto; white-space: nowrap; cursor: pointer; border: 0; }
.ma-off-chip__n { font-family: var(--ma-font-num); font-weight: 600; opacity: .75; }

.ma-off-list { display: flex; flex-direction: column; gap: 8px; }
/* A row is a slip's shape: the left border is the one thing a thumb
   reads at scrolling speed (gold is waiting on a client, forest is
   won, hint is over), then the name, the money, the sentence and the
   state. */
.ma-off-row {
  display: flex; width: 100%; text-align: left; cursor: pointer;
  padding: 11px 13px; min-height: 60px; border-radius: 11px;
  background: var(--ma-surface); border: 1px solid var(--ma-border); border-left-width: 4px;
  color: var(--ma-ink); font-family: var(--ma-font-body);
  -webkit-tap-highlight-color: transparent;
}
.ma-off-row--gold   { border-left-color: var(--ma-gold); }
.ma-off-row--forest { border-left-color: var(--ma-forest); }
.ma-off-row--hint   { border-left-color: var(--ma-border-strong); }
.ma-off-row--brick  { border-left-color: var(--ma-brick); }
.ma-off-row__main { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ma-off-row__top, .ma-off-row__bottom {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.ma-off-row__name {
  font-size: 14px; font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-off-row__amount {
  flex: 0 0 auto; font-family: var(--ma-font-num); font-size: 14px; font-weight: 700;
  letter-spacing: -.3px; font-feature-settings: 'tnum' 1;
}
.ma-off-row__amount--quiet { color: var(--ma-ink-3); font-weight: 500; }
.ma-off-row__sub {
  font-size: 11.5px; color: var(--ma-ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A row whose second line is a sentence, a part's cost and where it is
   used, wraps rather than ends in an ellipsis. */
.ma-off-row__sub--wrap { white-space: normal; overflow: visible; line-height: 1.35; }
.ma-off-row__state {
  flex: 0 0 auto; font-size: 10.5px; font-weight: 700; letter-spacing: .3px;
  text-transform: uppercase; color: var(--ma-ink-3);
}
.ma-off-row--gold .ma-off-row__state   { color: var(--ma-gold-ink); }
.ma-off-row--forest .ma-off-row__state { color: var(--ma-forest); }
.ma-off-row__note { font-size: 11px; color: var(--ma-ink-2); }
.ma-off-row__avatar { flex: 0 0 auto; margin-right: 10px; align-self: center; }
.ma-off-row--muted { opacity: .55; }
.ma-off-empty {
  padding: 26px 12px; text-align: center; font-size: 12.5px; line-height: 1.5; color: var(--ma-ink-3);
}

/* An experience is a card: name and price on the first line, how long
   and for how many under it, what it keeps in forest (brick when it is
   under the floor), then its parts as chips. */
.ma-off-cards { display: flex; flex-direction: column; gap: 10px; }
.ma-off-card {
  display: flex; flex-direction: column; gap: 6px; width: 100%; text-align: left; cursor: pointer;
  padding: 13px 14px; border-radius: 12px; color: var(--ma-ink); font-family: var(--ma-font-body);
  background: var(--ma-surface); border: 1px solid var(--ma-border);
  -webkit-tap-highlight-color: transparent;
}
.ma-off-card--off { opacity: .6; }
.ma-off-card__top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.ma-off-card__name { font-size: 15px; font-weight: 800; min-width: 0; }
.ma-off-card__price {
  flex: 0 0 auto; font-family: var(--ma-font-num); font-size: 13px; font-weight: 700;
  color: var(--ma-brick); white-space: nowrap;
}
.ma-off-card__sub { font-size: 11.5px; color: var(--ma-ink-3); }
.ma-off-card__keep { font-size: 12px; font-weight: 600; color: var(--ma-forest); line-height: 1.4; }
.ma-off-card__keep--low { color: var(--ma-brick-ink); }
.ma-off-card__parts { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 2px; }
.ma-off-partchip {
  display: inline-flex; align-items: center; padding: 3px 8px; border-radius: 999px;
  font-size: 11px; line-height: 1.3; background: var(--ma-cream); color: var(--ma-ink-2);
  border: 1px solid var(--ma-border);
}
.ma-off-partchip--missing { color: var(--ma-brick-ink); border-color: var(--ma-brick-soft); background: var(--ma-brick-soft); }
.ma-off-partchip--more { color: var(--ma-ink-3); border-style: dashed; }

/* ── recipe_weeks, Last four weeks say ──────────────────────────────
   The card under a recipe line and the same card in the chat: Zuzo's
   colour on the kicker, both numbers, the four weeks as tiles wearing
   the side each fell on, Use · Keep. A row on the check sheet is the
   same tiles under a name with the sentence beside them. Tokens only -
   dark mode paints itself. */
.ma-weeks { margin-top: 8px; padding: 10px 12px; border-radius: var(--ma-radius-lg); border: 1px solid var(--ma-zuzo); background: var(--ma-zuzo-soft); color: var(--ma-ink); }
.ma-weeks__k { display: flex; align-items: center; gap: 6px; font: 800 10.5px var(--ma-font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--ma-zuzo); }
.ma-weeks__k svg { width: 13px; height: 13px; }
.ma-weeks__t { margin-top: 4px; font: 800 14.5px var(--ma-font-body); line-height: 1.25; letter-spacing: -.1px; }
.ma-weeks__s { margin-top: 4px; font: 400 12px var(--ma-font-body); color: var(--ma-ink-2); line-height: 1.45; }
.ma-weeks__tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-top: 8px; }
.ma-wk { padding: 6px 4px; border-radius: var(--ma-radius-md); background: var(--ma-surface); border: 1px solid var(--ma-border); text-align: center; }
.ma-wk__k { font: 700 10px var(--ma-font-body); color: var(--ma-ink-3); text-transform: uppercase; letter-spacing: .04em; }
.ma-wk__v { margin-top: 2px; font: 700 12.5px var(--ma-font-num); color: var(--ma-ink); }
.ma-wk--over, .ma-wk--under { border-color: var(--ma-brick); }
.ma-wk--over .ma-wk__v, .ma-wk--under .ma-wk__v { color: var(--ma-brick); }
.ma-wk--agrees { border-color: var(--ma-forest); }
.ma-wk--agrees .ma-wk__v { color: var(--ma-forest-ink); }
.ma-wk--na .ma-wk__v { color: var(--ma-ink-3); }
.ma-weeks__btns { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.ma-weeks__btns .ma-btn { min-height: 36px; }
.ma-weeks--done { border-color: var(--ma-forest); background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
.ma-weeks--done .ma-weeks__k { color: var(--ma-forest-ink); }
.ma-weeks--done .ma-weeks__s { color: inherit; opacity: .9; }
.ma-weeks--quiet { border-style: dashed; border-color: var(--ma-border-strong); background: var(--ma-surface); }
.ma-weeks--quiet .ma-weeks__k { color: var(--ma-ink-3); }
.ma-weeks__putback { margin-top: 8px; }
.ma-weeks-take { padding: 10px 12px; margin-bottom: var(--ma-sp-3); border-radius: var(--ma-radius-lg); background: var(--ma-cream); border: 1px solid var(--ma-border); }
.ma-weeks-take b { display: block; font: 800 14px var(--ma-font-body); color: var(--ma-ink); }
.ma-weeks-take span { display: block; margin-top: 3px; font: 400 12.5px var(--ma-font-body); color: var(--ma-ink-2); line-height: 1.45; }
.ma-weeks-row { padding: 10px 12px; margin-bottom: var(--ma-sp-2); background: var(--ma-surface); border: 1px solid var(--ma-border); border-radius: var(--ma-radius-xl); color: var(--ma-ink); }
.ma-weeks-row--look { border-color: var(--ma-brick); }
.ma-weeks-row--quiet { opacity: .75; }
.ma-weeks-row__t { font: 700 14px var(--ma-font-body); line-height: 1.3; }
.ma-weeks-row__k { font: 800 10.5px var(--ma-font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--ma-ink-3); }
.ma-weeks-row__s { margin-top: 3px; font: 400 12px var(--ma-font-body); color: var(--ma-ink-2); line-height: 1.4; }
.ma-weeks-row .ma-weeks__tiles { margin-top: 6px; }
.ma-weeks-key { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 4px 2px 10px; font: 600 11.5px var(--ma-font-body); color: var(--ma-ink-2); }
.ma-weeks-key i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 5px; vertical-align: -1px; }
.ma-weeks-how { padding: 8px 12px; font: 400 12px var(--ma-font-body); color: var(--ma-ink-3); line-height: 1.45; }

/* ═════════════════════════════════════════════════════════════
   prefers-reduced-motion, the whole app, one block
   ─────────────────────────────────────────────────────────────
   This file is injected into every Mini App page BEFORE the page's
   own <style>, so `!important` is what makes this reach page-local
   rules too. Which is the point: one block switches motion off
   across all 90-odd pages, including any page that forgot to ask.

   Animations are collapsed rather than removed so keyframes still
   land on their final frame, a sheet that animates in with
   `translateY(100%) → 0` must still end up at 0, not stay
   off-screen. Three things are exempted below, each because its
   end frame is not a resting state.

   Do NOT add a page-local reduced-motion block. If something here
   is wrong for your surface, fix it here.
   ═════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
    animation-delay: 0s !important;
    scroll-behavior: auto !important;
  }

  /* The shell's chip cross-fade is tied to the operator's own scroll,
     not to a clock, a scroll-driven animation with a near-zero
     duration would misread the range. Keep it on the timeline; each
     keyframe still lands on its final frame. */
  .ma-ff-chip-host .ma-ff-chip--large,
  .ma-ff-chip-host .ma-ff-chip--slim,
  .ma-page-filter-bar::after {
    animation-duration: auto !important;
  }

  /* The busy spinner is the one thing on screen whose whole job is
     to say "still working". Slow it down; don't stop it. */
  .ma-spinner,
  .ma-spinner-inline {
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }

  /* Zuzo's thinking dots end their cycle at opacity 0, collapsed,
     they would vanish. Show all three, still. */
  .ma-thinking__dots span {
    animation: none !important;
    opacity: 1 !important;
  }

  /* The thinking verb is a moving gradient clipped to the text.
     Frozen mid-sweep it is a guess; paint it as plain ink instead. */
  .ma-thinking__verb {
    animation: none !important;
    background: none !important;
    -webkit-text-fill-color: var(--ma-ink-2) !important;
    color: var(--ma-ink-2) !important;
  }
}

/* ── MAPrint, the sheet that turns this view into a page of paper ────
   Built on the standard bottom-sheet atoms (.modal-overlay / .modal-sheet
   / .modal-header / .modal-body / .modal-footer), so only the pieces that
   are actually new live here: the paper miniature, the switch rows and
   the money segmented's caption. The miniature is drawn from the same
   spec the paper is, the operator watches the rands leave the page they
   are about to make, not a picture of one. */
.ma-print-btn { color: var(--ma-ink-2); }
.ma-print__sheet { max-height: min(92vh, var(--ma-sheet-max-h)); }
.ma-print__head { border-bottom: 1px solid var(--ma-border); }
.ma-print__titlerow { display: flex; align-items: center; gap: var(--ma-sp-3); }
.ma-print__titlerow h2 { flex: 1; margin: 0; font: 800 17px var(--ma-font-body); letter-spacing: -0.3px; }
.ma-print__x {
  width: 30px; height: 30px; flex: 0 0 auto; border: 0; border-radius: var(--ma-radius-sm);
  background: var(--ma-surface); box-shadow: inset 0 0 0 1px var(--ma-border);
  color: var(--ma-ink-2); display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
}
.ma-print__x svg { width: 15px; height: 15px; }
.ma-print__chips { display: flex; flex-wrap: wrap; gap: var(--ma-sp-1); margin-top: var(--ma-sp-2); }
.ma-print__tag {
  display: inline-flex; align-items: center; gap: 5px; border-radius: var(--ma-radius-pill);
  padding: 4px 9px; font: 700 11px var(--ma-font-body);
  background: var(--ma-border); color: var(--ma-ink-2);
}
.ma-print__tag--forest { background: var(--ma-forest-soft); color: var(--ma-forest-ink); }
/* The safe page's own mark, gold is what the paper's badge wears. */
.ma-print__tag--gold { background: var(--ma-gold-soft); color: var(--ma-gold-ink); }
.ma-print__body { padding: var(--ma-sp-5) var(--ma-sp-6) var(--ma-sp-1); }
.ma-print__lbl {
  margin: var(--ma-sp-5) 0 var(--ma-sp-2);
  font: 700 11px var(--ma-font-body); letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--ma-ink-3);
}
.ma-print__seg { margin: 0; }
.ma-print__note { margin: var(--ma-sp-2) 0 0; font-size: 12px; line-height: 1.45; color: var(--ma-ink-2); }
/* Who else can reach this page, the operator is deciding who may see
   the money two lines above, so the answer belongs next to that. Quieter
   than the money note: it is reassurance, not a choice to make. */
.ma-print__note--priv { margin-top: var(--ma-sp-3); color: var(--ma-ink-3); font-size: 11.5px; }
.ma-print__foot { border-top: 1px solid var(--ma-border); }
.ma-print__foot .ma-btn { flex: 1; }
/* The desk tier's two extra ways out (FE-15): the same sheet, the same
   PDF, beside the one button every width has. They sit above the footer
   row and are drawn only when the flag and the width say so, so a phone
   renders the sheet it always did. */
.ma-print__desk { display: flex; gap: var(--ma-sp-2); padding: var(--ma-sp-3) var(--ma-sp-6) 0; }
.ma-print__desk .ma-btn { flex: 1; }

/* The paper, small. */
.ma-print__pv { display: flex; gap: var(--ma-sp-4); align-items: flex-start; }
.ma-print-pv {
  width: 130px; height: 150px; flex: 0 0 auto; position: relative; overflow: hidden;
  background: #FFFFFF; border: 1px solid var(--ma-border-strong); border-radius: 3px;
  box-shadow: 0 4px 14px rgb(31 45 42 / 16%);
}
.ma-print-pv__rule { height: 4px; background: var(--ma-brick); }
.ma-print-pv__in { padding: 8px 9px; }
.ma-print-pv__kick { font: 800 5px var(--ma-font-body); letter-spacing: 0.7px;
                     text-transform: uppercase; color: #756B62; }
.ma-print-pv__t { font: 800 11px var(--ma-font-body); letter-spacing: -0.3px; color: #1F2D2A;
                  margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ma-print-pv__d { font-size: 6px; color: #46514C; margin-top: 2px;
                  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ma-print-pv__n { font: 800 19px/1 var(--ma-font-num); letter-spacing: -0.7px; color: #1F2D2A;
                  margin-top: 7px; white-space: nowrap; }
.ma-print-pv__m { font: 800 11px var(--ma-font-num); color: #1F2D2A; margin-top: 4px; white-space: nowrap; }
.ma-print-pv__nom {
  display: inline-block; margin-top: 4px; padding: 2px 5px; border-radius: var(--ma-radius-pill);
  font: 800 6px var(--ma-font-body); color: #6B4A0C; background: #FBE9B8;
  box-shadow: inset 0 0 0 1px #E8B33E; white-space: nowrap;
}
.ma-print-pv__bars { display: flex; align-items: flex-end; gap: 2px; height: 24px; margin-top: 7px; }
.ma-print-pv__bars span { flex: 1; border-radius: 1px; }
.ma-print-pv__rows { display: flex; flex-direction: column; gap: 5px; margin-top: 9px; }
.ma-print-pv__rows span { display: flex; align-items: center; gap: 5px; }
.ma-print-pv__rows i { height: 3px; border-radius: 2px; background: #D8D0C4; }
.ma-print-pv__rows b { height: 3px; width: 15px; border-radius: 2px; background: #1F2D2A; margin-left: auto; }
.ma-print-pv__rows b.ma-print-pv__tick--off { width: 9px; background: #B4AB9E; }
.ma-print-pv__fade {
  position: absolute; left: 0; right: 0; bottom: 0; height: 26px;
  background: linear-gradient(to bottom, rgb(255 255 255 / 0%), #FFFFFF);
}
.ma-print__pvside { flex: 1; min-width: 0; }
.ma-print__what { font: 800 14px var(--ma-font-body); color: var(--ma-ink); }
.ma-print__sub { font-size: 12px; color: var(--ma-ink-2); margin-top: 3px; line-height: 1.45; }
.ma-print__meta { display: flex; flex-wrap: wrap; gap: var(--ma-sp-1); margin-top: 9px; }

/* What goes in, the switch rows. */
.ma-print__rows {
  background: var(--ma-surface); border: 1px solid var(--ma-border);
  border-radius: var(--ma-radius-lg); overflow: hidden;
}
.ma-print__row {
  display: flex; align-items: center; gap: var(--ma-sp-3);
  width: 100%; padding: 9px var(--ma-sp-4); min-height: 44px;
  border: 0; border-top: 1px solid var(--ma-border); background: none;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-print__row:first-child { border-top: none; }
.ma-print__row:active { opacity: var(--ma-press-fade); }
.ma-print__rowtxt { flex: 1; min-width: 0; display: block; }
.ma-print__rownm { display: block; font: 700 13.5px var(--ma-font-body); color: var(--ma-ink); }
.ma-print__rowsub { display: block; font-size: 11.5px; color: var(--ma-ink-3); margin-top: 1px; }
/* The shared .ma-toggle is a <label> wrapping a checkbox; these rows are
   buttons carrying aria-pressed, so this is that switch's look on a
   button, not a second switch. */
.ma-print__sw {
  position: relative; width: 44px; height: 26px; flex: 0 0 auto; border-radius: var(--ma-radius-pill);
  border: 1.5px solid var(--ma-border-strong); background: var(--ma-surface);
}
.ma-print__sw span {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--ma-border-strong); transition: transform 0.18s var(--ma-ease), background 0.18s;
}
.ma-print__sw--on { border-color: var(--ma-forest); background: var(--ma-forest-soft); }
.ma-print__sw--on span { transform: translateX(18px); background: var(--ma-forest); }

.ma-print__warn {
  display: flex; gap: 9px; align-items: flex-start; margin-top: var(--ma-sp-5);
  background: var(--ma-brick-soft); border: 1px solid var(--ma-brick);
  border-radius: var(--ma-radius-lg); padding: var(--ma-sp-3) var(--ma-sp-4);
  color: var(--ma-brick-ink); font-size: 12px; line-height: 1.45;
}
.ma-print__warn svg { width: 16px; height: 16px; flex: 0 0 auto; color: var(--ma-brick); margin-top: 1px; }

/* ── MASetup, the shoebox as four shelves ─────────────────────────
   Each shelf is one MABatchCard under a coverage strip. The strip is
   the only thing drawn here that the batch card cannot know: how much
   of this shelf is answered, how that was worked out, and the gap in
   rands. One hue on the bar, a shelf is not a category, and colouring
   four of them differently would say they are four kinds of thing when
   they are four sizes of the same job.

   A shelf nobody measured draws no bar at all (`bar: null`), only its
   words, so an empty track never reads as a measured zero. */
.ma-setup-head { margin-bottom: 14px; }

.ma-setup-shelf { margin-bottom: 22px; }

.ma-setup-shelf .ma-section-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}

.ma-setup-shelf-sub {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ma-ink-3, #8A8580);
}

.ma-setup-cover {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  margin-bottom: 9px;
}

.ma-setup-bar {
  flex: 1 1 120px;
  min-width: 110px;
  height: 6px;
  border-radius: 999px;
  background: var(--ma-border, #E7E2DA);
  overflow: hidden;
}

.ma-setup-bar > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--ma-forest, #2F5D50);
  transition: width var(--ma-dur-grow, .32s) var(--ma-ease, cubic-bezier(.2,.8,.2,1));
}

.ma-setup-cover-n {
  font-family: var(--ma-font-num, "Space Grotesk", system-ui, sans-serif);
  font-size: 13px;
  font-weight: 600;
  color: var(--ma-ink-2, #5C5852);
  font-variant-numeric: tabular-nums;
}

.ma-setup-cover-said {
  flex: 1 1 100%;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ma-ink-3, #8A8580);
}

.ma-setup-empty {
  padding: 26px 16px;
  text-align: center;
  font-size: 14px;
  color: var(--ma-ink-3, #8A8580);
}

/* ── The row on the paper (MAReceiptCrop) ─────────────────────────────
   A window onto the receipt photo, showing only the printed line a card
   is asking about. The height is set by JS once the photo reports its
   pixel size, until then the frame stands at a line's worth so the card
   does not jump. `overflow: hidden` is the crop; the image inside is
   scaled and shifted, never resized to fit. */
.ma-crop {
  position: relative;
  overflow: hidden;
  width: 100%;
  border-radius: 10px;
  border: 1px solid var(--ma-border);
  background: var(--ma-surface);
  margin: 10px 0 2px;
}
.ma-crop::after {                     /* the skeleton, until the photo lands */
  content: ""; position: absolute; inset: 0;
  background: var(--ma-skel-bg);
  animation: ma-pulse var(--ma-pulse-skel) ease-in-out infinite;
}
.ma-crop[data-ma-crop-ready]::after { content: none; }
.ma-crop__img {
  position: absolute; top: 0; left: 0;
  max-width: none;                    /* the global img rule would undo the crop */
  transform-origin: top left;
}
.ma-crop__note {
  font-size: 11px; color: var(--ma-ink-3); margin: 0 0 6px;
}

/* ─────────────────────────────────────────────────────────────
   MALines, every loose line across every slip, grouped by likeness.

   A list, not Today's decision cards: the operator is scanning for
   likeness, so a row is one line of text with its money down the
   right-hand edge and its word beside it, and the decision happens in
   MALineSheet over it. The folds are MAAccordion's, so a pile left open
   is open on the next visit.
   ───────────────────────────────────────────────────────────── */
.ma-lines-pick {
  display: flex; align-items: center; gap: 6px;
  /* Horizontal scroll is right here and wrong on a tab strip: these are
     a filter's options, not the app's navigation, and the label anchored
     at the left says what the row is even when it is scrolled. */
  overflow-x: auto;
  scrollbar-width: none;
  padding: 6px 0;
}
.ma-lines-pick::-webkit-scrollbar { display: none; }
.ma-lines-pick__l {
  flex: 0 0 auto;
  font: 700 10.5px var(--ma-font-body);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--ma-ink-3);
  padding-right: 2px;
}
.ma-lines-pick .ma-pill { flex: 0 0 auto; }

.ma-lines-group {
  /* Matches the 10px its rows carry. */
  --ma-acc-inset: 10px;
  background: var(--ma-cream);
  border: 1px solid var(--ma-border);
  border-radius: 14px;
  overflow: hidden;
  margin-bottom: 10px;
}
.ma-lines-group__body {
  display: flex; flex-direction: column;
  border-top: 1px solid var(--ma-border);
}

/* The head says four things, what the pile is, whether there is a job
   in it, where and when it came from, and how much money is behind it.

   MAAccordion.head() wraps whatever it is given in `.ma-acc__l`, which is
   a nowrap block and not a flex container, so the title column and the
   amount stacked, and the money came out under the subline instead of on
   the right edge. Flexing that one wrapper here (scoped to this
   component, never to `.ma-acc__l` at large) puts them back on one row. */
.ma-lines-group > .ma-acc__h > .ma-acc__l {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 8px;
}
.ma-lines-g { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: 1px; }
.ma-lines-g__t {
  font: 800 13.5px var(--ma-font-body);
  letter-spacing: 0; text-transform: none;
  color: var(--ma-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-lines-g__m {
  font: 400 11px var(--ma-font-body);
  letter-spacing: 0; text-transform: none;
  color: var(--ma-ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-lines-g__amt {
  flex: 0 0 auto;
  font: 800 13.5px var(--ma-font-num);
  letter-spacing: 0; text-transform: none;
  color: var(--ma-ink);
  font-variant-numeric: tabular-nums;
}

.ma-lines-row {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px;
  border-top: 1px solid var(--ma-border);
}
.ma-lines-row:first-child { border-top: 0; }
.ma-lines-row__main {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1; min-width: 0;
  background: none; border: 0; padding: 0;
  text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  color: inherit; font: inherit;
}
.ma-lines-row__top { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.ma-lines-row__name {
  flex: 1; min-width: 0;
  font: 600 13px var(--ma-font-body);
  color: var(--ma-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-lines-row__amt {
  flex: 0 0 auto;
  font: 700 13px var(--ma-font-num);
  color: var(--ma-ink);
  font-variant-numeric: tabular-nums;
}
.ma-lines-row__sub {
  font: 400 11px var(--ma-font-body);
  color: var(--ma-ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-lines-row__word { flex: 0 0 auto; }
.ma-lines-row__slip {
  flex: 0 0 auto;
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 1px solid var(--ma-border);
  border-radius: 9px;
  color: var(--ma-ink-3); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ma-lines-row__slip svg { width: 15px; height: 15px; }

/* The read ran out of time on some sizes. Gold, not red: the figures are
   worse, not wrong, and the operator can fix it by narrowing the range. */
.ma-lines-partial {
  margin: 4px 0 10px;
  padding: 9px 11px;
  border: 1px solid var(--ma-gold-line);
  border-radius: 11px;
  background: var(--ma-gold-wash);
  font: 500 12px var(--ma-font-body);
  color: var(--ma-ink);
}

.ma-lines-empty,
.ma-lines-more {
  padding: 18px 10px;
  text-align: center;
  font: 400 12.5px var(--ma-font-body);
  color: var(--ma-ink-3);
}

/* ─────────────────────────────────────────────────────────────
   MAPhotoZoom, the slip, big enough to read.

   Its own layer rather than a `.modal-overlay`: a bottom sheet stops
   below Telegram's control strip and leaves a strip of page showing,
   which is right for a form and wrong for a photograph. This takes the
   whole screen, because the photograph IS the answer.

   Zoom is the image's own width and never a transform, a transformed
   image does not make its container scrollable, so panning would have
   needed a gesture engine of its own. Setting the width lets the phone
   do the panning, and the pixels stay sharp.
   ───────────────────────────────────────────────────────────── */
.ma-pz {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; flex-direction: column;
  /* Opaque, not a scrim. A bright cream list showing through behind a
     photograph of a slip is exactly the contrast the reader came here to
     get away from, and at .985 a white page still ghosted through it on
     a real phone. There is nothing behind this worth seeing. */
  background: #111614;
}
.ma-pz__head {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  /* In full-screen Telegram floats its own "< Back" pill over our top
     left and the collapse + menu pill over our top right. Clear the whole
     strip rather than share it: this row carries a shop name that would
     be cut in half and a Close that would sit under the kebab. Shipped
     without this, and the first real phone showed both. */
  padding: calc(16px + var(--ma-top-inset)) 14px 10px;
}
.ma-pz__who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ma-pz__title {
  font-family: var(--ma-font-body); font-size: 14px; font-weight: 800;
  color: var(--ma-surface, #FFFDF8);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ma-pz__sub { font-size: 11px; color: rgba(255, 253, 248, .6); }
.ma-pz__x {
  flex: 0 0 auto; width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 12px; border: 1px solid rgba(255, 253, 248, .38);
  background: #2C3532; color: #FFFDF8;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.ma-pz__x svg { width: 18px; height: 18px; }
/* The stage scrolls, which is how a zoomed slip pans: the phone's own
   scrolling, so it never fights the gesture. */
.ma-pz__stage {
  position: relative;      /* the drawn box is placed in content pixels */
  flex: 1; min-height: 0; overflow: auto;
  display: flex; align-items: center; justify-content: center;
  padding: 8px 14px;
  -webkit-overflow-scrolling: touch;
  /* One finger pans. Two is ours, the pinch handler takes that one. */
  touch-action: pan-x pan-y;
  /* Without this a pan that reaches an edge chains to the page behind, so
     the list scrolls under the photograph and the drag reads as fighting
     back. The other half of that fix locks the body while this is open. */
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.ma-pz__stage::-webkit-scrollbar { display: none; }
/* Zoomed in, the picture is bigger than the stage, and centring it would
   fight the scroll. */
.ma-pz.is-zoomed .ma-pz__stage { align-items: flex-start; justify-content: flex-start; }
.ma-pz__img {
  display: block; height: auto; max-width: none; flex: 0 0 auto;
  /* A drag on the slip is a drawing, never the browser's own image drag. */
  -webkit-user-drag: none; user-select: none;
  border-radius: 6px; box-shadow: 0 18px 44px rgba(0, 0, 0, .5);
  /* The rail's steps are discrete, so they ease. A pinch is not: the
     fingers ARE the animation, and easing every frame of one made the
     slip lag them and stutter, the "not smooth" this was reported as.
     `is-live` is on for the length of a pinch and nothing else. */
  transition: width var(--ma-dur-swap) var(--ma-ease);
}
.ma-pz.is-live .ma-pz__img { transition: none; }
.ma-pz__none {
  margin: auto; padding: 24px; text-align: center;
  font-size: 12.5px; color: rgba(255, 253, 248, .7);
}
/* The lasso, RCP-1: draw a box on the slip and Malini reads only that.
   Panning stops for the length of the drag, so the box is the gesture. */
.ma-pz.is-drawing .ma-pz__stage { touch-action: none; cursor: crosshair; }
.ma-pz__draw {
  position: absolute; box-sizing: border-box; pointer-events: none;
  border: 2px solid var(--ma-zuzo); border-radius: 4px;
  background: color-mix(in srgb, var(--ma-zuzo) 22%, transparent);
}
/* The ring, RCP-5: where on the slip a value was read from. Placed in the
   slip's own content pixels, like the lasso's box, so it scrolls with the
   photograph. Colour means STATE and never how sure the reader is: grey for
   a value read off the paper, green for one a person has checked, red for
   one that blocks the line. A green box that meant "the reader is
   confident" is what taught reviewers to skip green boxes. */
.ma-pz__ring {
  position: absolute; box-sizing: border-box; pointer-events: none;
  border-radius: 4px;
  /* Two edges, not one: the coloured ring reads on a pale slip, and the
     near-black keyline under it keeps it visible on a dark photo. */
  box-shadow: 0 0 0 1px rgba(17, 22, 20, .5);
  transition: left var(--ma-dur-swap) var(--ma-ease),
              top var(--ma-dur-swap) var(--ma-ease),
              width var(--ma-dur-swap) var(--ma-ease),
              height var(--ma-dur-swap) var(--ma-ease),
              border-color var(--ma-dur-swap) var(--ma-ease),
              background var(--ma-dur-swap) var(--ma-ease);
}
.ma-pz__ring--read {
  border: 2px solid var(--ma-ring-read);
  background: color-mix(in srgb, var(--ma-ring-read) 18%, transparent);
}
.ma-pz__ring--checked {
  border: 2px solid var(--ma-ring-checked);
  background: color-mix(in srgb, var(--ma-ring-checked) 18%, transparent);
}
.ma-pz__ring--blocks {
  border: 2px solid var(--ma-ring-blocks);
  background: color-mix(in srgb, var(--ma-ring-blocks) 18%, transparent);
}
/* A pinch is not eased either: the ring must grow with the fingers, not
   trail them. */
.ma-pz.is-live .ma-pz__ring { transition: none; }
/* A pan glides, so the ring is seen travelling to where it was read; the
   class is added for the length of that glide and nothing else, because a
   native drag-pan must stay 1:1 under the finger. */
.ma-pz__stage.is-panning { scroll-behavior: smooth; }
.ma-pz__lasso {
  flex: 0 0 auto; display: flex; flex-direction: column; gap: 8px;
  padding: 0 14px 6px;
}
.ma-pz__lasso__who { font-size: 12.5px; line-height: 1.4; color: rgba(255, 253, 248, .8); }
.ma-pz__lasso__acts { display: flex; align-items: center; gap: 10px; }
.ma-pz__lasso__acts .ma-pz__btn--wide { flex: 1; }
.ma-pz__btn:disabled { opacity: .45; }
.ma-pz__rail {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  gap: 10px;
  padding: 10px 14px calc(24px + max(var(--ma-inset-bottom), env(safe-area-inset-bottom, 0px)));
}
.ma-pz__btn {
  width: 48px; height: 48px;
  display: inline-flex; align-items: center; justify-content: center;
  /* A solid chip, not a 10%-white wash. These sit against a near-black
     ground and sometimes over the photograph itself, and on a real phone
     the wash did not read at all, which is what "the buttons are hiding
     behind others" meant. */
  border-radius: 14px; border: 1px solid rgba(255, 253, 248, .38);
  background: #2C3532; color: #FFFDF8;
  font-family: var(--ma-font-body); font-size: 13px; font-weight: 800;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.ma-pz__btn--wide { width: auto; min-width: 96px; padding: 0 14px; }
.ma-pz__btn svg { width: 20px; height: 20px; }


/* The album's photo is a target too, same promise as the list card's
   thumbnail, and the album is where the photo is already the screen. */
.ma-box-photo--live { padding: 0; border: 0; cursor: pointer; }


/* ──────────────────────────────────────────────────
   The desk palette (FE-14): Ctrl (or Cmd) and K on a computer.
   Its own switch (window.MA_DESK_PALETTE) and its own `.ma-palette*`
   family; the component builds no node below the desk tier, and the
   `html.desk-tier` gate is the rollback. It composes the shipped
   `.modal-overlay` atom, so the layer is the app's own.
   ────────────────────────────────────────────────── */
.ma-palette-overlay { align-items: flex-start; }
html.desk-tier .ma-palette-overlay.is-open { display: flex; }
.ma-palette {
  margin-top: 12vh; width: 100%; max-width: 560px;
  max-height: min(70vh, 560px); overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--ma-surface); color: var(--ma-ink);
  border: 1px solid var(--ma-border); border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .28);
}
.ma-palette__field {
  display: flex; align-items: center; gap: 10px; padding: 14px 16px;
  border-bottom: 1px solid var(--ma-border);
}
.ma-palette__label {
  flex: 0 0 auto; font-size: 11px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ma-ink-3);
}
.ma-palette__input {
  flex: 1; border: 0; background: transparent; color: var(--ma-ink);
  font-family: var(--ma-font-body); font-size: 16px; font-weight: 600; outline: none;
}
.ma-palette__list { overflow-y: auto; padding: 6px; }
.ma-palette__row {
  display: flex; flex-direction: column; gap: 2px; padding: 10px 12px;
  border-radius: 10px; text-decoration: none; color: var(--ma-ink);
}
.ma-palette__row--on, .ma-palette__row:hover { background: var(--ma-cream); }
.ma-palette__row-label { font-size: 14px; font-weight: 700; }
.ma-palette__row-sub { font-size: 12px; color: var(--ma-ink-3); }
.ma-palette__empty { padding: 18px 16px; font-size: 13px; color: var(--ma-ink-3); }
body.ma-palette-open { overflow: hidden; }


/* ─────────────────────────────────────────────────────────────
   The desk tier (FE-11): a rail down the left, Zuzo on the right.

   The founder's desktop handoff, built on FE-8's tiers. `html.desk-tier`
   is written by MAShell only while the server said window.MA_DESK_TIER
   AND the viewport is at the desk tier AND the page is not Sales or
   Staff, and it is a SEPARATE class from FE-8's `html.host-desk` so the
   two switches roll back independently. With the flag off no selector
   here can match, so the phone and the wide tier are untouched, and that
   is also the rollback: flip the flag back. Every pixel count that is a
   tier comes from --ma-bp-*; the rail and the dock are chrome widths,
   not tiers.

   The rail is fixed and the page body is padded past it, so a page keeps
   its own markup and its own scrolling; the bottom bar is hidden because
   the rail is the same list in another chrome. The docked Zuzo is the
   same panel with a second mount position, not a second chat.
   ───────────────────────────────────────────────────────────── */

/* The rail exists only when the shell builds it, and the shell builds it
   only with the flag on, so these rules are inert on every other host. */
[data-desk-rail] { display: none; }

.ma-desk-rail {
  display: flex;
  flex-direction: column;
  width: 212px;
  height: 100%;
  padding: 12px 10px;
  gap: 2px;
  background: var(--ma-surface);
  border-right: 1px solid var(--ma-border);
  overflow-y: auto;
}
.ma-desk-rail__brand {
  display: flex;
  align-items: center;
  height: 40px;
  padding: 0 10px 8px;
  text-decoration: none;
}
.ma-desk-rail__brand-dot {
  width: 22px; height: 22px;
  border-radius: 7px;
  background: var(--ma-accent);
}
.ma-desk-rail__item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 10px;
  border-radius: 10px;
  text-decoration: none;
  color: var(--ma-ink-3);
  font-size: 13px;
  font-weight: 600;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--ma-dur-swap) var(--ma-ease), background-color var(--ma-dur-swap) var(--ma-ease);
}
.ma-desk-rail__item:hover { background: var(--ma-cream); }
.ma-desk-rail__item--active { color: var(--ma-ink); background: var(--ma-cream); font-weight: 700; }
.ma-desk-rail__item--active.ma-desk-rail__item--accent-brick { color: var(--ma-brick); }
.ma-desk-rail__item--active.ma-desk-rail__item--accent-gold { color: var(--ma-gold-ink); }
.ma-desk-rail__item--active.ma-desk-rail__item--accent-forest { color: var(--ma-forest); }
.ma-desk-rail__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: 0 0 auto;
}
.ma-desk-rail__icon svg { width: 20px; height: 20px; }
.ma-desk-rail__label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

@media (min-width: 1100px) {
  /* The rail takes its own column and the bar steps aside. The rail is
     fixed chrome and the body is padded past it, so no page has to wrap
     its content or learn a grid. */
  html.desk-tier [data-desk-rail] {
    display: block;
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: 212px;
    z-index: 9100;               /* above the 9000 bar, below modals */
    padding-top: var(--ma-inset-top);
  }
  html.desk-tier body { padding-left: 212px; }
  /* The pad-back the slim filter row carries (a negative inline margin
     equal to the page's own gutter) must not pull the row left under the
     rail. At the desk tier the body's left padding IS the rail, so the
     row aligns to the content edge instead. */
  html.desk-tier .ma-page-filter-bar {
    margin-left: 0;
    margin-right: 0;
  }
  html.desk-tier .bottom-nav { display: none; }

  /* Zuzo, docked. The 324px panel is pinned to the right edge and stays
     open; the same panel, the same thread, the same propose-confirm-apply,
     only its mount position moved. The floating chip is hidden by the
     component, not by a rule here. */
  html.desk-tier body.ma-zuzo-docked { padding-right: 324px; }
  html.desk-tier .ma-ask-zuzo-panel--docked {
    position: fixed;
    top: 0; right: 0; bottom: 0;
    left: auto;
    width: 324px;
    height: auto;
    max-height: none;
    border-radius: 0;
    border-left: 1px solid var(--ma-border);
    box-shadow: none;
    transform: none;
    pointer-events: auto;
  }
  /* Below the width the dock needs, the panel falls back to the slide-up
     it already is: MAShell asks MABreakpoints.hasRoomForDock() and never
     adds the docked class, so there is nothing to undo here. */
}

@media (min-width: 1440px) {
  /* The cap: the work surface stops growing and the rail and the dock
     keep their fixed columns, so a 2560px monitor gets the same reading
     measure as a laptop. The cap is 1440 minus the chrome the desk tier
     actually took: always the rail, and the dock when Zuzo is pinned. */
  html.desk-tier body .ma-cols,
  html.desk-tier body .ma-app-header,
  html.desk-tier body .ma-tabs {
    max-width: calc(var(--ma-bp-cap) - 212px);
    margin-inline: auto;
  }
  html.desk-tier body.ma-zuzo-docked .ma-cols,
  html.desk-tier body.ma-zuzo-docked .ma-app-header,
  html.desk-tier body.ma-zuzo-docked .ma-tabs {
    max-width: calc(var(--ma-bp-cap) - 212px - 324px);
  }
}


/* ─────────────────────────────────────────────────────────────
   The wide tier (FE-8): the width Telegram Desktop already gives us.

   A Mini App opens maximised in a medium window on Telegram Desktop,
   so every operator on a laptop is looking at a 390px column in a
   900px frame today. Everything in this block is inside a media query
   at the FE-7 tiers, and every one of them is additionally gated on
   body[data-width-tier], which MAShell writes only while the server
   said window.MA_WIDE_TIER. With the flag off (the default) no
   selector here can match, so the phone layout is byte-for-byte the
   one that shipped, and that is also the rollback: flip the flag back,
   or delete this block.

   Three numbers, one rule. The pixel counts come from --ma-bp-* (FE-7)
   and no other literal appears below, so the tiers stay declared once.
   Sales and Staff never take the attribute at all (MAShell excludes
   them), so a frozen screen keeps its layout even with the flag on.
   ───────────────────────────────────────────────────────────── */

@media (min-width: 700px) {
  /* Two columns where content stacks, and a shell that centres in the
     room it is given rather than stretching a 390px column to 900.
     `minmax(0, 1fr)` is load-bearing: without the 0 a long unbroken
     number widens its own track and the page scrolls sideways, which
     is exactly the failure the generator's check_width asserts. */
  body[data-width-tier] .ma-cols {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--ma-sp-6);
    align-items: start;
  }
  body[data-width-tier] .ma-cols > * { min-width: 0; }

  /* A bottom sheet dragged to 1200px wide is the clearest sign nobody
     looked at the app on a laptop. Every sheet in the app already caps
     at 560px; above the wide tier they also centre instead of hugging
     the bottom edge, and they stop needing the rounded top corners
     that exist to read as "dragged up from below". */
  body[data-width-tier] .modal-overlay,
  body[data-width-tier] .ma-picker-overlay {
    align-items: center;
  }
  body[data-width-tier] .modal-sheet {
    border-radius: var(--ma-radius-2xl);
    max-height: min(85vh, var(--ma-sheet-max-h));
  }
  body[data-width-tier] .ma-ff-sheet {
    border-radius: var(--ma-radius-2xl);
    max-height: min(85vh, var(--ma-sheet-max-h));
  }
  body[data-width-tier] .ma-ff-handle { display: none; }
}

@media (min-width: 1100px) {
  /* The desk tier. The content stops growing at --ma-bp-cap and centres
     inside .host-page, so a 2560px monitor gets the same reading
     measure as a laptop and not a 2-metre card. The cap is applied to
     the page body, not to the fixed chrome (nav, sheets, snack), which
     must keep spanning the window. */
  html.host-desk body > .host-page {
    max-width: var(--ma-bp-cap);
    margin-inline: auto;
    width: 100%;
  }
}

/* The installed PWA host's display face (FE-13). A HOST rule, not a
   width rule: at every width the installed app sets html.host-app and
   --ma-font-display becomes Fraunces; inside Telegram the class is never
   written, so no rule references the family and the browser never fetches
   it. Written only while the server said window.MA_PWA_DISPLAY_FONT, so
   the flag off (or deleting this block) puts every host back on the
   shared pair. */
html.host-app {
  --ma-font-display: 'Fraunces', var(--ma-font-num);
}
