/* ============================================================================
   mm-foundation.css — MailMoolah semantic adapter (MM-01, 2026-08-08)
   ============================================================================

   WHAT THIS IS
   A THIN SEMANTIC ADAPTER. It aliases meaning onto the palette that
   SHARED_DASHBOARD already ships, and supplies the four domain primitives that
   neither Tailwind nor the shell provides (status chip, state panel, context
   head, dialog/drawer chrome).

   WHAT THIS IS NOT
   Not a design system. There is deliberately NO grid system, NO type scale, NO
   button library, NO spacing scale and NO colour ramp here. Tailwind owns
   layout/typography/utilities; SHARED_DASHBOARD owns shell geometry, the global
   header and the dashboard embed context. If you are about to add `.mm-btn`,
   `.mm-col-6` or `.mm-text-lg`, stop — that belongs to one of the two owners
   above.

   SERVED AT  /css/mm-foundation.css
   (backend/app/main.py mounts frontend/css → /css). Load it AFTER Tailwind and
   AFTER /SHARED_DASHBOARD/css/header-embed.css so the derivations below resolve
   against the shell's live values rather than only against the fallbacks.

   ---------------------------------------------------------------------------
   DERIVATION POLICY — why some values are `var(--he-…)` and some are literals
   ---------------------------------------------------------------------------
   The shell's `--he-*` / `--dash-*` / `--icon-*` tokens declare their DARK
   values in `:root` and only correct themselves to light inside
   `[data-theme="light"]`. 51 of MailMoolah's 77 active pages stamp no theme at
   all (MM-00 §G.1), so on those pages a bare `var(--he-text-primary)` resolves
   to the DARK `#f1f5f9` — invisible on a light surface. Therefore:

     * theme-INDEPENDENT tokens (touch floor, radius) derive unconditionally —
       they are correct in every theme. NOTE: `--he-shadow` is NOT one of them.
       It is theme-dependent (header-embed.css:25 dark 0.4 alpha / :58 light 0.1),
       so `--mm-shadow-overlay` is derived per-theme like the colours below.
       Measured on an unstamped page: deriving it unconditionally resolved to the
       DARK 0.4-alpha shadow while `--mm-surface` was #ffffff;
     * DARK tokens derive from the shell, because the shell's `:root` default IS
       the dark value, so the derivation is right whether or not header-embed.css
       is loaded;
     * the `[data-theme="light"]` block derives, because there the shell's light
       overrides are guaranteed to be in effect;
     * the base `:root` block (the unstamped-page default) uses light literals —
       these are the only invented values, and they are chosen from the same
       Tailwind slate/violet/emerald/amber/red/cyan ramps the shell uses.

   The `--icon-*` accents are additionally re-tuned for light: the shell's ramp
   (#22c55e, #f59e0b, …) is authored for dark chrome and fails 4.5:1 as text on
   a white surface.

   ---------------------------------------------------------------------------
   SEMANTIC COLOUR CONTRACT (MailMoolah UI spec §"Semantic visual language")
   ---------------------------------------------------------------------------
     brand    violet   MailMoolah, selection, normal primary action
     verified emerald  verified / approved / successful / genuinely available
                       — ONLY when persisted state confirms it
     review   amber    needs review / held / delayed / pilot-managed
     blocked  red      failed / blocked / destructive / complaint / safety stop
     info     cyan     neutral information and measured data (NOT the default CTA)
     neutral  slate    cards, tables, filters, dialogs, content

   COLOUR NEVER CARRIES MEANING ALONE. Every primitive here has a mandatory text
   slot and an icon slot; nothing in this file communicates state by hue only.

   ---------------------------------------------------------------------------
   UNITS
   ---------------------------------------------------------------------------
   ZERO authored px (DEV_GUIDE #1). Hairlines are `0.0625rem` (= 1px at the
   default root size) — the same convention frontend/css/help-sidebar.css
   already uses. Media queries are in rem. Mobile-first from 22.5rem (360px).

   THEME SIGNALS honoured — all four stamping mechanisms MM-00 §G.1 found in the
   wild, plus the unstamped majority:
     html.dark (5 pages) · body.dark (4) · html[data-theme="dark"] (22)  → dark
     html[data-theme="light"] (3)                                       → light
     no stamp at all (51)                          → the :root light literals
   A `prefers-color-scheme` auto-switch is deliberately NOT wired: 51 pages have
   no theme stamp and are otherwise light, so auto-darkening only these tokens
   would produce exactly the mixed-theme surface hard gate #11 forbids.
   ============================================================================ */

/* ---------------------------------------------------------------------------
   1. TOKENS — light (base; also the unstamped-page default)
   --------------------------------------------------------------------------- */
:root {
  /* Structure — theme-independent, derived unconditionally from the shell.
     The touch floor is CLAMPED, not merely aliased. Both shell tokens are
     2.75rem today (dashboard-shell.css:69, header-embed.css:32), so max() is a
     no-op right now — but DEV_GUIDE #6 is a hard gate on MailMoolah, and the
     value it depends on lives in SHARED_DASHBOARD: a repo MailMoolah does not
     own, shares with 29 sites, and is forbidden to edit. Without max(), a
     future tightening of the shell header (e.g. to 2.5rem) would silently drop
     every MailMoolah touch target below the gate with nothing failing. */
  --mm-touch-min: max(2.75rem, var(--he-touch-min, var(--touch-min, 2.75rem)));
  --mm-radius: var(--he-radius-lg, var(--radius-lg, 0.75rem));
  --mm-radius-sm: var(--he-radius-sm, 0.375rem);
  --mm-hairline: 0.0625rem;

  /* Motion — ONE easing family, 150–300ms (DEV_GUIDE #14). */
  --mm-ease: cubic-bezier(0.2, 0, 0, 1);
  --mm-dur-fast: 150ms;
  --mm-dur: 200ms;

  /* Elevation — reserved for menus, dialogs, drawers, raised layers only.
     AUTHORED here, not derived: `--he-shadow` is theme-dependent, and on the 51
     unstamped pages the shell's `:root` value is the DARK 0.4-alpha shadow on a
     white surface. The two stamped branches below derive it. */
  --mm-shadow-overlay: 0 0.5rem 1.5rem rgba(15, 23, 42, 0.18);

  /* Neutral operational surfaces — opaque, never translucent. */
  --mm-surface: #ffffff;
  --mm-surface-sunken: #f8fafc;
  --mm-border: #e2e8f0;
  --mm-text: #0f172a;
  --mm-text-secondary: #475569;
  --mm-text-muted: #64748b;

  /* Semantic states: -accent = icon/rule, -fg = text on -surface, -surface = opaque tint. */
  --mm-brand: #7c3aed;
  --mm-brand-fg: #5b21b6;
  --mm-brand-surface: #f5f3ff;

  --mm-status-verified: #059669;
  --mm-status-verified-fg: #065f46;
  --mm-status-verified-surface: #ecfdf5;

  --mm-status-review: #d97706;
  --mm-status-review-fg: #92400e;
  --mm-status-review-surface: #fffbeb;

  --mm-status-blocked: #dc2626;
  --mm-status-blocked-fg: #991b1b;
  --mm-status-blocked-surface: #fef2f2;

  --mm-status-info: #0e7490;
  --mm-status-info-fg: #155e75;
  --mm-status-info-surface: #ecfeff;

  /* Focus ring — ≥3:1 against both --mm-surface and --mm-surface-sunken. */
  --mm-focus: #7c3aed;
}

/* Explicit light stamp: the shell's light overrides are in force here, so the
   neutrals become a live link to the shell instead of a parallel palette. */
:root[data-theme="light"] {
  --mm-surface: var(--he-bg, #ffffff);
  --mm-surface-sunken: var(--he-bg-elevated, #f8fafc);
  --mm-text: var(--he-text-primary, #0f172a);
  --mm-text-secondary: var(--he-text-secondary, #475569);
  --mm-shadow-overlay: var(--he-shadow, 0 0.5rem 1.5rem rgba(15, 23, 42, 0.18));
}

/* ---------------------------------------------------------------------------
   2. TOKENS — dark. The shell's `:root` defaults ARE the dark values, so these
   derivations are correct whether or not header-embed.css is on the page.
   --------------------------------------------------------------------------- */
html.dark,
:root[data-theme="dark"],
body.dark {
  --mm-surface: var(--he-bg-elevated, #1f2a40);
  --mm-surface-sunken: var(--dash-bg-secondary, #111827);
  --mm-border: var(--he-border, rgba(255, 255, 255, 0.08));
  --mm-text: var(--he-text-primary, #f1f5f9);
  --mm-text-secondary: var(--he-text-secondary, #94a3b8);
  --mm-shadow-overlay: var(--he-shadow, 0 0.5rem 1.5rem rgba(0, 0, 0, 0.4));
  /* NOT derived from --dash-text-tertiary (#64748b): measured 3.02:1 against
     --mm-surface in dark, which fails AA for the 0.75rem eyebrow/metadata text
     this token is for. Lightened to the smallest value that clears 4.5:1. */
  --mm-text-muted: #8b99ad;

  --mm-brand: var(--icon-purple, #a855f7);
  --mm-brand-fg: #ddd6fe;
  --mm-brand-surface: #241b3d;

  --mm-status-verified: var(--icon-green, #22c55e);
  --mm-status-verified-fg: #a7f3d0;
  --mm-status-verified-surface: #0d2b22;

  --mm-status-review: var(--icon-amber, #f59e0b);
  --mm-status-review-fg: #fde68a;
  --mm-status-review-surface: #33240c;

  --mm-status-blocked: var(--icon-rose, #f87171);
  --mm-status-blocked-fg: #fecaca;
  --mm-status-blocked-surface: #3a1618;

  --mm-status-info: var(--icon-cyan, #06b6d4);
  --mm-status-info-fg: #a5f3fc;
  --mm-status-info-surface: #0c2b33;

  --mm-focus: #a78bfa;
}

/* ---------------------------------------------------------------------------
   2b. BOX MODEL — every primitive here sizes itself border-box.
   Scoped to mm-* only (no global `*` reset — that is Tailwind preflight's job
   and re-declaring it would be a second authority). This is not belt-and-braces:
   measured at 360px WITHOUT preflight, `.mm-dialog` computed 329.3px wide
   inside a 328 allowance because the two hairline borders sat outside the
   declared width. Pages that do not load Tailwind preflight are real here
   (MM-00 §B.2: 18 pages ship their own chrome).
   `.mm-surface` is in this list for the same reason: it is the card primitive,
   it carries a border, and an author who gives it an explicit width on a
   preflight-less page would otherwise overflow by exactly two hairlines.
   --------------------------------------------------------------------------- */
.mm-surface,
.mm-chip,
.mm-state,
.mm-state__icon,
.mm-context-head,
.mm-dialog,
.mm-dialog__header,
.mm-dialog__body,
.mm-dialog__footer,
.mm-dialog__close {
  box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   3. TONE MODIFIERS — the single place a semantic name becomes three colours.
   Every primitive below reads --mm-tone-*; nothing hard-codes a status colour.
   --------------------------------------------------------------------------- */
.mm-tone-brand,
.mm-chip--brand,
.mm-state--signed-out {
  --mm-tone: var(--mm-brand);
  --mm-tone-fg: var(--mm-brand-fg);
  --mm-tone-surface: var(--mm-brand-surface);
}
.mm-tone-verified,
.mm-chip--verified,
.mm-state--verified {
  --mm-tone: var(--mm-status-verified);
  --mm-tone-fg: var(--mm-status-verified-fg);
  --mm-tone-surface: var(--mm-status-verified-surface);
}
.mm-tone-review,
.mm-chip--review,
.mm-state--not-configured {
  --mm-tone: var(--mm-status-review);
  --mm-tone-fg: var(--mm-status-review-fg);
  --mm-tone-surface: var(--mm-status-review-surface);
}
.mm-tone-blocked,
.mm-chip--blocked,
.mm-state--denied,
.mm-state--failed {
  --mm-tone: var(--mm-status-blocked);
  --mm-tone-fg: var(--mm-status-blocked-fg);
  --mm-tone-surface: var(--mm-status-blocked-surface);
}
.mm-tone-info,
.mm-chip--info {
  --mm-tone: var(--mm-status-info);
  --mm-tone-fg: var(--mm-status-info-fg);
  --mm-tone-surface: var(--mm-status-info-surface);
}
.mm-tone-neutral,
.mm-chip--neutral,
.mm-state--unavailable {
  --mm-tone: var(--mm-text-muted);
  --mm-tone-fg: var(--mm-text-secondary);
  --mm-tone-surface: var(--mm-surface-sunken);
}

/* ---------------------------------------------------------------------------
   4. TOUCH TARGET UTILITY — DEV_GUIDE #6, spec hard gate 3.
   2.75rem computed in BOTH axes. `inline-flex` + centring is what makes the
   floor computed rather than merely declared: a bare min-height on an inline
   element does not grow the hit box.
   Apply to every actionable control EXCEPT true inline text links.
   --------------------------------------------------------------------------- */
.mm-touch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--mm-touch-min);
  min-height: var(--mm-touch-min);
  box-sizing: border-box;
}
/* Text buttons: keep the 2.75rem floor and add a comfortable horizontal run. */
.mm-touch--text {
  padding-left: 0.875rem;
  padding-right: 0.875rem;
  gap: 0.5rem;
}

/* ---------------------------------------------------------------------------
   5. FOCUS — visible, ≥3:1, and never removed by the reduced-motion block.
   Scoped to mm-* primitives so this file cannot regress the rest of the app.
   --------------------------------------------------------------------------- */
.mm-focus-ring:focus-visible,
.mm-touch:focus-visible,
.mm-chip:focus-visible,
.mm-dialog :focus-visible {
  outline: 0.1875rem solid var(--mm-focus);
  outline-offset: 0.125rem;
}

/* ---------------------------------------------------------------------------
   6. OPERATIONAL SURFACE — opaque, 0.75rem radius, ONE subtle border, NO shadow.
   No glassmorphism, no glow, no blur (spec §"What to avoid").
   Shadow lives on .mm-raised and on dialogs/drawers only.
   --------------------------------------------------------------------------- */
.mm-surface {
  background-color: var(--mm-surface);
  border: var(--mm-hairline) solid var(--mm-border);
  border-radius: var(--mm-radius);
  color: var(--mm-text);
}
.mm-raised {
  box-shadow: var(--mm-shadow-overlay);
}

/* ---------------------------------------------------------------------------
   7. STATUS CHIP — text + icon + colour, on TWO independent axes:
        tone       .mm-chip--verified|review|blocked|info|brand|neutral
        vocabulary .mm-chip--availability|workflow|trust|reward
   ONE component, not four. The vocabulary modifier changes SHAPE (radius and
   leading rule) so the four vocabularies stay distinguishable with colour
   removed — a "Failed" workflow chip and a "Failed" payout chip are not the
   same statement.

   Required markup (the label is mandatory; the icon is decorative):
     <span class="mm-chip mm-chip--trust mm-chip--verified">
       <i class="fas fa-circle-check mm-chip__icon" aria-hidden="true"></i>
       <span class="mm-chip__label">Approved</span>
     </span>

   The vocabulary word belongs in the surrounding label or an aria-label — this
   file never generates text via CSS `content`.
   A chip is NOT interactive. If it must be a button, add .mm-touch.
   --------------------------------------------------------------------------- */
.mm-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 1.75rem;
  padding: 0.1875rem 0.625rem;
  border: var(--mm-hairline) solid var(--mm-tone, var(--mm-border));
  border-radius: 999rem;
  background-color: var(--mm-tone-surface, var(--mm-surface-sunken));
  color: var(--mm-tone-fg, var(--mm-text-secondary));
  font-size: 0.8125rem;
  line-height: 1.25rem;
  font-weight: 600;
  white-space: nowrap;
  transition: background-color var(--mm-dur-fast) var(--mm-ease),
              border-color var(--mm-dur-fast) var(--mm-ease),
              color var(--mm-dur-fast) var(--mm-ease);
}
.mm-chip__icon {
  flex: 0 0 auto;
  color: var(--mm-tone, currentColor);
  font-size: 0.75rem;
  line-height: 1;
}
.mm-chip__label {
  min-width: 0;
}
/* Vocabulary shapes — non-chromatic differentiation. */
.mm-chip--availability { border-radius: 999rem; }
.mm-chip--workflow     { border-radius: var(--mm-radius-sm); }
.mm-chip--trust        { border-radius: 999rem; border-left-width: 0.1875rem; padding-left: 0.5rem; }
.mm-chip--reward       { border-radius: var(--mm-radius-sm); border-left-width: 0.1875rem; padding-left: 0.5rem; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------------
   8. STATE PANEL — semantic icon, short kind, title, plain cause, ONE recovery
   action, optional help link.

   The five kinds are distinct BOTH visually and verbally:
     kind              tone     panel treatment                  verbal slot
     --signed-out      brand    solid, uniform border            .mm-state__kind
     --denied          blocked  solid + leading rule             .mm-state__kind
     --unavailable     neutral  dashed, uniform border           .mm-state__kind
     --not-configured  review   dashed + leading rule            .mm-state__kind
     --failed          blocked  solid + leading rule + tinted    .mm-state__kind
   The five treatments are distinct on (border-style, leading-rule width,
   background) ALONE — verified in the browser — so none of them depends on hue.
   `--denied` and `--failed` share red (both are stops, per the colour contract)
   and separate on the tinted fill plus the mandatory kind text.
   `.mm-state__kind` is REQUIRED: it is what makes the kinds verbally distinct.

   Markup:
     <section class="mm-state mm-state--denied">
       <span class="mm-state__icon"><i class="fas fa-ban" aria-hidden="true"></i></span>
       <div class="mm-state__body">
         <p class="mm-state__kind">Permission denied</p>
         <h2 class="mm-state__title">You cannot approve senders</h2>
         <p class="mm-state__cause">Approval needs the Sender Admin role…</p>
         <div class="mm-state__actions">
           <button type="button" class="mm-touch mm-touch--text">Request access</button>
           <a class="mm-state__help" href="/help/…">How approvals work</a>
         </div>
       </div>
     </section>
   --------------------------------------------------------------------------- */
.mm-state {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.75rem 1rem;
  align-items: start;
  padding: 1.25rem;
  background-color: var(--mm-surface);
  border: var(--mm-hairline) solid var(--mm-border);
  border-radius: var(--mm-radius);
  color: var(--mm-text);
}
.mm-state__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 999rem;
  background-color: var(--mm-tone-surface, var(--mm-surface-sunken));
  color: var(--mm-tone, var(--mm-text-secondary));
  font-size: 1rem;
}
.mm-state__body {
  min-width: 0;
}
.mm-state__kind {
  margin: 0 0 0.25rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--mm-tone-fg, var(--mm-text-secondary));
}
.mm-state__title {
  margin: 0 0 0.375rem;
  font-size: 1rem;
  line-height: 1.5rem;
  font-weight: 600;
  color: var(--mm-text);
}
.mm-state__cause {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.375rem;
  color: var(--mm-text-secondary);
}
.mm-state__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
}
.mm-state__help {
  font-size: 0.875rem;
  color: var(--mm-brand-fg);
  text-decoration: underline;
}
/* Kind treatments */
.mm-state--denied,
.mm-state--failed { border-left: 0.1875rem solid var(--mm-tone); }
.mm-state--failed { background-color: var(--mm-tone-surface); }
/* The dashed boundary is one of the signals that separates these two kinds from
   the solid-bordered three, so it must actually be visible: --mm-border measures
   1.23:1 against the card in light. Tone-colouring it clears 3:1 AND ties the
   boundary to the kind. */
.mm-state--unavailable,
.mm-state--not-configured { border-style: dashed; border-color: var(--mm-tone); }
/* Without this, `unavailable` and `not-configured` are separated by HUE ALONE
   (slate vs amber) — measured: identical border-style, width and background.
   Colour must never be the only signal, so not-configured takes the dashed
   leading rule, giving all five kinds a distinct structure. */
.mm-state--not-configured { border-left-width: 0.1875rem; }

/* ---------------------------------------------------------------------------
   9. CONTEXT HEAD — eyebrow/breadcrumb, h1, one-sentence purpose, ONE primary
   action, overflow. Grid is minmax(0, 1fr) auto; stacked below 48rem.
   The actions cell is LAST in the DOM (so it follows title and purpose in
   reading order) and is placed into column 2 by grid-template-areas — no
   wrapper div and no `order` reshuffle of the accessibility tree.
   --------------------------------------------------------------------------- */
.mm-context-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "eyebrow"
    "title"
    "purpose"
    "actions";
  gap: 0.5rem 1.5rem;
  padding-bottom: 1rem;
  border-bottom: var(--mm-hairline) solid var(--mm-border);
}
.mm-context-head__eyebrow {
  grid-area: eyebrow;
  margin: 0;
  min-width: 0;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--mm-text-muted);
}
.mm-context-head__title {
  grid-area: title;
  margin: 0;
  min-width: 0;
  font-size: 1.375rem;
  line-height: 1.875rem;
  font-weight: 700;
  color: var(--mm-text);
}
.mm-context-head__purpose {
  grid-area: purpose;
  margin: 0;
  min-width: 0;
  max-width: 48rem;
  font-size: 0.875rem;
  line-height: 1.375rem;
  color: var(--mm-text-secondary);
}
.mm-context-head__actions {
  grid-area: actions;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.5rem;
}
@media (min-width: 48rem) {
  .mm-context-head {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "eyebrow actions"
      "title   actions"
      "purpose actions";
  }
  .mm-context-head__actions {
    align-self: start;
    justify-content: flex-end;
    margin-top: 0;
  }
  .mm-context-head__title { font-size: 1.5rem; line-height: 2rem; }
}

/* ---------------------------------------------------------------------------
   10. DIALOG + DRAWER CHROME — pairs with /js/mm-dialog.js, which owns focus,
   Escape, containment, inert and opener restoration. This block owns geometry
   only: fixed header, SCROLLABLE body, fixed footer, 85vh then 85dvh.

   Built on the native <dialog> element (see mm-dialog.js for the rationale).
   The UA sheet hides a closed <dialog>; because we set `display: flex` on the
   panel we must restate BOTH branches explicitly or a closed dialog would show.
   --------------------------------------------------------------------------- */
.mm-dialog:not([open]) { display: none; }

.mm-dialog[open] {
  display: flex;
  flex-direction: column;
}
.mm-dialog {
  width: min(100% - 2rem, 40rem);   /* forms/review dialogs: 40rem–48rem */
  max-width: 100%;
  padding: 0;
  border: var(--mm-hairline) solid var(--mm-border);
  border-radius: var(--mm-radius);
  background-color: var(--mm-surface);
  color: var(--mm-text);
  box-shadow: var(--mm-shadow-overlay);
  /* DEV_GUIDE #4 — 85vh FIRST, then 85dvh, both present, in this order. */
  max-height: 85vh;
  max-height: 85dvh;
  overflow: hidden;                  /* the BODY scrolls, not the panel */
}
.mm-dialog--sm { width: min(100% - 2rem, 26rem); }
.mm-dialog--lg { width: min(100% - 2rem, 48rem); }

/* Right-anchored side sheet. Same 85vh/85dvh ceiling as the dialog (declared
   once, above) — `height: 100%` resolves against it, so the drawer is a
   full-height-of-allowance sheet inset from the right edge. */
.mm-dialog--drawer {
  height: 100%;
  width: min(100% - 2rem, 28rem);
  margin: auto 1rem auto auto;
}

.mm-dialog::backdrop {
  background-color: rgba(15, 23, 42, 0.6);   /* flat scrim: no blur, no glow */
}

.mm-dialog__header,
.mm-dialog__footer {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
}
.mm-dialog__header { border-bottom: var(--mm-hairline) solid var(--mm-border); }
.mm-dialog__footer {
  justify-content: flex-end;
  flex-wrap: wrap;
  border-top: var(--mm-hairline) solid var(--mm-border);
  background-color: var(--mm-surface-sunken);
}
.mm-dialog__title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.5rem;
  font-weight: 600;
  color: var(--mm-text);
}
.mm-dialog__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 1rem;
  font-size: 0.875rem;
  line-height: 1.375rem;
  color: var(--mm-text);
}
/* Icon-only close: 2.75rem both axes. mm-dialog.js enforces type="button" and
   an aria-label (DEV_GUIDE #15, #16) if the author omitted them. */
.mm-dialog__close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--mm-touch-min);
  min-height: var(--mm-touch-min);
  border: 0;
  border-radius: var(--mm-radius-sm);
  background: transparent;
  color: var(--mm-text-secondary);
  cursor: pointer;
  transition: background-color var(--mm-dur-fast) var(--mm-ease),
              color var(--mm-dur-fast) var(--mm-ease);
}
.mm-dialog__close:hover { background-color: var(--mm-surface-sunken); color: var(--mm-text); }

/* Entrance: dialog feedback, not a page-load reveal. 200ms, the one easing. */
@keyframes mm-dialog-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to   { opacity: 1; transform: none; }
}
@keyframes mm-drawer-in {
  from { opacity: 0; transform: translateX(1rem); }
  to   { opacity: 1; transform: none; }
}
.mm-dialog[open] { animation: mm-dialog-in var(--mm-dur) var(--mm-ease) both; }
.mm-dialog--drawer[open] { animation-name: mm-drawer-in; }

/* Scroll lock while any dialog is open (mm-dialog.js toggles the class and
   ref-counts nested dialogs). */
.mm-dialog-open, .mm-dialog-open body { overflow: hidden; }

/* Fallback path only — used when showModal() is unavailable, so there is no
   top layer and no ::backdrop. mm-dialog.js inserts/removes this element. */
.mm-dialog-scrim {
  position: fixed;
  inset: 0;
  z-index: 1040;
  background-color: rgba(15, 23, 42, 0.6);
}
.mm-dialog--fallback[open] {
  position: fixed;
  inset: 0;
  z-index: 1050;
  margin: auto;
}
.mm-dialog--fallback.mm-dialog--drawer[open] { margin: auto 1rem auto auto; }

/* ---------------------------------------------------------------------------
   11. REDUCED MOTION — DEV_GUIDE #14.
   REMOVES non-essential transform/opacity movement (the dialog/drawer entrance).
   PRESERVES focus feedback and status/tone colour feedback.

   Preservation needs NO re-declaration. This file never blanket-disables
   transitions, so the base `.mm-chip` / `.mm-dialog__close` colour transitions
   in §7 and §10 survive this media query untouched — there is nothing here to
   restore. An earlier revision re-declared them "to be safe" and that was
   actively harmful, measured in-browser:

     * it listed `outline-color`, which NO base rule transitions, so under
       reduced motion the focus ring FADED IN over 150ms — arriving as
       currentColor (black) and only settling to violet afterwards. Degrading
       the focus indicator is precisely what #14 forbids;
     * it listed `.mm-touch`, whose computed `transition-duration` is 0s
       normally. So the query ADDED motion for users who asked for less.

   Rule of thumb for anyone extending this block: a reduced-motion query may
   only ever remove motion. If a declaration here would introduce a transition
   that does not exist outside the query, it is wrong. Never transition
   `outline`/`outline-color`.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .mm-dialog[open],
  .mm-dialog--drawer[open] {
    animation: none;
    transform: none;
  }
}
