/*! Meridian UI core v0.1.0 | MIT | every class is mk- namespaced */
/* @meridian/core — entry point.
   Import order matters only for the @layer statement, which must come first. */
/* Cascade layer order for the whole library.
   Consumers writing unlayered CSS automatically win over every Meridian rule,
   so overriding never requires a specificity fight. */
@layer meridian.tokens, meridian.base, meridian.components, meridian.utilities;

/* ============================================================
   TOKENS — the entire theming API.

   Dark is defined twice on purpose:
     1. under prefers-color-scheme, guarded by :not([data-theme="light"])
     2. under [data-theme="dark"]
   so an explicit user choice wins in BOTH directions, and the default
   "system" setting still tracks the OS.

   Token names are unprefixed (--canvas, --accent) because they are the
   documented public API; see --mk-* aliases at the bottom for consumers
   who need collision-proof names.
   ============================================================ */
@layer meridian.tokens {
  :root {
    color-scheme: light;

    /* neutrals — biased slightly blue so they read as chosen */
    --canvas:        #f2f4f8;
    --surface:       #ffffff;
    --surface-2:     #f7f9fc;
    --surface-3:     #eaeef6;
    --nav-bg:        #edf1f7;
    --border:        #dce1eb;
    --border-strong: #c2cad9;
    --text:          #151a24;
    --text-2:        #525c6e;
    /* Darkened from #7b8698, which measured 3.17:1 on --surface-3 and failed
       WCAG AA everywhere it is used as text (hints, table headers, stat keys,
       option sub-lines). This clears 4.5:1 on every light surface. */
    --text-3:        #616a78;

    /* accent */
    --accent:        #2a50d8;
    --accent-hover:  #1e3eb4;
    --accent-press:  #17318f;
    --accent-soft:   #e5eafc;
    --on-accent:     #ffffff;

    /* semantic — deliberately not the accent hue */
    --ok:        #1b6f45;  --ok-soft:     #dcf2e6;
    --warn:      #8a5806;  --warn-soft:   #fbefd5;
    --danger:    #a81f17;  --danger-soft: #fae3e1;
    --epic:      #6335b8;  --epic-soft:   #eee6fb;
    --neutral-soft: #e7ebf2;

    /* type */
    --font-brand: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
    --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, "Liberation Mono", monospace;

    --fs-2xs: 10.5px;
    --fs-xs:  11.5px;
    --fs-sm:  12.5px;
    --fs-md:  14px;
    --fs-lg:  16px;
    --fs-xl:  20px;
    --fs-2xl: 26px;
    --fs-3xl: 34px;

    /* spacing — a 4px base. The layout utilities use nothing else, and a
       consumer who wants a denser or roomier library changes these six numbers
       rather than hunting for gaps. Before this existed there were eleven
       hardcoded gap values across the components, three of them used once. */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;

    /* layout — the container widths the column classes switch at. These measure
       the *row*, not the window: a column inside a 300px drawer is narrow
       wherever the browser window happens to be. */
    --bp-sm: 420px;
    --bp-md: 640px;
    --bp-lg: 900px;

    /* geometry */
    --r-sm: 3px;
    --r-md: 4px;
    --r-lg: 6px;
    --r-pill: 999px;
    --nav-w: 248px;
    /* Width of a collapsed Sidebar. A token rather than a magic number because
       it has to agree with whatever the consumer sets --nav-w to. */
    --nav-rail: 56px;
    --header-h: 48px;

    --shadow-1: 0 1px 2px rgb(16 24 40 / 8%), 0 0 0 1px rgb(16 24 40 / 4%);
    --shadow-2: 0 4px 12px rgb(16 24 40 / 10%), 0 0 0 1px rgb(16 24 40 / 5%);
    --shadow-3: 0 16px 40px rgb(16 24 40 / 18%), 0 0 0 1px rgb(16 24 40 / 6%);

    /* z-index scale — was scattered as magic numbers, now a token */
    --z-header:  40;
    --z-scrim:   90;
    --z-dialog: 100;
    --z-popover: 200;
    --z-toast:  300;
    /* Above everything: a tooltip may describe a control inside a dialog. */
    --z-tooltip: 400;

    /* motion — respected by the reduced-motion block in _base.css */
    --dur-fast: .12s;
    --dur-mid:  .18s;
    --dur-slow: .22s;
    --ease: cubic-bezier(.32, .72, 0, 1);
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      color-scheme: dark;
      --canvas:        #0d1116;
      --surface:       #161b23;
      --surface-2:     #1c222c;
      --surface-3:     #252c38;
      --nav-bg:        #11161d;
      --border:        #29313d;
      --border-strong: #3a4454;
      --text:          #e7ebf3;
      --text-2:        #a4aec0;
      /* Lightened from #78839a (3.68:1 on --surface-3); clears 4.5:1 on every
         dark surface. See the light-theme note above. */
      --text-3:        #8c96ab;

      --accent:        #7d9cff;
      --accent-hover:  #96afff;
      --accent-press:  #b0c2ff;
      --accent-soft:   #1a2340;
      --on-accent:     #0a0f1c;

      --ok:      #55c48f;  --ok-soft:     #10301f;
      --warn:    #e0a544;  --warn-soft:   #33250d;
      --danger:  #f0796c;  --danger-soft: #351a17;
      --epic:    #b58cf7;  --epic-soft:   #221a3a;
      --neutral-soft: #232a35;

      --shadow-1: 0 1px 2px rgb(0 0 0 / 50%), 0 0 0 1px rgb(255 255 255 / 4%);
      --shadow-2: 0 4px 12px rgb(0 0 0 / 55%), 0 0 0 1px rgb(255 255 255 / 5%);
      --shadow-3: 0 16px 40px rgb(0 0 0 / 65%), 0 0 0 1px rgb(255 255 255 / 7%);
    }
  }

  :root[data-theme="dark"] {
    color-scheme: dark;
    --canvas:        #0d1116;
    --surface:       #161b23;
    --surface-2:     #1c222c;
    --surface-3:     #252c38;
    --nav-bg:        #11161d;
    --border:        #29313d;
    --border-strong: #3a4454;
    --text:          #e7ebf3;
    --text-2:        #a4aec0;
    /* Lightened from #78839a — see the note in the light palette. */
    --text-3:        #8c96ab;

    --accent:        #7d9cff;
    --accent-hover:  #96afff;
    --accent-press:  #b0c2ff;
    --accent-soft:   #1a2340;
    --on-accent:     #0a0f1c;

    --ok:      #55c48f;  --ok-soft:     #10301f;
    --warn:    #e0a544;  --warn-soft:   #33250d;
    --danger:  #f0796c;  --danger-soft: #351a17;
    --epic:    #b58cf7;  --epic-soft:   #221a3a;
    --neutral-soft: #232a35;

    --shadow-1: 0 1px 2px rgb(0 0 0 / 50%), 0 0 0 1px rgb(255 255 255 / 4%);
    --shadow-2: 0 4px 12px rgb(0 0 0 / 55%), 0 0 0 1px rgb(255 255 255 / 5%);
    --shadow-3: 0 16px 40px rgb(0 0 0 / 65%), 0 0 0 1px rgb(255 255 255 / 7%);
  }
}

/* ============================================================
   BASE

   Scoped to .mk-root (applied to <html> or <body> by the consumer) rather
   than bare element selectors, so dropping Meridian into an existing app
   cannot restyle that app's typography. The one exception is box-sizing,
   which is opt-in via .mk-reset.
   ============================================================ */
@layer meridian.base {
  .mk-reset,
  .mk-reset *,
  .mk-reset *::before,
  .mk-reset *::after { box-sizing: border-box; }

  .mk-root {
    background: var(--canvas);
    color: var(--text);
    font-family: var(--font-ui);
    font-size: var(--fs-md);
    line-height: 1.45;
    -webkit-font-smoothing: antialiased;
  }

  .mk-root [hidden] { display: none !important; }

  .mk-root :where(button, input, select, textarea) { font: inherit; color: inherit; }

  .mk-root :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-sm);
  }

  /* Kill transitions and animations, but leave the elements they act on
     in their final state — never leave a component mid-animation. */
  @media (prefers-reduced-motion: reduce) {
    .mk-root *,
    .mk-root *::before,
    .mk-root *::after {
      transition-duration: .01ms !important;
      animation-duration: .01ms !important;
      animation-iteration-count: 1 !important;
      scroll-behavior: auto !important;
    }
  }

  /* Visually hidden, still announced. */
  .mk-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* Icons behave like an icon font: sized in em, colored by currentColor. */
  .mk-ic {
    width: 1.15em;
    height: 1.15em;
    flex: none;
    fill: none;
    stroke: currentcolor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
    vertical-align: -.22em;
  }
  .mk-ic-solid { fill: currentcolor; stroke: none; }

  /* Locked by Modal/Drawer while an overlay owns the viewport. */
  .mk-scroll-locked { overflow: hidden !important; }

  /* Applied for one frame while the theme swaps.
     Two reasons, both real: animating every colour on the page at once looks
     like a fault rather than a transition, and Chromium leaves an element's
     computed background stale when a custom property behind a transitioning
     property changes — which also feeds wrong colours to auditing tools. */
  .mk-theming,
  .mk-theming *,
  .mk-theming *::before,
  .mk-theming *::after {
    transition: none !important;
  }
}
@layer meridian.components {
  .mk-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 32px;
    padding: 0 12px;
    border-radius: var(--r-md);
    border: 1px solid transparent;
    background: var(--surface-3);
    color: var(--text);
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition:
      background var(--dur-fast) ease,
      border-color var(--dur-fast) ease,
      color var(--dur-fast) ease;
  }
  .mk-btn:hover { background: var(--border); }
  .mk-btn:active { transform: translateY(.5px); }

  .mk-btn-primary { background: var(--accent); color: var(--on-accent); }
  .mk-btn-primary:hover { background: var(--accent-hover); }
  .mk-btn-primary:active { background: var(--accent-press); }

  .mk-btn-outline { background: transparent; border-color: var(--border-strong); color: var(--text); }
  .mk-btn-outline:hover { background: var(--surface-3); }

  .mk-btn-ghost { background: transparent; color: var(--text-2); }
  .mk-btn-ghost:hover { background: var(--surface-3); color: var(--text); }

  /* --on-accent, not #fff: in dark mode --danger is a light salmon, and white
     text on it measures ~2.5:1. --on-accent flips with the theme. */
  .mk-btn-danger { background: var(--danger); color: var(--on-accent); }
  /* The background has to be restated. `.mk-btn:hover` outranks `.mk-btn-danger`
     (0-2-0 against 0-1-0), so a filter-only hover rule left a hovered danger
     button painted --border with white text — 1.31:1, caught by axe. */
  .mk-btn-danger:hover { background: var(--danger); filter: brightness(1.08); }

  .mk-btn-link {
    background: none;
    height: auto;
    padding: 0;
    color: var(--accent);
    text-decoration: none;
  }
  .mk-btn-link:hover { background: none; text-decoration: underline; }

  .mk-btn-sm { height: 26px; padding: 0 9px; font-size: var(--fs-xs); }
  .mk-btn-lg { height: 40px; padding: 0 18px; font-size: var(--fs-md); }

  /* :disabled covers <button>; [disabled]/[aria-disabled] covers the rest */
  .mk-btn:is([disabled], [aria-disabled="true"]) {
    opacity: .45;
    cursor: not-allowed;
    pointer-events: none;
  }

  .mk-btn-icon {
    width: 32px;
    height: 32px;
    padding: 0;
    background: transparent;
    color: var(--text-2);
    border-radius: var(--r-md);
  }
  .mk-btn-icon:hover { background: var(--surface-3); color: var(--text); }
  .mk-btn-icon .mk-ic { width: 17px; height: 17px; }
  .mk-btn-icon-sm { width: 26px; height: 26px; }
  .mk-btn-icon-sm .mk-ic { width: 15px; height: 15px; }

  /* Segmented control. Driven by aria-pressed, so the a11y state and the
     visual state cannot drift apart. */
  .mk-btn-group {
    display: inline-flex;
    background: var(--surface-3);
    border-radius: var(--r-md);
    padding: 2px;
    gap: 2px;
  }
  .mk-btn-group .mk-btn {
    height: 26px;
    background: transparent;
    font-weight: 560;
    color: var(--text-2);
  }
  .mk-btn-group .mk-btn[aria-pressed="true"] {
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow-1);
  }

  /* Split button: one control, two hit areas. The seam is a border rather than
     a gap, so the pair reads as a single button and not as two that happen to
     touch. */
  .mk-split { display: inline-flex; }
  .mk-split .mk-btn { border-radius: 0; }
  .mk-split > .mk-btn:first-child { border-top-left-radius: var(--r-md); border-bottom-left-radius: var(--r-md); }
  .mk-split > .mk-btn:last-child { border-top-right-radius: var(--r-md); border-bottom-right-radius: var(--r-md); }
  .mk-split-toggle {
    padding: 0 6px;
    /* color-mix on currentcolor: the seam has to read on a primary, a danger
       and a default button without three separate rules. */
    border-left: 1px solid color-mix(in srgb, currentcolor 24%, transparent);
  }
  .mk-split-toggle .mk-ic { width: 15px; height: 15px; }

  /* Copy button. `is-on` is set for the length of the confirmation only. */
  .mk-copy-text { display: inline-block; }
  .mk-btn.is-on { color: var(--ok); }
  .mk-btn.is-on .mk-ic { color: var(--ok); }
  .mk-btn.is-bad { color: var(--danger); }
  .mk-btn.is-bad .mk-ic { color: var(--danger); }
}

@layer meridian.components {
  .mk-field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 16px; }

  /* A field laid out *inside* a grid must not carry a bottom margin of its
     own: the grid gap already sets the row rhythm, and the two were adding up
     — 45px between rows of a `.mk-cols-12` form where 16 was intended. The
     margin is still right for a field standing on its own, so it stays on the
     rule above rather than being deleted. */
  .mk-cols-12 .mk-field,
  .mk-field-row .mk-field { margin-bottom: 0; }
  /* Two fields where there is room, one where there is not — decided by
     `auto-fit`, so there is no breakpoint to state and nothing to keep in step
     with a media query. This replaced a `1fr 1fr` plus an override in
     @meridian/app that fired on the *window* width. */
  .mk-field-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--mk-field-min, 220px), 1fr));
    gap: var(--space-4);
  }

  .mk-label {
    font-size: var(--fs-sm);
    font-weight: 620;
    color: var(--text-2);
    display: flex;
    align-items: center;
    gap: var(--space-1);
  }
  .mk-required { color: var(--danger); }
  /* A <p>, so it arrives with the UA's 1em block margins — not what the 5px
     flex gap of `.mk-field` meant to say. They stranded every hint 17px under
     its input and pushed another 11.5px onto the row beneath. */
  .mk-hint { font-size: var(--fs-xs); color: var(--text-3); margin: 0; }
  .mk-error {
    font-size: var(--fs-xs);
    color: var(--danger);
    display: flex;
    align-items: center;
    gap: var(--space-1);
  }
  .mk-error .mk-ic { width: 13px; height: 13px; }

  .mk-input,
  .mk-select,
  .mk-textarea {
    width: 100%;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    padding: 0 10px;
    height: 34px;
    font-size: var(--fs-sm);
    color: var(--text);
    transition: border-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease;
  }
  .mk-textarea {
    height: auto;
    padding: 8px 10px;
    resize: vertical;
    min-height: 96px;
    line-height: 1.55;
  }
  .mk-input::placeholder,
  .mk-textarea::placeholder { color: var(--text-3); }

  .mk-input:hover,
  .mk-select:hover,
  .mk-textarea:hover { border-color: var(--text-3); }

  .mk-input:focus,
  .mk-select:focus,
  .mk-textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }

  /* Invalid state applies to every control, not just .mk-input as before. */
  :is(.mk-input, .mk-select, .mk-textarea)[aria-invalid="true"] { border-color: var(--danger); }
  :is(.mk-input, .mk-select, .mk-textarea)[aria-invalid="true"]:focus {
    box-shadow: 0 0 0 3px var(--danger-soft);
  }
  :is(.mk-input, .mk-select, .mk-textarea)[disabled] {
    background: var(--surface-2);
    color: var(--text-3);
    cursor: not-allowed;
  }

  .mk-select {
    appearance: none;
    padding-right: 30px;
    /* Chevron is drawn with currentColor via mask, so it follows the theme
       instead of being frozen at the light-mode hex the original inlined. */
    background-image: none;
    mask: none;
  }
  .mk-select-wrap { position: relative; display: block; }
  .mk-select-wrap::after {
    content: "";
    position: absolute;
    right: 9px;
    top: 50%;
    width: 15px;
    height: 15px;
    transform: translateY(-50%);
    background: var(--text-3);
    pointer-events: none;
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>") center / contain no-repeat;
  }

  /* A control with things attached to it: a prefix, a unit, a button.
     Borders are collapsed by pulling each item one pixel over its neighbour, so
     the group reads as a single field rather than a row of separate ones. The
     focused item is raised, because a ring drawn under the next item's border
     looks like a rendering fault. */
  .mk-input-group {
    display: flex;
    align-items: stretch;
    width: 100%;
  }
  .mk-input-group > * {
    position: relative;
    border-radius: 0;
    margin-left: -1px;
  }
  .mk-input-group > :first-child {
    margin-left: 0;
    border-start-start-radius: var(--r-md);
    border-end-start-radius: var(--r-md);
  }
  .mk-input-group > :last-child {
    border-start-end-radius: var(--r-md);
    border-end-end-radius: var(--r-md);
  }
  /* The field takes the slack; addons and buttons take only what they need. */
  .mk-input-group > .mk-input,
  .mk-input-group > .mk-select-wrap { flex: 1; min-width: 0; }
  .mk-input-group > .mk-btn { flex: none; }
  .mk-input-group > :focus-within,
  .mk-input-group > :focus { z-index: 1; }

  /* A label attached to the control rather than sitting above it: a currency
     symbol, a unit, a domain. Not focusable, and not a control. */
  .mk-input-addon {
    display: flex;
    align-items: center;
    flex: none;
    padding: 0 10px;
    height: 34px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    color: var(--text-2);
    font-size: var(--fs-sm);
    white-space: nowrap;
  }

  .mk-check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: var(--fs-sm);
    cursor: pointer;
  }
  .mk-check input {
    width: 15px;
    height: 15px;
    margin: 2px 0 0;
    accent-color: var(--accent);
    cursor: pointer;
  }

  .mk-switch {
    position: relative;
    width: 34px;
    height: 19px;
    flex: none;
    border-radius: var(--r-pill);
    background: var(--border-strong);
    border: 0;
    padding: 0;
    cursor: pointer;
    transition: background .15s ease;
  }
  .mk-switch::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: #fff;
    transition: transform .15s ease;
    box-shadow: 0 1px 2px rgb(0 0 0 / 30%);
  }
  .mk-switch[aria-checked="true"] { background: var(--accent); }
  .mk-switch[aria-checked="true"]::after { transform: translateX(15px); }
  .mk-switch[disabled] { opacity: .45; cursor: not-allowed; }

  /* Rich-text shell: a bordered box that owns focus for the textarea inside. */
  .mk-rte {
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    overflow: hidden;
  }
  .mk-rte:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  .mk-rte-bar {
    display: flex;
    align-items: center;
    gap: 1px;
    padding: 4px 5px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
  }
  .mk-rte-bar .mk-sep { width: 1px; height: 16px; background: var(--border); margin: 0 4px; }
  .mk-rte .mk-textarea { border: 0; border-radius: 0; }
  .mk-rte .mk-textarea:focus { box-shadow: none; }

  /* Hidden until the Form component fills it, so an empty slot leaves no gap. */
  .mk-error[hidden] { display: none; }
  .mk-field.is-invalid .mk-label { color: var(--danger); }

  .mk-form-summary {
    border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
    background: var(--danger-soft);
    color: var(--danger);
    border-radius: var(--r-md);
    padding: 10px 12px;
    font-size: var(--fs-sm);
    margin-bottom: 16px;
  }
  .mk-form-summary strong { display: block; margin-bottom: 4px; }
  .mk-form-summary ul { margin: 0; padding-left: 18px; }
  .mk-form-summary li { margin-bottom: 2px; }
  .mk-form-summary a { color: inherit; }

  .mk-kbd {
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    color: var(--text-3);
    border: 1px solid var(--border);
    border-bottom-width: 2px;
    border-radius: var(--r-sm);
    padding: 1px 5px;
    background: var(--surface);
  }
}

/* After form.css: an OTP box is an .mk-input the component re-sizes, and equal
   specificity means the later rule is the one that wins. */
@layer meridian.components {
  /* One-time code entry.

     The boxes are squares rather than fields: no padding, centred text, and a
     size that gives way rather than pushing the page sideways. Eight boxes at a
     fixed 2.75rem overflow a phone, and a code field is mostly typed on a
     phone — so a box shrinks with its container down to a floor, stays square
     on `aspect-ratio`, and wraps only if even the floor does not fit. Set
     `--mk-otp-size` for a bigger or smaller set.

     `flex: 1 1 0` rather than a basis of `--mk-otp-size`, because wrapping is
     decided on the base size *before* anything shrinks: eight boxes based at
     2.75rem push one onto a second line while there is still room to make all
     eight narrower. Basing at zero and growing to the cap keeps the group on
     one line and shrinks it to fit, which leaves wrapping as the last resort
     it was meant to be. */
  .mk-otp {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
  }

  .mk-otp-input {
    flex: 1 1 0;
    max-width: var(--mk-otp-size, 2.75rem);
    min-width: 1.75rem;
    height: auto;
    aspect-ratio: 1;
    padding: 0;
    text-align: center;
    font-size: var(--fs-xl);
    font-weight: 620;
    /* Digits of equal width, so a 4 does not shuffle the row a 1 left alone. */
    font-variant-numeric: tabular-nums;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    transition: border-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease;
  }
  .mk-otp-input::placeholder { color: var(--text-3); }
  .mk-otp-input:hover { border-color: var(--text-3); }
  .mk-otp-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  .mk-otp-input[disabled] {
    background: var(--surface-2);
    color: var(--text-3);
    cursor: not-allowed;
  }
  .mk-otp-input[readonly] { background: var(--surface-2); }

  .mk-otp-sep {
    color: var(--text-3);
    user-select: none;
  }

  /* Form paints the error on the field it knows about, which is the first box.
     The group is one field to the user, so all of it reddens: `:has()` covers
     a bare group, `.mk-field.is-invalid` the usual form layout. */
  .mk-otp:has(.mk-otp-input[aria-invalid="true"]) .mk-otp-input,
  .mk-field.is-invalid .mk-otp-input { border-color: var(--danger); }
  .mk-otp:has(.mk-otp-input[aria-invalid="true"]) .mk-otp-input:focus,
  .mk-field.is-invalid .mk-otp-input:focus { box-shadow: 0 0 0 3px var(--danger-soft); }
}

@layer meridian.components {
  /* Base pill. Semantic variants (todo/progress/done/…) live in
     @meridian/tracker — core ships only the neutral form plus the
     generic tone modifiers. */
  .mk-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 20px;
    padding: 0 8px;
    border-radius: var(--r-sm);
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    background: var(--neutral-soft);
    color: var(--text-2);
    white-space: nowrap;
  }
  .mk-pill-accent { background: var(--accent-soft); color: var(--accent); }
  .mk-pill-ok     { background: var(--ok-soft);     color: var(--ok); }
  .mk-pill-warn   { background: var(--warn-soft);   color: var(--warn); }
  .mk-pill-danger { background: var(--danger-soft); color: var(--danger); }
  .mk-pill-epic   { background: var(--epic-soft);   color: var(--epic); }

  .mk-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 22px;
    padding: 0 4px 0 8px;
    border-radius: var(--r-pill);
    background: var(--surface-3);
    color: var(--text-2);
    font-size: var(--fs-xs);
    font-weight: 560;
  }
  .mk-chip button {
    border: 0;
    background: none;
    cursor: pointer;
    display: grid;
    place-items: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    color: var(--text-3);
  }
  .mk-chip button:hover { background: var(--border); color: var(--text); }
  .mk-chip .mk-ic { width: 11px; height: 11px; stroke-width: 2.4; }

  .mk-tag {
    display: inline-flex;
    align-items: center;
    height: 18px;
    padding: 0 6px;
    border-radius: var(--r-sm);
    font-size: var(--fs-2xs);
    font-weight: 640;
    background: var(--surface-3);
    color: var(--text-2);
    border: 1px solid var(--border);
  }

  /* Square icon badge — the generic mechanic behind tracker's type glyphs. */
  .mk-glyph {
    display: inline-grid;
    place-items: center;
    width: 16px;
    height: 16px;
    border-radius: var(--r-sm);
    flex: none;
    background: var(--surface-3);
    color: var(--text-2);
  }
  .mk-glyph .mk-ic { width: 11px; height: 11px; stroke-width: 2.1; }

  .mk-avatar {
    border-radius: var(--r-pill);
    display: grid;
    place-items: center;
    font-size: var(--fs-xs);
    font-weight: 650;
    letter-spacing: .02em;
    color: #fff;
    width: 26px;
    height: 26px;
    flex: none;
    user-select: none;
    border: 0;
    padding: 0;
    font-family: inherit;
    background: var(--text-3);
  }
  button.mk-avatar { cursor: pointer; }
  button.mk-avatar:hover { box-shadow: 0 0 0 2px var(--accent-soft); }
  .mk-avatar-sm { width: 20px; height: 20px; font-size: var(--fs-2xs); }
  .mk-avatar-lg { width: 34px; height: 34px; font-size: var(--fs-sm); }

  /* Overlapping stack. The ring is the surface colour, not white — on a tinted
     panel a white ring outlines every avatar in a colour nothing else uses. */
  .mk-avatars { display: inline-flex; align-items: center; }
  .mk-avatars .mk-avatar {
    box-shadow: 0 0 0 2px var(--surface);
    margin-left: -8px;
  }
  .mk-avatars .mk-avatar:first-child { margin-left: 0; }
  .mk-avatars .mk-avatar[hidden] { display: none; }
  .mk-avatar-more {
    background: var(--surface-3);
    color: var(--text-2);
    font-size: var(--fs-2xs);
    letter-spacing: 0;
  }

  /* Deterministic tint set for initials avatars. */
  .mk-av-1 { background: #2a50d8; }
  .mk-av-2 { background: #6335b8; }
  .mk-av-3 { background: #0e7c6b; }
  .mk-av-4 { background: #b5541c; }
  .mk-av-5 { background: #a8296b; }
  .mk-av-6 { background: #3e5c76; }
}

@layer meridian.components {
  .mk-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-1);
  }
  .mk-panel-head {
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .mk-panel-title { font-size: var(--fs-md); font-weight: 640; margin: 0; }
  .mk-panel-body { padding: 16px; }
  .mk-panel-foot {
    padding: 12px 16px;
    border-top: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    background: var(--surface-2);
  }
  /* An image cap. The panel clips it, so the corners follow the panel's radius
     without the image having to know what that radius is. */
  .mk-panel { overflow: hidden; }
  .mk-panel-img {
    display: block;
    width: 100%;
    height: auto;
  }

  .mk-tabs {
    display: flex;
    gap: 2px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 18px;
  }
  .mk-tab {
    border: 0;
    background: none;
    padding: 8px 12px;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--text-2);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
  }
  .mk-tab:hover { color: var(--text); background: var(--surface-2); }
  .mk-tab[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); }

  .mk-alert {
    display: flex;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--r-md);
    font-size: var(--fs-sm);
    border: 1px solid;
    align-items: flex-start;
  }
  .mk-alert .mk-ic { width: 17px; height: 17px; margin-top: 1px; flex: none; }
  .mk-alert strong { display: block; margin-bottom: 1px; }
  .mk-alert-body { flex: 1; min-width: 0; }
  /* The dismiss control inherits the alert's colour, so one rule covers all four
     intents — and `currentColor` on the glyph means no per-intent icon. */
  .mk-alert-close {
    flex: none;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    margin: -2px -4px -2px 0;
    padding: 0;
    border: 0;
    background: none;
    border-radius: var(--r-sm);
    color: inherit;
    opacity: .7;
    cursor: pointer;
  }
  .mk-alert-close:hover { opacity: 1; background: color-mix(in srgb, currentcolor 12%, transparent); }
  .mk-alert-close .mk-ic { width: 14px; height: 14px; margin: 0; }
  .mk-alert-info   { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 28%, transparent); color: var(--accent); }
  .mk-alert-ok     { background: var(--ok-soft);     border-color: color-mix(in srgb, var(--ok) 28%, transparent);     color: var(--ok); }
  .mk-alert-warn   { background: var(--warn-soft);   border-color: color-mix(in srgb, var(--warn) 32%, transparent);   color: var(--warn); }
  .mk-alert-danger { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 30%, transparent); color: var(--danger); }

  .mk-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
    gap: var(--space-3);
    margin-bottom: 18px;
  }
  .mk-stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 12px 14px;
    box-shadow: var(--shadow-1);
  }
  .mk-stat-key {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: .08em;
    font-weight: 700;
    color: var(--text-3);
  }
  .mk-stat-value {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-2xl);
    font-weight: 600;
    letter-spacing: -.02em;
    line-height: 1.15;
    margin-top: 4px;
  }
  .mk-stat-delta {
    font-size: var(--fs-xs);
    color: var(--text-3);
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-top: 2px;
  }
  .mk-stat-delta .mk-ic { width: 13px; height: 13px; }
  .mk-up { color: var(--ok); }
  .mk-down { color: var(--danger); }

  .mk-section-title {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: .09em;
    font-weight: 750;
    color: var(--text-3);
    margin: 22px 0 8px;
  }

  .mk-empty {
    text-align: center;
    padding: 44px 20px;
    color: var(--text-3);
  }
  .mk-empty .mk-ic { width: 30px; height: 30px; color: var(--border-strong); }
  .mk-empty b {
    display: block;
    color: var(--text);
    font-size: var(--fs-md);
    margin: 10px 0 4px;
  }
  .mk-empty p { margin: 0 auto; max-width: 42ch; font-size: var(--fs-sm); line-height: 1.55; }
}

@layer meridian.components {
  /* A plain list of records — files, members, saved views — with active and
     actionable states. Deliberately not `.mk-menu-item`, which carries menu
     semantics, nor `.mk-sidebar-item`, which is navigation: this is a list of
     things, and what it is is decided by the element the author uses.

     CSS only. A `<ul>` of `<li>`s needs no JavaScript, and anything that would
     want some — selection, keyboard traversal — is `Tree` or `DataGrid`. */
  .mk-list {
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
    overflow: hidden;
  }

  .mk-list-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 12px;
    font-size: var(--fs-sm);
    color: var(--text);
    text-align: start;
    background: none;
    border: 0;
    /* Only between items, so the list's own border is not doubled. */
    border-top: 1px solid var(--border);
    text-decoration: none;
  }
  .mk-list-item:first-child { border-top: 0; }
  .mk-list-item .mk-ic { width: 15px; height: 15px; flex: none; color: var(--text-3); }

  /* An `<a>` or `<button>` item is interactive; an `<li>` or `<div>` is not, and
     must not pretend to be with a hover state it cannot honour. */
  a.mk-list-item,
  button.mk-list-item { cursor: pointer; }
  a.mk-list-item:hover,
  button.mk-list-item:hover { background: var(--surface-2); }

  .mk-list-item.is-active,
  .mk-list-item[aria-current] {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 620;
  }
  .mk-list-item.is-active .mk-ic,
  .mk-list-item[aria-current] .mk-ic { color: var(--accent); }

  .mk-list-item[aria-disabled="true"],
  .mk-list-item:disabled {
    opacity: .5;
    cursor: not-allowed;
    background: none;
  }

  /* A second line, for the common "name over detail" row. */
  .mk-list-main { min-width: 0; flex: 1; }
  .mk-list-sub {
    display: block;
    font-size: var(--fs-xs);
    color: var(--text-3);
    margin-top: 1px;
  }
  .mk-list-end { margin-left: auto; flex: none; color: var(--text-3); font-size: var(--fs-xs); }

  /* Flush: no outer border or rounding, for a list already inside a panel. */
  .mk-list-flush {
    border: 0;
    border-radius: 0;
    background: none;
  }
  .mk-list-flush > .mk-list-item:first-child { border-top: 1px solid var(--border); }
  .mk-list-flush > .mk-list-item:last-child { border-bottom: 1px solid var(--border); }
}

@layer meridian.components {
  .mk-accordion {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
    overflow: hidden;
  }
  .mk-accordion-item + .mk-accordion-item { border-top: 1px solid var(--border); }

  .mk-accordion-trigger {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 14px;
    border: 0;
    background: none;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 620;
    color: var(--text);
    text-align: left;
    cursor: pointer;
  }
  .mk-accordion-trigger:hover { background: var(--surface-2); }

  /* The marker is drawn here rather than left to the author, because it is the
     only thing distinguishing a header from a row of text. */
  .mk-accordion-trigger::after {
    content: "";
    margin-left: auto;
    width: 8px;
    height: 8px;
    flex: none;
    border-right: 2px solid currentcolor;
    border-bottom: 2px solid currentcolor;
    opacity: .5;
    transform: translateY(-2px) rotate(45deg);
    transition: transform var(--dur-fast) ease;
  }
  .mk-accordion-trigger[aria-expanded="true"]::after {
    transform: translateY(1px) rotate(225deg);
    opacity: .8;
  }

  .mk-accordion-panel {
    padding: 0 14px 14px;
    font-size: var(--fs-sm);
    color: var(--text-2);
    line-height: 1.55;
  }

  /* Flush variant: dividers only, for a panel that already has a border. */
  .mk-accordion-flush {
    border: 0;
    border-radius: 0;
    background: none;
  }
  .mk-accordion-flush .mk-accordion-trigger { padding-left: 0; padding-right: 0; }
  .mk-accordion-flush .mk-accordion-panel { padding-left: 0; padding-right: 0; }
}

@layer meridian.components {
  .mk-tree,
  .mk-tree-group {
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .mk-tree-group[hidden] { display: none; }

  /* Indent per level, driven by aria-level rather than by nesting: the depth a
     screen reader announces and the depth the eye sees are then the same
     number, and a flattened or virtualised tree would indent identically. */
  .mk-tree-label {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px 4px calc(6px + (var(--mk-tree-level, 1) - 1) * 16px);
    border-radius: var(--r-md);
    font-size: var(--fs-sm);
    color: var(--text);
    cursor: default;
    user-select: none;
    min-height: 26px;
  }
  .mk-tree-item[aria-level="2"] > .mk-tree-label { --mk-tree-level: 2; }
  .mk-tree-item[aria-level="3"] > .mk-tree-label { --mk-tree-level: 3; }
  .mk-tree-item[aria-level="4"] > .mk-tree-label { --mk-tree-level: 4; }
  .mk-tree-item[aria-level="5"] > .mk-tree-label { --mk-tree-level: 5; }
  .mk-tree-item[aria-level="6"] > .mk-tree-label { --mk-tree-level: 6; }
  /* Deeper than six, the indent stops growing rather than pushing the label
     off the edge of a narrow panel. */

  .mk-tree-label:hover { background: var(--surface-2); }

  .mk-tree-item[aria-selected="true"] > .mk-tree-label {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
  }

  /* The focus ring belongs on the row, but focus sits on the <li>, which also
     contains the whole subtree — outlining that would ring the branch. */
  .mk-tree-item:focus { outline: none; }
  .mk-tree-item:focus-visible > .mk-tree-label {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }

  .mk-tree-item[aria-disabled="true"] > .mk-tree-label {
    opacity: .45;
    cursor: not-allowed;
  }

  .mk-tree-twisty {
    width: 14px;
    height: 14px;
    flex: none;
    display: grid;
    place-items: center;
    color: var(--text-3);
    cursor: pointer;
  }
  /* A leaf keeps the space so labels line up, but shows nothing to click. */
  .mk-tree-item:not([aria-expanded]) > .mk-tree-label > .mk-tree-twisty {
    visibility: hidden;
    cursor: default;
  }
  .mk-tree-twisty::before {
    content: "";
    width: 5px;
    height: 5px;
    border-right: 1.6px solid currentcolor;
    border-bottom: 1.6px solid currentcolor;
    transform: rotate(-45deg) translate(-1px, -1px);
    transition: transform var(--dur-fast) ease;
  }
  .mk-tree-item[aria-expanded="true"] > .mk-tree-label > .mk-tree-twisty::before {
    transform: rotate(45deg) translate(-1px, -1px);
  }

  /* Loading a branch: the twisty becomes the spinner, in place. */
  .mk-tree-item.is-busy > .mk-tree-label > .mk-tree-twisty::before {
    width: 10px;
    height: 10px;
    border: 1.6px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: var(--r-pill);
    transform: none;
    animation: mk-spin .7s linear infinite;
  }

  .mk-tree-label .mk-ic { width: 15px; height: 15px; color: var(--text-3); flex: none; }
  .mk-tree-item[aria-selected="true"] > .mk-tree-label .mk-ic { color: var(--accent); }
}

@layer meridian.components {
  /* Moved here from @meridian/app's page chrome: a trail of ancestors is the
     same control in a docs site, a file manager and an admin table. The page's
     own spacing stayed behind in app. */
  .mk-crumbs {
    font-size: var(--fs-sm);
    color: var(--text-3);
    min-width: 0;
  }
  .mk-crumbs a { color: var(--text-3); text-decoration: none; }
  .mk-crumbs a:hover { color: var(--accent); text-decoration: underline; }
  .mk-crumbs .mk-ic { width: 13px; height: 13px; }

  .mk-crumbs-list {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 6px;
    list-style: none;
    margin: 0;
    padding: 0;
    min-width: 0;
  }

  .mk-crumb {
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    min-width: 0;
  }
  .mk-crumb[hidden] { display: none; }

  /* The separator is drawn, not inserted. An element in the DOM would be a
     crumb as far as a screen reader is concerned, and would have to be added
     and removed around every collapse. */
  .mk-crumb + .mk-crumb::before {
    content: "/";
    color: var(--border-strong);
    user-select: none;
  }

  /* Only the current page truncates. Shortening an ancestor to "Pro…" saves a
     few pixels and costs the one piece of information it carried. */
  .mk-crumb:last-child {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 3ch;
  }
  .mk-crumb [aria-current="page"] { color: var(--text); font-weight: 600; }

  .mk-crumb-more {
    border: 0;
    background: none;
    font: inherit;
    color: var(--text-3);
    cursor: pointer;
    padding: 0 4px;
    border-radius: var(--r-sm);
    line-height: 1;
  }
  .mk-crumb-more:hover { background: var(--surface-3); color: var(--text); }
  .mk-crumb-more[aria-expanded="true"] { background: var(--surface-3); color: var(--text); }
}

@layer meridian.components {
  .mk-navbar {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 0 12px;
    min-height: 46px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
  }

  .mk-navbar-brand {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: none;
    font-family: var(--font-brand);
    font-size: var(--fs-lg);
    color: var(--text);
    text-decoration: none;
    white-space: nowrap;
  }

  .mk-navbar-nav {
    display: flex;
    align-items: center;
    gap: 2px;
    list-style: none;
    margin: 0;
    padding: 0;
    /* min-width lets the row shrink below its content, and overflow hidden is
       what makes scrollWidth exceed clientWidth once it does — which is the
       measurement the overflow menu is driven by. */
    flex: 1;
    min-width: 0;
    overflow: hidden;
  }
  /* `display: flex` outranks the UA sheet's `[hidden] { display: none }`, so a
     collapsed panel would stay on screen. Same trap as .mk-tree-group. */
  .mk-navbar-nav[hidden],
  .mk-navbar-panel[hidden],
  .mk-navbar-item[hidden] { display: none; }

  .mk-navbar-item { flex: none; }

  .mk-navbar-link {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 9px;
    border: 0;
    background: none;
    border-radius: var(--r-md);
    color: var(--text-2);
    font: inherit;
    font-size: var(--fs-sm);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
  }
  .mk-navbar-link .mk-ic { width: 15px; height: 15px; }
  .mk-navbar-link:hover { background: var(--surface-3); color: var(--text); }
  /* Any `aria-current`, not only "page": `Scrollspy` marks a position within the
     document with "location", which is the same visual state. */
  .mk-navbar-link[aria-current] {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 620;
  }
  /* A link that opens a menu says so. Drawn rather than shipped as an icon, so
     a bar built from plain markup needs no sprite. */
  .mk-navbar-link[aria-haspopup]::after {
    content: "";
    width: 5px;
    height: 5px;
    margin-left: 1px;
    border-right: 1.5px solid currentcolor;
    border-bottom: 1.5px solid currentcolor;
    transform: translateY(-2px) rotate(45deg);
  }

  .mk-navbar-end {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: none;
    margin-left: auto;
  }

  /* The toggle is hidden by the component in bar mode rather than by a media
     query, so the breakpoint lives in one place: the `breakpoint` option. */
  .mk-navbar-toggle {
    display: grid;
    place-items: center;
    flex: none;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    background: none;
    border-radius: var(--r-md);
    color: var(--text-2);
    cursor: pointer;
  }
  .mk-navbar-toggle:hover { background: var(--surface-3); color: var(--text); }
  .mk-navbar-toggle[hidden] { display: none; }

  /* Three bars, in one element: two are the ::before/::after of the middle. */
  .mk-navbar-burger,
  .mk-navbar-burger::before,
  .mk-navbar-burger::after {
    display: block;
    width: 15px;
    height: 1.5px;
    border-radius: var(--r-pill);
    background: currentcolor;
  }
  .mk-navbar-burger {
    position: relative;
  }
  .mk-navbar-burger::before,
  .mk-navbar-burger::after {
    content: "";
    position: absolute;
    left: 0;
  }
  .mk-navbar-burger::before { top: -5px; }
  .mk-navbar-burger::after { top: 5px; }

  /* Compact: the links stop being a row and become a panel under the bar. */
  .mk-navbar[data-mk-navbar-mode="compact"] {
    flex-wrap: wrap;
    row-gap: 0;
  }
  .mk-navbar[data-mk-navbar-mode="compact"] .mk-navbar-nav,
  .mk-navbar[data-mk-navbar-mode="compact"] .mk-navbar-panel {
    flex-direction: column;
    align-items: stretch;
    /* Full width on its own row, below everything already in the bar. */
    flex-basis: 100%;
    order: 99;
    gap: 1px;
    padding: 4px 0 10px;
    overflow: visible;
  }
  .mk-navbar[data-mk-navbar-mode="compact"] .mk-navbar-link {
    padding: 8px 10px;
    font-size: var(--fs-md);
  }
  /* The toggle sits at the end of the bar's own row, after any actions — the
     conventional place for it, and the DOM order stays "toggle before the links
     it reveals" so Tab still reaches it first. */
  .mk-navbar[data-mk-navbar-mode="compact"] .mk-navbar-end {
    order: 97;
    margin-left: auto;
  }
  .mk-navbar[data-mk-navbar-mode="compact"] .mk-navbar-toggle { order: 98; }
}

@layer meridian.components {
  /* The sidebar owns its own width. It used to be a column on the app shell's
     grid, which meant the shell had to know the sidebar's collapsed state — two
     owners of one fact. Now the shell is `auto 1fr` and this is the only rule
     that decides how wide the navigation is. */
  .mk-sidebar {
    width: var(--nav-w);
    flex: none;
    background: var(--nav-bg);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    min-height: 0;
    position: relative;
    overflow: hidden;
    transition: width var(--dur-mid) ease;
  }
  .mk-sidebar[data-mk-sidebar-mode="rail"] { width: var(--nav-rail); }

  .mk-sidebar-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 10px 10px;
  }
  .mk-sidebar-icon {
    width: 30px;
    height: 30px;
    border-radius: var(--r-md);
    background: var(--epic-soft);
    color: var(--epic);
    display: grid;
    place-items: center;
    flex: none;
  }
  .mk-sidebar-icon .mk-ic { width: 17px; height: 17px; }
  .mk-sidebar-meta { min-width: 0; }
  .mk-sidebar-name {
    font-size: var(--fs-sm);
    font-weight: 640;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .mk-sidebar-sub { font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }

  .mk-sidebar-body { overflow-y: auto; padding: 2px 8px 12px; flex: 1; }

  /* Generated: a heading plus the items under it, as one labelled group. */
  .mk-sidebar-items[hidden] { display: none; }

  .mk-sidebar-label {
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--text-3);
    padding: 14px 8px 5px;
    white-space: nowrap;
  }

  /* A collapsible heading is a button, and inherits the heading's own look
     rather than a button's. */
  .mk-sidebar-section {
    display: flex;
    align-items: center;
    gap: 5px;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    cursor: pointer;
    text-align: start;
  }
  .mk-sidebar-section:hover { color: var(--text-2); }
  .mk-sidebar-twisty {
    width: 8px;
    height: 8px;
    flex: none;
    display: grid;
    place-items: center;
  }
  .mk-sidebar-twisty::before {
    content: "";
    width: 4px;
    height: 4px;
    border-right: 1.5px solid currentcolor;
    border-bottom: 1.5px solid currentcolor;
    transform: rotate(45deg) translate(-1px, -1px);
    transition: transform var(--dur-fast) ease;
  }
  .mk-sidebar-section[aria-expanded="false"] .mk-sidebar-twisty::before {
    transform: rotate(-45deg) translate(-1px, -1px);
  }

  .mk-sidebar-item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border: 0;
    background: none;
    border-radius: var(--r-md);
    color: var(--text-2);
    font: inherit;
    font-size: var(--fs-sm);
    text-align: start;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    position: relative;
  }
  .mk-sidebar-item .mk-ic { width: 16px; height: 16px; flex: none; }
  .mk-sidebar-item:hover { background: var(--surface-3); color: var(--text); }
  .mk-sidebar-item[aria-current] {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 620;
  }
  .mk-sidebar-item[aria-current]::before {
    content: "";
    position: absolute;
    left: -8px;
    top: 5px;
    bottom: 5px;
    width: 3px;
    border-radius: 0 2px 2px 0;
    background: var(--accent);
  }

  .mk-sidebar-text {
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .mk-sidebar-count {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    color: var(--text-3);
    background: var(--surface-3);
    border-radius: var(--r-pill);
    padding: 1px 6px;
  }
  .mk-sidebar-item[aria-current] .mk-sidebar-count {
    background: var(--surface);
    color: var(--accent);
  }

  .mk-sidebar-foot {
    border-top: 1px solid var(--border);
    padding: 8px;
    display: flex;
    align-items: center;
    gap: 6px;
  }

  /* The rail. Everything that carries words goes; the icons and the rhythm of
     the list stay. The accessible name does not disappear with the text — the
     component moves it to aria-label, because a button holding an icon and a
     display:none span has no name at all. */
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-meta,
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-count,
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-text,
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-foot .mk-text-xs { display: none; }
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-item { justify-content: center; padding: 8px 0; }
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-head { justify-content: center; padding: 12px 0 10px; }
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-foot { justify-content: center; }
  /* The heading keeps its box so the list does not re-space when the rail
     appears, but its text is pushed out of sight rather than removed — a
     display:none heading would take its group's aria-labelledby target with it. */
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-label {
    height: 12px;
    padding: 8px 0 0;
    overflow: hidden;
    text-indent: -999px;
  }
  .mk-sidebar[data-mk-sidebar-mode="rail"] .mk-sidebar-twisty { display: none; }
}

@layer meridian.components {
  /* Laid out as a table, deliberately.
     `display: grid` or `flex` on a <table> strips its implicit roles in every
     browser — no table, no rows, no cells — which would throw away every
     `scope` the component wires and every header association that depends on
     them. The old `.mk-props` was a <dl> laid out with grid, where there was no
     table semantics to lose; this is not that. */
  .mk-props {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
  }

  .mk-props caption {
    text-align: start;
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: .09em;
    font-weight: 700;
    color: var(--text-3);
    padding-bottom: 8px;
  }

  .mk-props th,
  .mk-props td {
    text-align: start;
    vertical-align: top;
    padding: 7px 12px 7px 0;
  }
  .mk-props th:last-child,
  .mk-props td:last-child { padding-right: 0; }

  .mk-props thead th {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: .08em;
    font-weight: 700;
    color: var(--text-3);
    white-space: nowrap;
    border-bottom: 1px solid var(--border);
    padding-bottom: 6px;
  }

  /* The property column is as narrow as its longest name: `width: 1%` on a
     table cell means "shrink to fit" once something else can take the slack. */
  .mk-props tbody th {
    width: 1%;
    white-space: nowrap;
    font-weight: 500;
    color: var(--text-3);
  }
  .mk-props tbody td { color: var(--text); }

  /* Three columns: the value stays narrow too and the description takes the
     rest. Two columns: the value is what takes the rest, so it must not be
     pinned — which is why this keys off the count the component measured. */
  .mk-props[data-mk-props-cols="3"] tbody td:first-of-type {
    width: 1%;
    white-space: nowrap;
  }

  .mk-props tbody tr + tr > th,
  .mk-props tbody tr + tr > td,
  .mk-props tbody + tbody > tr > th,
  .mk-props tbody + tbody > tr > td { border-top: 1px solid var(--border); }

  /* `display: table-row` from the UA sheet outranks nothing, but a filtered row
     has to disappear rather than merely be marked — the same trap as the flex
     containers elsewhere, in the one place it is easiest to miss. */
  .mk-props tr[hidden] { display: none; }

  /* A group heading spans the table and is not a property. */
  .mk-props-group > th {
    padding-top: 16px;
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: .09em;
    font-weight: 700;
    color: var(--text-2);
    white-space: nowrap;
    width: auto;
  }
  .mk-props tbody:first-child .mk-props-group:first-child > th { padding-top: 7px; }

  /* An empty cell: the dash is the visual half, and the component puts the
     spoken half beside it in an .mk-sr span. */
  .mk-props td.is-empty { color: var(--text-3); }

  /* The value and its copy button share the cell, which is why the value is
     wrapped: the button must not end up inside what gets copied. */
  .mk-props-value { min-width: 0; }

  .mk-props-copy {
    display: inline-grid;
    place-items: center;
    width: 20px;
    height: 20px;
    margin-left: 6px;
    padding: 0;
    border: 0;
    background: none;
    border-radius: var(--r-sm);
    color: var(--text-3);
    cursor: pointer;
    vertical-align: -3px;
    /* Out of the way until wanted, but never out of the tab order — it fades in
       on focus as well as hover, so reaching it by keyboard is not a guess. */
    opacity: 0;
    transition: opacity var(--dur-fast) ease;
  }
  .mk-props-copy .mk-ic { width: 13px; height: 13px; }
  /* `:focus`, not `:focus-visible`. The usual reason to prefer the latter is to
     suppress a ring after a mouse click, and this is not a ring — it is a control
     that is otherwise invisible, so *any* focus arriving here needs it shown,
     including a programmatic one. `:focus-visible` is a browser heuristic that
     answers "no" often enough to leave a focused button unfindable. */
  .mk-props tr:hover .mk-props-copy,
  .mk-props-copy:focus { opacity: 1; }
  .mk-props-copy:hover { background: var(--surface-3); color: var(--text); }
  .mk-props-copy.is-on { opacity: 1; color: var(--ok); }
  .mk-props-copy.is-bad { opacity: 1; color: var(--danger); }

  /* A pointer that cannot hover has no way to reveal it. */
  @media (hover: none) {
    .mk-props-copy { opacity: 1; }
  }
}

@layer meridian.components {
  .mk-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .mk-timeline-item {
    position: relative;
    display: flex;
    gap: 10px;
    padding-bottom: 14px;
  }
  /* `display: flex` on the item outranks the UA sheet's `[hidden]{display:none}`,
     so a collapsed item would stay on screen. Same trap as .mk-tree-group. */
  .mk-timeline-item[hidden],
  .mk-timeline-sep[hidden] { display: none; }

  /* The connector runs from under one dot to the next item's edge, drawn on the
     item rather than inserted as an element: a line in the DOM is a list item as
     far as a screen reader is concerned, and it would have to be added and
     removed around every collapse. */
  .mk-timeline-item::before {
    content: "";
    position: absolute;
    left: 9px;
    top: 21px;
    bottom: 1px;
    width: 1px;
    /* --border-strong, not --border: a hairline that runs the height of a feed
       has to read as a deliberate thread rather than a rendering artefact, and
       at 1px against --canvas the softer token disappears. */
    background: var(--border-strong);
  }
  /* The last item has nothing to connect to. Collapsed items are only ever at
     the start of the list, so the structural last child is also the visual one. */
  .mk-timeline-item:last-child::before { display: none; }

  .mk-timeline-marker {
    position: relative;
    flex: none;
    box-sizing: border-box;
    width: 18px;
    height: 18px;
    margin-top: 1px;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--text-3);
    display: grid;
    place-items: center;
  }
  /* An empty disc draws a dot; one holding an icon shows the icon instead. */
  .mk-timeline-marker:empty::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: var(--r-pill);
    background: var(--border-strong);
  }
  .mk-timeline-marker .mk-ic { width: 11px; height: 11px; }

  .mk-timeline-body {
    min-width: 0;
    flex: 1;
    font-size: var(--fs-sm);
    color: var(--text);
    padding-top: 1px;
  }
  .mk-timeline-body > :first-child { margin-top: 0; }
  .mk-timeline-body > :last-child { margin-bottom: 0; }

  .mk-timeline-time {
    display: block;
    font-size: var(--fs-xs);
    color: var(--text-3);
    /* The relative text changes width as it re-ticks; tabular figures stop the
       line reflowing under the reader once a minute. */
    font-variant-numeric: tabular-nums;
  }
  /* Empty when the item's only label would have repeated its day heading. After
     the rule above, not before: equal specificity means the later one wins. */
  .mk-timeline-time:empty { display: none; }

  /* Tones. The item carries the state class so the disc and anything the author
     puts beside it can both respond to it. */
  .mk-timeline-item.is-ok > .mk-timeline-marker { border-color: var(--ok); color: var(--ok); }
  .mk-timeline-item.is-ok > .mk-timeline-marker:empty::before { background: var(--ok); }
  .mk-timeline-item.is-warn > .mk-timeline-marker { border-color: var(--warn); color: var(--warn); }
  .mk-timeline-item.is-warn > .mk-timeline-marker:empty::before { background: var(--warn); }
  .mk-timeline-item.is-danger > .mk-timeline-marker { border-color: var(--danger); color: var(--danger); }
  .mk-timeline-item.is-danger > .mk-timeline-marker:empty::before { background: var(--danger); }
  .mk-timeline-item.is-active > .mk-timeline-marker {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--on-accent);
  }
  .mk-timeline-item.is-active > .mk-timeline-marker:empty::before { background: var(--on-accent); }

  /* Day heading. The connector stops at it deliberately — a break in the line is
     what makes the grouping read as a grouping. */
  .mk-timeline-sep {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 2px 0 10px;
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: .08em;
    font-weight: 700;
    color: var(--text-3);
  }
  .mk-timeline-sep::after {
    content: "";
    height: 1px;
    flex: 1;
    background: var(--border);
  }

  .mk-timeline-more {
    display: flex;
    padding-bottom: 14px;
    padding-left: 28px;
  }
}

@layer meridian.components {
  .mk-steps {
    display: flex;
    align-items: center;
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .mk-step {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    flex: 1;
  }
  /* Nothing follows the last stage, so it takes only the room it needs rather
     than stretching its (absent) connector across the leftover space. */
  .mk-step:last-child { flex: 0 1 auto; }

  /* The connector is part of the flex row, not an absolute overlay: it takes
     whatever space the labels leave, which is what keeps four stages of very
     different label lengths evenly spread. */
  .mk-step:not(:last-child)::after {
    content: "";
    flex: 1;
    min-width: 16px;
    height: 1px;
    margin: 0 8px;
    background: var(--border);
  }
  /* A stage already behind the user is joined to the next one in accent — the
     line is the progress bar of a stepper. */
  .mk-step[data-mk-step-state="done"]:not(:last-child)::after { background: var(--accent); }

  .mk-step-marker {
    box-sizing: border-box;
    flex: none;
    width: 22px;
    height: 22px;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--text-3);
    display: grid;
    place-items: center;
    font-size: var(--fs-xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .mk-step-marker .mk-ic { width: 13px; height: 13px; }

  .mk-step-label {
    min-width: 0;
    font-size: var(--fs-sm);
    color: var(--text-2);
  }

  .mk-step[data-mk-step-state="done"] > .mk-step-marker {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
  }
  .mk-step[data-mk-step-state="current"] > .mk-step-marker {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--on-accent);
  }
  .mk-step[data-mk-step-state="current"] > .mk-step-label {
    color: var(--text);
    font-weight: 650;
  }

  /* A revisitable stage is a <button> the author wrote. Zero-specificity reset
     so an .mk-btn put there deliberately still wins. */
  .mk-step-label :where(button) {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: inherit;
    text-align: start;
    cursor: pointer;
  }
  .mk-step-label :where(button:hover) { color: var(--accent); }
  .mk-step-label :where(button:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-sm);
  }

  /* Author tones come last so a stage that failed outranks its position. */
  .mk-step.is-warn > .mk-step-marker {
    border-color: var(--warn);
    background: var(--warn-soft);
    color: var(--warn);
  }
  .mk-step.is-danger > .mk-step-marker {
    border-color: var(--danger);
    background: var(--danger-soft);
    color: var(--danger);
  }

  /* Vertical: the connector becomes a line down the left, the same one the
     timeline draws, because at this point they are the same picture. */
  .mk-steps-vertical {
    display: block;
  }
  .mk-steps-vertical .mk-step {
    align-items: flex-start;
    padding-bottom: 16px;
  }
  .mk-steps-vertical .mk-step::after { display: none; }
  .mk-steps-vertical .mk-step:not(:last-child)::before {
    content: "";
    position: absolute;
    left: 11px;
    top: 26px;
    bottom: 1px;
    width: 1px;
    background: var(--border);
  }
  .mk-steps-vertical .mk-step[data-mk-step-state="done"]:not(:last-child)::before {
    background: var(--accent);
  }
  .mk-steps-vertical .mk-step:last-child { padding-bottom: 0; }
}

@layer meridian.components {
  .mk-progress {
    height: 6px;
    border-radius: var(--r-pill);
    background: var(--surface-3);
    overflow: hidden;
    display: flex;
  }
  /* Bare child selector kept for markup that predates the component: a hand
     written <span> inside .mk-progress still fills correctly. */
  .mk-progress > * { display: block; height: 100%; }

  .mk-progress-bar {
    background: var(--accent);
    transition: width var(--dur-mid) ease;
    /* A zero-width bar must not paint a rounded sliver of accent. */
    min-width: 0;
  }
  .mk-progress-ok .mk-progress-bar,
  .mk-progress-bar.is-ok { background: var(--ok); }
  .mk-progress-warn .mk-progress-bar,
  .mk-progress-bar.is-warn { background: var(--warn); }
  .mk-progress-danger .mk-progress-bar,
  .mk-progress-bar.is-danger { background: var(--danger); }

  .mk-progress-sm { height: 4px; }
  .mk-progress-lg { height: 10px; }

  /* Indeterminate: a fixed-width sliver travelling the track, because there is
     no proportion to show. The bar keeps its full height so the control does
     not change size when the total becomes known. */
  .mk-progress.is-indeterminate .mk-progress-bar {
    width: 35%;
    transition: none;
    animation: mk-progress-slide 1.1s ease-in-out infinite;
  }
  @keyframes mk-progress-slide {
    from { transform: translateX(-110%); }
    to { transform: translateX(320%); }
  }

  /* The travel *is* the message — "still working" — so it is slowed rather
     than stopped, the same call the spinner makes. */
  @media (prefers-reduced-motion: reduce) {
    .mk-progress.is-indeterminate .mk-progress-bar { animation-duration: 3s; }
    .mk-progress-bar { transition: none; }
  }

  .mk-progress-label {
    font-size: var(--fs-xs);
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
  }
}

@layer meridian.components {
  .mk-panes { display: flex; min-height: 0; min-width: 0; }
  .mk-panes-stacked { flex-direction: column; }
  /* Both panes must be allowed to shrink, or flex-basis becomes a floor and
     the handle stops moving once the content inside is wider than its pane. */
  .mk-pane { min-width: 0; min-height: 0; overflow: auto; }

  .mk-splitter {
    flex: none;
    position: relative;
    background: var(--border);
    cursor: col-resize;
    /* 1px of paint, 9px of target. A 1px hit area is a 1px hit area. */
    width: 1px;
    margin: 0 4px;
    touch-action: none;
  }
  .mk-panes-stacked > .mk-splitter {
    cursor: row-resize;
    width: auto;
    height: 1px;
    margin: 4px 0;
  }
  .mk-splitter::before {
    content: "";
    position: absolute;
    inset: 0 -4px;
  }
  .mk-panes-stacked > .mk-splitter::before { inset: -4px 0; }

  .mk-splitter:hover,
  .mk-splitter.is-dragging { background: var(--accent); }
  .mk-splitter:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* Set on <html> for the length of a drag: the pointer leaves the handle
     immediately, so a cursor and a selection guard on the handle alone would
     flicker and select text across both panes. */
  .mk-resizing,
  .mk-resizing * {
    cursor: col-resize !important;
    user-select: none !important;
  }
  .mk-resizing-h,
  .mk-resizing-h * { cursor: row-resize !important; }
}

@layer meridian.components {
  .mk-dropzone {
    border: 1.5px dashed var(--border-strong);
    border-radius: var(--r-lg);
    background: var(--surface-2);
    padding: 18px 16px;
    text-align: center;
    font-size: var(--fs-sm);
    color: var(--text-2);
    cursor: pointer;
    transition:
      border-color var(--dur-fast) ease,
      background var(--dur-fast) ease;
  }
  .mk-dropzone:hover { border-color: var(--accent); }
  /* Focus lives on the input inside, so the zone shows the ring for it. */
  .mk-dropzone:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  .mk-dropzone.is-over {
    border-color: var(--accent);
    background: var(--accent-soft);
  }
  /* Children only — never the zone itself, which has to keep receiving the
     drop. Without this the hint text steals dragleave and the highlight
     flickers as the pointer crosses it. */
  .mk-dropzone.is-over * { pointer-events: none; }
  .mk-dropzone.is-empty .mk-dropzone-list { display: none; }

  .mk-dropzone-hint { margin: 0; }
  .mk-dropzone-hint b { color: var(--text); font-weight: 600; }

  .mk-dropzone-list {
    list-style: none;
    margin: 12px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    text-align: left;
  }
  .mk-dropzone-list:empty { display: none; }

  .mk-dropzone-file {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px 6px 10px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    cursor: default;
  }
  .mk-dropzone-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text);
  }
  .mk-dropzone-size {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-xs);
    color: var(--text-3);
    flex: none;
  }
  .mk-dropzone-empty { color: var(--text-3); font-size: var(--fs-xs); }
}

@layer meridian.components {
  .mk-toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: 12px;
  }
  .mk-toolbar-search { position: relative; width: 220px; }
  .mk-toolbar-search .mk-ic {
    position: absolute;
    left: 9px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-3);
    width: 15px;
    height: 15px;
    pointer-events: none;
  }
  .mk-toolbar-search .mk-input { height: 32px; padding-left: 30px; }
  .mk-toolbar-spacer { flex: 1; }

  .mk-table-wrap {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    box-shadow: var(--shadow-1);
  }
  .mk-table-scroll { overflow-x: auto; }
  /* Vertical scrolling is opt-in: a grid that scrolls by default would trap
     the page's own scroll on touch. `height` (or CSS) turns it on. */
  .mk-table-virtual { overflow-y: auto; }
  /* Spacer rows stand in for everything not rendered. They hold height and
     nothing else — no borders, no hover, no padding to round the maths off. */
  .mk-row-spacer,
  .mk-row-spacer > td { padding: 0; border: 0; background: none; }
  .mk-row-spacer:hover > td { background: none; }

  table.mk-table {
    width: 100%;
    min-width: 940px;
    border-collapse: collapse;
    font-size: var(--fs-sm);
  }
  .mk-table thead th {
    position: sticky;
    top: 0;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--text-3);
    padding: 0;
    white-space: nowrap;
    z-index: 2;
  }
  .mk-table thead th.mk-th-plain { padding: 9px 12px; }

  .mk-th-btn {
    width: 100%;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: 9px 12px;
    background: none;
    border: 0;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
  }
  .mk-th-btn:hover { background: var(--surface-3); color: var(--text-2); }
  .mk-th-btn .mk-ic { width: 12px; height: 12px; opacity: 0; }
  .mk-th-btn:hover .mk-ic { opacity: .5; }
  .mk-table th[aria-sort] .mk-th-btn { color: var(--text); }
  .mk-table th[aria-sort] .mk-th-btn .mk-ic { opacity: 1; color: var(--accent); }
  .mk-table th[aria-sort="descending"] .mk-th-btn .mk-ic { transform: rotate(180deg); }

  .mk-table tbody td {
    padding: 0 12px;
    height: 40px;
    border-bottom: 1px solid var(--border);
    color: var(--text-2);
    vertical-align: middle;
    white-space: nowrap;
  }
  .mk-table tbody tr:last-child td { border-bottom: 0; }
  .mk-table-rows-interactive tbody tr { cursor: pointer; }
  .mk-table tbody tr:hover td { background: var(--surface-2); }
  .mk-table tbody tr.is-selected td { background: var(--accent-soft); }
  .mk-table tbody tr.is-selected:hover td { background: var(--accent-soft); filter: brightness(.985); }

  .mk-cell-select { width: 36px; }
  .mk-table tbody td.mk-cell-select { padding-left: 12px; }
  .mk-cell-select input {
    width: 14px;
    height: 14px;
    accent-color: var(--accent);
    cursor: pointer;
    display: block;
  }
  .mk-cell-key { width: 96px; }
  .mk-cell-main { color: var(--text); font-weight: 520; min-width: 300px; }
  .mk-cell-num {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    text-align: right;
    width: 76px;
  }
  .mk-cell-date {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-xs);
    width: 104px;
  }
  .mk-cell-actions { width: 44px; text-align: right; }
  .mk-table tbody td.mk-cell-actions { padding-right: 8px; }

  /* Row actions stay hidden until the row is hovered or holds focus —
     :focus-within is what keeps them reachable by keyboard. */
  .mk-table tbody tr .mk-btn-icon { opacity: 0; }
  .mk-table tbody tr:hover .mk-btn-icon,
  .mk-table tbody tr:focus-within .mk-btn-icon { opacity: 1; }

  .mk-cell-inline { display: flex; align-items: center; gap: var(--space-2); }
  .mk-cell-inline > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 42ch;
  }

  .mk-table-foot {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 9px 12px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
    font-size: var(--fs-sm);
    color: var(--text-2);
    flex-wrap: wrap;
  }

  .mk-pager { margin-left: auto; display: flex; align-items: center; gap: var(--space-1); }
  .mk-page-btn {
    min-width: 26px;
    height: 26px;
    border: 0;
    background: none;
    border-radius: var(--r-sm);
    font-size: var(--fs-sm);
    font-family: var(--font-mono);
    color: var(--text-2);
    cursor: pointer;
  }
  .mk-page-btn:hover { background: var(--surface-3); color: var(--text); }
  .mk-page-btn[aria-current="true"],
  .mk-page-btn[aria-current="page"] {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 700;
  }
  /* Dimmed rather than removed: an arrow that vanishes at the ends shifts
     every page number sideways on the first and last page. */
  .mk-page-btn:is([disabled], [aria-disabled="true"]) {
    opacity: .4;
    cursor: default;
    pointer-events: none;
  }
  .mk-page-nav { display: inline-grid; place-items: center; }
  .mk-page-nav .mk-ic { width: 15px; height: 15px; }
  .mk-page-gap {
    min-width: 20px;
    text-align: center;
    color: var(--text-3);
    user-select: none;
  }

  .mk-bulkbar {
    display: none;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    background: var(--accent-soft);
    border-bottom: 1px solid var(--border);
    font-size: var(--fs-sm);
    color: var(--accent);
    font-weight: 600;
  }
  .mk-bulkbar.is-on { display: flex; }
}

@layer meridian.components {
  /* Shared floating surface for menus, comboboxes and filter panels.
     Positioned by util/position.js; the CSS only owns appearance. */
  .mk-popover {
    position: fixed;
    z-index: var(--z-popover);
    min-width: 180px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-3);
    padding: 4px;
    display: none;
    max-height: min(420px, 70vh);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .mk-popover.is-open { display: block; animation: mk-popin .11s ease-out; }

  @keyframes mk-popin {
    from { opacity: 0; transform: translateY(-3px); }
  }

  .mk-menu-label {
    font-size: var(--fs-2xs);
    font-weight: 750;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--text-3);
    padding: 8px 10px 4px;
  }

  .mk-menu-item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 6px 10px;
    border: 0;
    background: none;
    border-radius: var(--r-md);
    font-size: var(--fs-sm);
    color: var(--text);
    text-align: left;
    cursor: pointer;
    white-space: nowrap;
  }
  .mk-menu-item .mk-ic { width: 15px; height: 15px; color: var(--text-3); }
  .mk-menu-item:hover,
  .mk-menu-item.is-active { background: var(--surface-3); }
  .mk-menu-item:hover .mk-ic,
  .mk-menu-item.is-active .mk-ic { color: var(--text-2); }

  .mk-menu-item .mk-shortcut {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    color: var(--text-3);
    padding-left: 20px;
  }

  .mk-menu-item.is-danger { color: var(--danger); }
  .mk-menu-item.is-danger .mk-ic { color: var(--danger); }
  .mk-menu-item.is-danger:hover { background: var(--danger-soft); }

  .mk-menu-item[disabled],
  .mk-menu-item[aria-disabled="true"] {
    opacity: .45;
    cursor: not-allowed;
    pointer-events: none;
  }

  .mk-menu-item[aria-checked] .mk-tick { opacity: 0; margin-left: auto; color: var(--accent); }
  .mk-menu-item[aria-checked="true"] .mk-tick { opacity: 1; }
  .mk-menu-item[aria-checked="true"] { color: var(--accent); font-weight: 600; }

  .mk-menu-sep { height: 1px; background: var(--border); margin: 4px 6px; }

  /* The chevron marks an item that leads somewhere rather than doing
     something, so a parent is never mistaken for a choice. */
  .mk-has-submenu::after {
    content: "";
    margin-left: auto;
    width: 0;
    height: 0;
    border: 4px solid transparent;
    border-left-color: currentcolor;
    opacity: .5;
    flex: none;
  }
  .mk-has-submenu[aria-expanded="true"] { background: var(--surface-3); }
  .mk-has-submenu[aria-expanded="true"]::after { opacity: 1; }
  .mk-menu-item.mk-has-submenu .mk-shortcut { margin-left: 0; }

  .mk-menu-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px 10px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 4px;
  }
  .mk-menu-head b { display: block; font-size: var(--fs-sm); }
  .mk-menu-head span { display: block; font-size: var(--fs-xs); color: var(--text-3); }

  /* Trigger caret flips with the popover's own expanded state. */
  .mk-has-caret .mk-caret {
    margin-left: 2px;
    opacity: .65;
    transition: transform .13s ease;
  }
  .mk-has-caret[aria-expanded="true"] .mk-caret { transform: rotate(180deg); }

  .mk-popover-foot {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    border-top: 1px solid var(--border);
    margin: 4px -4px -4px;
    padding: 8px 10px;
    background: var(--surface-2);
    border-radius: 0 0 5px 5px;
    position: sticky;
    bottom: -4px;
  }
  .mk-popover-search {
    position: sticky;
    top: -4px;
    background: var(--surface);
    padding: 4px 4px 6px;
    margin: -4px -4px 2px;
    border-bottom: 1px solid var(--border);
    z-index: 1;
  }
  .mk-popover-search .mk-input { height: 30px; }

  /* Popconfirm — a question, not a menu, so it gets padding and a prose width
     rather than the menu's flush 4px. */
  .mk-popconfirm {
    max-width: 264px;
    min-width: 200px;
    padding: 12px;
  }
  .mk-popconfirm-body {
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--text);
  }
  .mk-popconfirm-foot {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
    margin-top: 12px;
  }
}

@layer meridian.components {
  .mk-combo { position: relative; }
  .mk-combo-input { padding-right: 30px; }
  .mk-combo-caret {
    position: absolute;
    right: 9px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-3);
    pointer-events: none;
    width: 15px;
    height: 15px;
  }

  .mk-opt {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 6px 9px;
    border: 0;
    background: none;
    border-radius: var(--r-md);
    cursor: pointer;
    text-align: left;
    font-size: var(--fs-sm);
    color: var(--text);
  }
  .mk-opt:hover,
  .mk-opt.is-active { background: var(--surface-3); }
  /* The active option is the aria-activedescendant target — it needs a
     visual cue distinct from hover, since the two move independently. */
  .mk-opt.is-active { box-shadow: inset 0 0 0 1px var(--border-strong); }
  .mk-opt[aria-selected="true"] { background: var(--accent-soft); color: var(--accent); }

  .mk-opt-main { min-width: 0; flex: 1; }
  .mk-opt-value {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .mk-opt-sub { display: block; font-size: var(--fs-xs); color: var(--text-3); }
  .mk-opt-key {
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    color: var(--text-3);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    padding: 1px 5px;
    white-space: nowrap;
  }
  .mk-opt mark { background: none; color: var(--accent); font-weight: 720; }
  .mk-opt[aria-selected="true"] mark { color: inherit; text-decoration: underline; }

  .mk-opt-check {
    width: 15px;
    height: 15px;
    flex: none;
    color: var(--accent);
    opacity: 0;
  }
  .mk-opt[aria-selected="true"] .mk-opt-check { opacity: 1; }

  .mk-opt-empty {
    padding: 14px 10px;
    font-size: var(--fs-sm);
    color: var(--text-3);
    text-align: center;
  }
  .mk-opt-empty b { display: block; color: var(--text-2); margin-bottom: 2px; }

  .mk-opt-group {
    font-size: var(--fs-2xs);
    font-weight: 750;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--text-3);
    padding: 8px 10px 4px;
  }

  /* Multi-select token field. */
  .mk-tokens {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 5px;
    min-height: 34px;
    padding: 4px 6px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    cursor: text;
  }
  .mk-tokens:hover { border-color: var(--text-3); }
  .mk-tokens:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  .mk-tokens input {
    flex: 1;
    min-width: 90px;
    border: 0;
    outline: none;
    background: none;
    height: 24px;
    font-size: var(--fs-sm);
  }
  .mk-tokens input::placeholder { color: var(--text-3); }
  .mk-tokens[aria-disabled="true"] {
    background: var(--surface-2);
    cursor: not-allowed;
  }
}

@layer meridian.components {
  /* Faceted filter bar. Lives in core, not tracker: filtering a list by a set
     of fields is not domain vocabulary — it is the same control in an issue
     tracker, a CRM, a log viewer or an admin table. */
  .mk-filterbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding: 8px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-1);
  }

  .mk-fbtn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 30px;
    padding: 0 9px;
    border-radius: var(--r-md);
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--text-2);
    font-size: var(--fs-sm);
    font-weight: 560;
    cursor: pointer;
    white-space: nowrap;
  }
  .mk-fbtn:hover { background: var(--surface-2); color: var(--text); }
  .mk-fbtn .mk-ic { width: 14px; height: 14px; }
  .mk-fbtn.is-on {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 640;
  }

  .mk-fbtn-count {
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    font-weight: 700;
    background: var(--accent);
    color: var(--on-accent);
    border-radius: var(--r-pill);
    min-width: 16px;
    height: 16px;
    display: grid;
    place-items: center;
    padding: 0 4px;
  }

  .mk-filterbar-search { position: relative; width: 200px; }
  .mk-filterbar-search .mk-ic {
    position: absolute;
    left: 9px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-3);
    width: 15px;
    height: 15px;
    pointer-events: none;
  }
  .mk-filterbar-search .mk-input { height: 30px; padding-left: 30px; }

  /* Summary of what is currently applied, with per-chip removal. */
  .mk-activebar {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin: 10px 0 12px;
    font-size: var(--fs-sm);
    color: var(--text-3);
    min-height: 24px;
  }
}

@layer meridian.components {
  .mk-scrim {
    position: fixed;
    inset: 0;
    background: rgb(9 13 20 / 45%);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-mid) ease;
    z-index: var(--z-scrim);
  }
  .mk-scrim.is-open { opacity: 1; pointer-events: auto; }

  /* Dialogs are display:flex at all times and hidden via opacity/transform so
     they can animate. `inert` (set by JS while closed) is what actually keeps
     them out of the tab order — visibility alone would not. */
  .mk-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(560px, 92vw);
    background: var(--surface);
    border-left: 1px solid var(--border);
    box-shadow: var(--shadow-3);
    transform: translateX(100%);
    transition: transform var(--dur-slow) var(--ease);
    z-index: var(--z-dialog);
    display: flex;
    flex-direction: column;
  }
  .mk-drawer.is-open { transform: none; }

  .mk-drawer-start {
    right: auto;
    left: 0;
    border-left: 0;
    border-right: 1px solid var(--border);
    transform: translateX(-100%);
  }

  .mk-drawer-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
  }
  /* `flex: 1` with `min-height: 0` is what makes the body the part that
     scrolls. Without it the item cannot shrink under its content — flex items
     default to `min-height: auto` — so a long drawer overflowed the panel
     instead of scrolling inside it, and `overflow-y` never had anything to do. */
  .mk-drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 18px 32px; }
  /* No bottom margin: the title is a flex item in a centred row next to the
     close button, so 14px underneath only made the head 14px taller and sat
     the text 7px above the button it is supposed to line up with. */
  .mk-drawer-title {
    font-size: var(--fs-lg);
    font-weight: 650;
    margin: 0;
    letter-spacing: -.01em;
  }

  .mk-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -48%) scale(.98);
    width: min(680px, 94vw);
    max-height: 88vh;
    background: var(--surface);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-3);
    z-index: var(--z-dialog);
    display: flex;
    flex-direction: column;
    opacity: 0;
    pointer-events: none;
    transition: opacity .16s ease, transform .16s ease;
  }
  .mk-modal.is-open {
    opacity: 1;
    pointer-events: auto;
    transform: translate(-50%, -50%) scale(1);
  }
  /* Centring is not content-dependent: translate(-50%, -50%) works from the
     element's own size, so a two-line confirm and a tall form both land in the
     middle of the viewport without measuring anything. */
  .mk-modal-sm { width: min(440px, 94vw); }

  .mk-confirm-text {
    color: var(--text-2);
    font-size: var(--fs-sm);
    line-height: 1.55;
    margin-bottom: 12px;
  }
  .mk-confirm-text:last-child { margin-bottom: 0; }

  .mk-modal-head {
    padding: 16px 20px 12px;
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .mk-modal-title { margin: 0; font-size: var(--fs-lg); font-weight: 650; }
  .mk-modal-body { padding: 4px 20px 8px; overflow-y: auto; }
  .mk-modal-foot {
    padding: 12px 20px 16px;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    border-top: 1px solid var(--border);
    margin-top: 8px;
  }
  /* Pushes the confirm/cancel cluster right; use .mk-modal-foot-spacer for
     explicit control when the footer has more than one group. */
  .mk-modal-foot > .mk-modal-foot-spacer { margin-left: auto; }
  .mk-modal-foot > .mk-btn:first-of-type:not(.mk-modal-foot-start) { margin-left: auto; }
}

@layer meridian.components {
  /* ---- spinner ---- */

  .mk-spinner {
    display: inline-block;
    width: 1em;
    height: 1em;
    flex: none;
    border: 2px solid currentcolor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: mk-spin .7s linear infinite;
    /* Sits on the text baseline like an icon rather than shifting the line. */
    vertical-align: -.15em;
    opacity: .75;
  }
  .mk-spinner-sm { width: .85em; height: .85em; border-width: 1.5px; }
  .mk-spinner-lg { width: 1.6em; height: 1.6em; }

  @keyframes mk-spin {
    to { transform: rotate(360deg); }
  }

  /* ---- skeleton ---- */

  .mk-skeleton {
    background: linear-gradient(
      90deg,
      var(--surface-3) 25%,
      color-mix(in srgb, var(--surface-3) 55%, var(--surface)) 37%,
      var(--surface-3) 63%
    );
    background-size: 400% 100%;
    animation: mk-shimmer 1.4s ease infinite;
    border-radius: var(--r-sm);
  }
  .mk-skeleton-text {
    height: 1em;
    margin: .25em 0;
  }
  .mk-skeleton-text:last-child { width: 60%; }

  @keyframes mk-shimmer {
    from { background-position: 100% 50%; }
    to { background-position: 0 50%; }
  }

  /* A spinner's rotation *is* the message, so it is slowed rather than
     stopped. A skeleton's shimmer is decoration and simply goes. */
  @media (prefers-reduced-motion: reduce) {
    .mk-spinner { animation-duration: 1.6s; }
    .mk-skeleton { animation: none; }
  }

  /* ---- busy states ---- */

  /* The label stays put and the spinner takes the icon slot, so the button
     does not resize and the page does not reflow when work starts. */
  .mk-btn.is-busy {
    cursor: progress;
    pointer-events: none;
  }
  .mk-btn.is-busy::before {
    content: "";
    width: .9em;
    height: .9em;
    border: 2px solid currentcolor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: mk-spin .7s linear infinite;
  }
  @media (prefers-reduced-motion: reduce) {
    .mk-btn.is-busy::before { animation-duration: 1.6s; }
  }

  .mk-combo-busy {
    position: absolute;
    right: 9px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-3);
    display: none;
    font-size: var(--fs-md);
  }
  .mk-combo.is-busy .mk-combo-busy { display: block; }
  /* Only one thing occupies that slot at a time. */
  .mk-combo.is-busy .mk-combo-caret { display: none; }

  .mk-opt-busy {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 10px;
    font-size: var(--fs-sm);
    color: var(--text-3);
  }

  /* Skeleton rows keep the grid's height while data is in flight, so the page
     below does not jump when it arrives. */
  .mk-table tbody tr.mk-skeleton-row td { height: 40px; }
  .mk-table tbody tr.mk-skeleton-row .mk-skeleton { height: 12px; }
}

@layer meridian.components {
  /* The palette sits high rather than dead centre: it grows downward as
     results arrive, and a centred box would jump on every keystroke. */
  .mk-palette {
    top: 12vh;
    transform: translate(-50%, 0) scale(.98);
    width: min(560px, 94vw);
    max-height: 70vh;
    padding: 0;
  }
  .mk-palette.is-open { transform: translate(-50%, 0) scale(1); }

  .mk-palette-search {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
  }
  .mk-palette-search .mk-ic {
    width: 17px;
    height: 17px;
    color: var(--text-3);
    flex: none;
  }

  .mk-palette-input {
    flex: 1;
    border: 0;
    outline: none;
    background: none;
    font-size: var(--fs-lg);
    color: var(--text);
    min-width: 0;
  }
  .mk-palette-input::placeholder { color: var(--text-3); }

  .mk-palette-list {
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 6px;
  }
  .mk-palette-list .mk-opt { padding: 8px 10px; }
  .mk-palette-list .mk-kbd { margin-left: auto; flex: none; }

  /* Cheatsheet layout, shared by any two-column key list. */
  .mk-keys {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 6px 16px;
    align-items: center;
    margin: 0 0 12px;
    font-size: var(--fs-sm);
  }
  .mk-keys dt { color: var(--text-2); }
  .mk-keys dd { margin: 0; text-align: right; }
}

@layer meridian.components {
  /* Fixed stack of transient messages. The region is always in the DOM once
     created — a live region has to exist before content lands in it, or screen
     readers announce nothing. */
  .mk-toast-region {
    position: fixed;
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: min(380px, calc(100vw - 24px));
    /* The region spans a corner but must not eat clicks where it is empty. */
    pointer-events: none;
  }
  .mk-toast-region > * { pointer-events: auto; }

  .mk-toast-region-top-end    { top: 12px; right: 12px; align-items: flex-end; }
  .mk-toast-region-top-start  { top: 12px; left: 12px; align-items: flex-start; }
  .mk-toast-region-top-center { top: 12px; left: 50%; transform: translateX(-50%); align-items: center; }
  .mk-toast-region-bottom-end    { bottom: 12px; right: 12px; align-items: flex-end; flex-direction: column-reverse; }
  .mk-toast-region-bottom-start  { bottom: 12px; left: 12px; align-items: flex-start; flex-direction: column-reverse; }
  .mk-toast-region-bottom-center { bottom: 12px; left: 50%; transform: translateX(-50%); align-items: center; flex-direction: column-reverse; }

  .mk-toast {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    padding: 10px 10px 10px 12px;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-2);
    font-size: var(--fs-sm);
    animation: mk-toast-in .18s ease-out;
  }
  .mk-toast.is-leaving {
    animation: mk-toast-out .14s ease-in forwards;
  }

  .mk-toast-info   { border-left-color: var(--accent); }
  .mk-toast-ok     { border-left-color: var(--ok); }
  .mk-toast-warn   { border-left-color: var(--warn); }
  .mk-toast-danger { border-left-color: var(--danger); }

  .mk-toast > .mk-ic { width: 17px; height: 17px; margin-top: 1px; flex: none; }
  .mk-toast-info   > .mk-ic { color: var(--accent); }
  .mk-toast-ok     > .mk-ic { color: var(--ok); }
  .mk-toast-warn   > .mk-ic { color: var(--warn); }
  .mk-toast-danger > .mk-ic { color: var(--danger); }

  .mk-toast-body { flex: 1; min-width: 0; }
  .mk-toast-title { font-weight: 640; margin-bottom: 1px; }
  .mk-toast-text { color: var(--text-2); line-height: 1.45; overflow-wrap: anywhere; }

  .mk-toast-actions {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-left: auto;
    flex: none;
  }

  /* Progress hairline showing the time left. Paused with the timer, so it
     never claims a toast is about to leave while it is being read. */
  .mk-toast-timer {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 2px;
    background: var(--border-strong);
    border-radius: 0 0 0 var(--r-md);
    transform-origin: left center;
  }
  .mk-toast { position: relative; overflow: hidden; }

  @keyframes mk-toast-in {
    from { opacity: 0; transform: translateY(-6px) scale(.99); }
  }
  @keyframes mk-toast-out {
    to { opacity: 0; transform: translateY(-4px) scale(.99); }
  }

  /* A toast that slides in from off-screen is disorienting when motion is
     reduced, but it must still appear and still disappear. */
  @media (prefers-reduced-motion: reduce) {
    .mk-toast,
    .mk-toast.is-leaving { animation: none; }
  }
}

@layer meridian.components {
  .mk-tooltip {
    position: fixed;
    z-index: var(--z-tooltip);
    max-width: 260px;
    padding: 5px 8px;
    background: var(--text);
    color: var(--surface);
    font-size: var(--fs-xs);
    line-height: 1.4;
    border-radius: var(--r-md);
    box-shadow: var(--shadow-2);
    /* A tooltip must never sit between the pointer and its trigger, or moving
       toward it would dismiss it and re-show it in a loop. */
    pointer-events: none;
    display: none;
    overflow-wrap: anywhere;
  }
  .mk-tooltip.is-open { display: block; animation: mk-tooltip-in .1s ease-out; }

  .mk-tooltip-multiline { white-space: pre-line; }

  @keyframes mk-tooltip-in {
    from { opacity: 0; }
  }

  @media (prefers-reduced-motion: reduce) {
    .mk-tooltip.is-open { animation: none; }
  }
}

@layer meridian.components {
  .mk-datefield { position: relative; }
  .mk-datefield .mk-input { padding-right: 34px; }

  .mk-datefield-btn {
    position: absolute;
    right: 3px;
    top: 50%;
    transform: translateY(-50%);
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    background: none;
    border-radius: var(--r-sm);
    color: var(--text-3);
    cursor: pointer;
    display: grid;
    place-items: center;
  }
  .mk-datefield-btn:hover { background: var(--surface-3); color: var(--text); }
  .mk-datefield-btn .mk-ic { width: 16px; height: 16px; }

  .mk-cal { padding: 4px; }
  .mk-cal-panels { display: flex; gap: var(--space-2); align-items: flex-start; }

  .mk-cal-head {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px 2px 6px;
  }
  .mk-cal-title {
    flex: 1;
    text-align: center;
    font-size: var(--fs-sm);
    font-weight: 640;
    color: var(--text);
  }

  .mk-cal-grid {
    border-collapse: collapse;
    /* A fixed cell size keeps the popover from resizing as months change. */
    table-layout: fixed;
  }
  .mk-cal-grid th {
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-3);
    padding: 2px 0 6px;
    width: 34px;
  }

  .mk-cal-day {
    width: 34px;
    height: 32px;
    text-align: center;
    vertical-align: middle;
    font-size: var(--fs-sm);
    color: var(--text);
    border-radius: var(--r-md);
    cursor: pointer;
    user-select: none;
  }
  .mk-cal-day:hover { background: var(--surface-3); }

  /* Days from the neighbouring months stay visible but recede, so the shape
     of the month is readable at a glance. */
  .mk-cal-day.is-outside { color: var(--text-3); }

  .mk-cal-day[aria-selected="true"] {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 650;
  }
  .mk-cal-day[aria-selected="true"]:hover { background: var(--accent-hover); }

  /* Today is marked with an underline rather than a fill, so it cannot be
     confused with the selection. */
  .mk-cal-day[aria-current="date"]:not([aria-selected="true"]) {
    box-shadow: inset 0 -2px 0 var(--accent);
    font-weight: 640;
  }

  .mk-cal-day[aria-disabled="true"] {
    color: var(--border-strong);
    cursor: not-allowed;
    text-decoration: line-through;
  }
  .mk-cal-day[aria-disabled="true"]:hover { background: none; }

  .mk-cal-month + .mk-cal-month { margin-left: 4px; }
  .mk-cal-spacer { width: 26px; flex: none; }

  /* ---- range ---- */

  /* The band is drawn with a background on the cell rather than a pseudo
     element, so it lines up exactly with the 34px grid and cannot drift. */
  .mk-cal-day.is-in-range {
    background: var(--accent-soft);
    color: var(--accent);
    border-radius: 0;
  }
  .mk-cal-day.is-in-range:hover { background: var(--accent-soft); }

  .mk-cal-day.is-range-start,
  .mk-cal-day.is-range-end {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 650;
  }
  .mk-cal-day.is-range-start { border-radius: var(--r-md) 0 0 var(--r-md); }
  .mk-cal-day.is-range-end { border-radius: 0 var(--r-md) var(--r-md) 0; }
  .mk-cal-day.is-range-start.is-range-end { border-radius: var(--r-md); }

  /* An in-range day from a neighbouring month keeps the band but stays muted,
     so the run reads as continuous across the month boundary. */
  .mk-cal-day.is-outside.is-in-range { color: var(--text-3); }

  .mk-cal-foot {
    display: flex;
    align-items: center;
    gap: 6px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
    padding-top: 6px;
  }

  /* ---- time column ---- */
  .mk-cal-times {
    border-left: 1px solid var(--border);
    padding-left: 8px;
    margin-left: 4px;
    max-height: 244px;
    overflow-y: auto;
    overscroll-behavior: contain;
    min-width: 84px;
  }
  .mk-cal-only-time .mk-cal-times {
    border-left: 0;
    padding-left: 0;
    margin-left: 0;
  }

  .mk-cal-time {
    display: block;
    width: 100%;
    padding: 5px 10px;
    border: 0;
    background: none;
    border-radius: var(--r-md);
    font-size: var(--fs-sm);
    font-variant-numeric: tabular-nums;
    color: var(--text);
    text-align: left;
    cursor: pointer;
  }
  .mk-cal-time:hover,
  .mk-cal-time.is-active { background: var(--surface-3); }
  .mk-cal-time[aria-selected="true"] {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 640;
  }

  /* Two months do not fit on a phone; stack them instead of overflowing. */
  @media (max-width: 560px) {
    .mk-cal-panels { flex-direction: column; }
    .mk-cal-month + .mk-cal-month { margin-left: 0; margin-top: 8px; }
  }
}
/* ============================================================
   LAYOUT — the patterns the pages kept spelling out by hand.

   Six primitives plus a twelve-column grid, and nothing else. This is not a
   utility framework: there is no margin scale, no padding scale, no colour
   utilities. Every one of these earned its place by appearing repeatedly as an
   inline style in `docs/` or `demo/` — the reflowing column was written ten
   different ways with ten different numbers before it had a name.

   Sizes are answered by the **container**, not the window. `Navbar` measures its
   own bar, `Sidebar` measures its own container, and three viewport media queries
   were deleted for the same reason: a column inside a 300px drawer is narrow
   wherever the browser window happens to be.
   ============================================================ */
@layer meridian.utilities {
  /* ---------- stack: vertical rhythm ---------- */

  /* Owl selector rather than a gap, so it works on anything — a form, a run of
     panels, prose — without turning the element into a flex container and
     changing how its children size themselves. */
  .mk-stack > * + * { margin-top: var(--mk-stack-gap, var(--space-4)); }
  .mk-stack-tight { --mk-stack-gap: var(--space-2); }
  .mk-stack-loose { --mk-stack-gap: var(--space-5); }

  /* ---------- cols: columns that reflow on their own ---------- */

  /* The pattern that was written as `flex: 1 1 300px; min-width: 280px` ten
     times over. One number — the narrowest a column may get before the row wraps
     — and the browser decides how many fit. No breakpoints, no classes per size.

     `min-width: 0` on the children is not decoration: a flex item defaults to
     `min-width: auto`, so one long unbroken string inside it makes the whole row
     overflow rather than wrap. */
  .mk-cols {
    display: flex;
    flex-wrap: wrap;
    gap: var(--mk-cols-gap, var(--space-4));
  }
  .mk-cols > * {
    flex: 1 1 var(--mk-cols-min, 280px);
    min-width: 0;
  }
  /* A column that should take only what it needs, in a row of ones that stretch. */
  .mk-cols > .mk-col-auto { flex: 0 1 auto; }

  /* ---------- grid: equal cells that reflow ---------- */

  .mk-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--mk-grid-min, 200px), 1fr));
    gap: var(--mk-grid-gap, var(--space-3));
  }

  /* ---------- aside: a fixed column beside a fluid one ---------- */

  /* Named `.mk-aside` because `.mk-sidebar` is the sidebar component and
     `.mk-split` is the split button.

     It collapses with **no query at all**. A container query cannot style the
     element it queries — only that element's descendants — so `.mk-aside` asking
     about its own width was a rule that could never match. Wrapping flex answers
     it directly instead: the content asks for 55%, and a row that cannot give
     both children what they ask for wraps. The threshold is therefore a ratio
     rather than a number, which is one fewer thing to keep in step.

     `container-type` stays, not for this, but so *descendants* can respond to the
     layout — the saved-views rail unsticking itself once it is full width. */
  .mk-aside {
    container-type: inline-size;
    display: flex;
    flex-wrap: wrap;
    gap: var(--mk-aside-gap, var(--space-5));
    align-items: flex-start;
  }
  /* `flex-grow: 999` on the content is the trick that holds the aside at its
     basis without pinning it: the content takes essentially all the free space,
     so the aside keeps its 216px. The aside grows too, which only shows once it
     has wrapped and is alone on its line — otherwise a collapsed sidebar sits at
     216px in a full-width row. And the content's `min-inline-size` is what forces
     the wrap: 216px plus 55% cannot both fit under about 480px. */
  .mk-aside > :first-child { flex: 1 1 var(--mk-aside-width, 216px); }
  .mk-aside > :last-child {
    flex: 999 1 0;
    min-inline-size: var(--mk-aside-content, 55%);
  }
  /* The aside on the trailing side. Swaps which child is fixed rather than
     reversing the row, so reading order and focus order stay the DOM's. */
  .mk-aside-end > :first-child {
    flex: 999 1 0;
    min-inline-size: var(--mk-aside-content, 55%);
  }
  .mk-aside-end > :last-child { flex: 1 1 var(--mk-aside-width, 216px); }

  /* ---------- center: a measure-limited column ---------- */

  .mk-center {
    box-sizing: border-box;
    max-width: var(--mk-center-max, 72ch);
    margin-inline: auto;
    padding-inline: var(--mk-center-pad, 0);
  }

  /* ---------- row: the cluster that already existed ---------- */

  /* `.mk-row` predates all of this and means a horizontal cluster with its items
     centred. It is deliberately *not* a grid row — renaming it would break the
     demo, the sandbox and the README, and a grid row is `.mk-cols-12` below. */
  .mk-row-top { align-items: flex-start; }
  .mk-row-baseline { align-items: baseline; }

  /* ---------- the twelve-column grid ---------- */

  /* An explicit grid for layouts that are drawn on one, rather than reflowing
     freely.

     **This is the row.** It is named for what it contains because `.mk-row` was
     already the horizontal cluster — see `../../../context/decisions.md`. Two
     consequences worth knowing before writing markup against it:

       · One container holds as many visual rows as you like. It is a real grid,
         so columns flow and wrap within it; Bootstrap needs a `.row` per line
         because its columns are not. A second container is for a different gap
         or a deliberate break, nothing else.
       · `.mk-col` — singular, in `_utilities.css` — is a vertical stack, not a
         span. The adjacency is a wart, not a pattern.

     `container-type` is set here and nowhere else, because it is not free: layout
     containment makes the element a containing block for absolutely positioned
     descendants. A row you want to query without the grid opts in with
     `.mk-container`. */
  .mk-cols-12,
  .mk-container { container-type: inline-size; }

  .mk-cols-12 {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--mk-cols-gap, var(--space-4));
  }

  /* Spans apply at every width; the container sizes below widen them. Narrowest
     first, so a column with no size class is full width on a narrow row — the
     behaviour a page wants by default. */
  .mk-cols-12 > .mk-col-1  { grid-column: span 1; }
  .mk-cols-12 > .mk-col-2  { grid-column: span 2; }
  .mk-cols-12 > .mk-col-3  { grid-column: span 3; }
  .mk-cols-12 > .mk-col-4  { grid-column: span 4; }
  .mk-cols-12 > .mk-col-5  { grid-column: span 5; }
  .mk-cols-12 > .mk-col-6  { grid-column: span 6; }
  .mk-cols-12 > .mk-col-7  { grid-column: span 7; }
  .mk-cols-12 > .mk-col-8  { grid-column: span 8; }
  .mk-cols-12 > .mk-col-9  { grid-column: span 9; }
  .mk-cols-12 > .mk-col-10 { grid-column: span 10; }
  .mk-cols-12 > .mk-col-11 { grid-column: span 11; }
  .mk-cols-12 > .mk-col-12 { grid-column: span 12; }

  /* Container sizes, not viewport ones. `sm` means "this row has at least
     --bp-sm of room", which is true in a drawer and in a full-width page alike.
     The literals are repeated because a media/container query cannot read a
     custom property; the tokens are the documentation of what they mean. */
  @container (min-width: 420px) {
    .mk-cols-12 > .mk-col-sm-1  { grid-column: span 1; }
    .mk-cols-12 > .mk-col-sm-2  { grid-column: span 2; }
    .mk-cols-12 > .mk-col-sm-3  { grid-column: span 3; }
    .mk-cols-12 > .mk-col-sm-4  { grid-column: span 4; }
    .mk-cols-12 > .mk-col-sm-5  { grid-column: span 5; }
    .mk-cols-12 > .mk-col-sm-6  { grid-column: span 6; }
    .mk-cols-12 > .mk-col-sm-7  { grid-column: span 7; }
    .mk-cols-12 > .mk-col-sm-8  { grid-column: span 8; }
    .mk-cols-12 > .mk-col-sm-9  { grid-column: span 9; }
    .mk-cols-12 > .mk-col-sm-10 { grid-column: span 10; }
    .mk-cols-12 > .mk-col-sm-11 { grid-column: span 11; }
    .mk-cols-12 > .mk-col-sm-12 { grid-column: span 12; }
  }
  @container (min-width: 640px) {
    .mk-cols-12 > .mk-col-md-1  { grid-column: span 1; }
    .mk-cols-12 > .mk-col-md-2  { grid-column: span 2; }
    .mk-cols-12 > .mk-col-md-3  { grid-column: span 3; }
    .mk-cols-12 > .mk-col-md-4  { grid-column: span 4; }
    .mk-cols-12 > .mk-col-md-5  { grid-column: span 5; }
    .mk-cols-12 > .mk-col-md-6  { grid-column: span 6; }
    .mk-cols-12 > .mk-col-md-7  { grid-column: span 7; }
    .mk-cols-12 > .mk-col-md-8  { grid-column: span 8; }
    .mk-cols-12 > .mk-col-md-9  { grid-column: span 9; }
    .mk-cols-12 > .mk-col-md-10 { grid-column: span 10; }
    .mk-cols-12 > .mk-col-md-11 { grid-column: span 11; }
    .mk-cols-12 > .mk-col-md-12 { grid-column: span 12; }
  }
  @container (min-width: 900px) {
    .mk-cols-12 > .mk-col-lg-1  { grid-column: span 1; }
    .mk-cols-12 > .mk-col-lg-2  { grid-column: span 2; }
    .mk-cols-12 > .mk-col-lg-3  { grid-column: span 3; }
    .mk-cols-12 > .mk-col-lg-4  { grid-column: span 4; }
    .mk-cols-12 > .mk-col-lg-5  { grid-column: span 5; }
    .mk-cols-12 > .mk-col-lg-6  { grid-column: span 6; }
    .mk-cols-12 > .mk-col-lg-7  { grid-column: span 7; }
    .mk-cols-12 > .mk-col-lg-8  { grid-column: span 8; }
    .mk-cols-12 > .mk-col-lg-9  { grid-column: span 9; }
    .mk-cols-12 > .mk-col-lg-10 { grid-column: span 10; }
    .mk-cols-12 > .mk-col-lg-11 { grid-column: span 11; }
    .mk-cols-12 > .mk-col-lg-12 { grid-column: span 12; }
  }
}

/* ============================================================
   UTILITIES — deliberately small. Meridian is a component library,
   not a utility framework; these exist only to cover the gaps the
   demo markup kept reaching for inline styles to fill.
   ============================================================ */
@layer meridian.utilities {
  .mk-row { display: flex; align-items: center; gap: var(--space-2); }
  .mk-row-tight { gap: var(--space-1); }
  /* `.mk-col` is a vertical stack and predates the layout utilities. It is NOT
     `.mk-col-1` with the number left off — those are twelve-track grid spans in
     `_layout.css`, and this sits one hyphen away from them meaning something
     unrelated. Prefer `.mk-stack`; retiring this is in memory/backlog.md. */
  .mk-col { display: flex; flex-direction: column; gap: var(--space-2); }
  .mk-wrap { flex-wrap: wrap; }
  .mk-spacer { flex: 1; }
  .mk-push-end { margin-left: auto; }

  .mk-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }

  .mk-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
  .mk-brand-font { font-family: var(--font-brand); }

  .mk-muted { color: var(--text-2); }
  .mk-subtle { color: var(--text-3); }

  .mk-text-xs { font-size: var(--fs-xs); }
  .mk-text-sm { font-size: var(--fs-sm); }
  .mk-text-lg { font-size: var(--fs-lg); }
}
