/* axi design language - generated by scripts/build.mjs.
   Edit the files in src/ and run `npm run build`; do not edit this file. */

/* --- tokens.css --- */
/* axi design language - tokens.
   The only file in this system permitted to contain a colour literal. Every
   component resolves its colours through the names below, which is what keeps
   a future light theme a palette swap instead of a rewrite.

   Three layers:
   - surface & text: the neutral ramp the language is drawn on
   - accent & status: the per-app override surface. An app that sets
     --axi-accent and nothing else is correctly themed.
   - form: geometry and type. Shared by every property; overriding these
     means leaving the design language, not theming it. */
:root {
  /* --- surface & text --- */
  --axi-ground: #15181d;          /* the page itself */
  --axi-surface: #222731;         /* a panel raised off the page */
  --axi-surface-raised: #2b313d;  /* a chip or popover raised off a panel */
  --axi-ink-line: #0c0e12;        /* the outline every raised element is drawn with */
  --axi-rule: #3a4250;            /* internal rules, inside an outlined panel */
  --axi-text: #f4f6f9;
  --axi-text-dim: #a7b0be;
  --axi-text-faint: #7c8695;
  --axi-scrim: rgba(6, 7, 9, .72); /* behind a drawer */

  /* --- accent & status --- */
  --axi-accent: #ffc53d;
  /* Text drawn on top of an accent fill. Near-black suits every accent bright
     enough to pass contrast on this ground; an app choosing a dark accent
     overrides this to --axi-text rather than editing components. */
  --axi-accent-ink: var(--axi-ink-line);
  --axi-meta: #4ec3ff;            /* the one cool ink: meta only, never a status */
  --axi-ok: #2fd38a;
  --axi-warn: #ff7a2f;
  --axi-danger: #ff5252;

  /* --- form: outline and offset --- */
  /* Two steps, and only two. A panel is drawn heavier than a control so a
     toolbar full of controls still reads as sitting inside its panel. */
  --axi-border-panel: 4px;
  --axi-offset-panel: 6px;
  --axi-border-control: 3px;
  --axi-offset-control: 3px;
  /* The deepened blocks a hover lifts into. Not a third and fourth step: a
     step is a border+offset pair a thing rests at, and nothing rests at
     these. They are tokenised so the offset check can enumerate every legal
     block instead of admitting bare literals. */
  --axi-offset-panel-hover: 10px;
  --axi-offset-control-hover: 6px;
  --axi-radius: 10px;
  --axi-radius-sm: 6px;
  /* Not a form step: a hairline used inside prose (inline code, table rules,
     the list bullet) where the two form steps above would read as too heavy
     for a line of running text. */
  --axi-border-hairline: 2px;

  /* --- form: measure --- */
  --axi-page: 1280px;             /* the default reading//browsing width */
  --axi-page-narrow: 46rem;       /* prose */
  --axi-page-wide: 1660px;        /* dense card grids */
  --axi-gutter: 18px;

  /* --- form: type --- */
  --axi-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI Variable", "Segoe UI", Roboto, sans-serif;
  --axi-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
  /* Font shorthands, so a component sets `font: var(--axi-t-h2)` in one
     declaration. Tracking cannot ride in the shorthand, so the headings that
     need it carry a paired --axi-ls-* token. */
  --axi-t-display: 900 54px/1.02 var(--axi-sans);
  --axi-ls-display: -.045em;
  --axi-t-h1: 900 30px/1.1 var(--axi-sans);
  --axi-ls-h1: -.035em;
  --axi-t-h2: 900 24px/1.15 var(--axi-sans);
  --axi-ls-h2: -.035em;
  --axi-t-h3: 900 19px/1.2 var(--axi-sans);
  --axi-ls-h3: -.025em;
  --axi-t-body: 400 15px/1.55 var(--axi-sans);
  --axi-t-small: 400 13.5px/1.5 var(--axi-sans);
  --axi-t-label: 800 13px/1 var(--axi-sans);
  --axi-ls-label: .02em;
  --axi-t-micro: 800 11px/1 var(--axi-sans);
  --axi-ls-micro: .04em;
  --axi-t-eyebrow: 900 11.5px/1 var(--axi-sans);
  --axi-ls-eyebrow: .1em;
}

/* --- base.css --- */
/* axi design language - base.
   Element-level defaults every axi property inherits. Nothing here is a
   component; if it needs a class, it belongs in a later layer. */

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

/* Dark is the only theme shipped today. Declaring the scheme means form
   controls, scrollbars and the like come up dark from the first paint rather
   than flashing light, and it is the one line a light theme will flip. */
html { color-scheme: dark; }

body {
  margin: 0;
  background: var(--axi-ground);
  color: var(--axi-text);
  font: var(--axi-t-body);
  -webkit-font-smoothing: antialiased;
}

/* Links inherit their colour by default: in this language a link is usually
   inside something that has already chosen an ink, and a globally accented
   link would fight every card title and nav item. Components opt into the
   accent where a link should read as one. */
a { color: inherit; }

/* The focus ring is accent-coloured and thick enough to read against a
   near-black outline, which a 1px ring does not. It is drawn entirely with
   `outline`, which follows whatever radius the element already has: a
   `border-radius` here would reshape the focused element itself, which was
   harmless for our components only because each one's own radius happens to
   land later in the build ORDER, and visibly wrong for an unclassed
   checkbox or link. */
:focus-visible {
  outline: var(--axi-border-control) solid var(--axi-accent);
  outline-offset: 2px;
}

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

/* Everything this language animates is the hover lift, and the lift is the
   one thing a reader who has asked for less motion is asking not to see.
   Neutralise the movement, not the appearance: the resting transforms - the
   diamond's rotation, the sigil, the search glyph's centring - are geometry
   rather than motion and are deliberately untouched, so nothing looks
   different until you point at it. The !important is what lets this sit in
   the base layer and still outrank component hover rules that come later in
   the build ORDER at equal specificity. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
  .axi-btn:hover,
  .axi-pill:hover,
  .axi-pill[aria-pressed="true"]:hover,
  .axi-select:hover,
  .axi-card:hover,
  .axi-drawer__close:hover {
    transform: none !important;
  }
}

/* --- primitives.css --- */
/* axi design language - primitives.
   The pieces that recur in every property. Two rules govern the whole file:
   a raised thing is outlined in --axi-ink-line and carries a hard offset
   block, never a blur; and a filled chip asserts a value while an outlined
   one annotates. */

/* ---------- panel ---------- */
/* The surface every larger component is built on. Kept as its own class so a
   consumer can raise an arbitrary block into the language without waiting for
   us to ship a component for it. */
.axi-panel {
  background: var(--axi-surface);
  border: var(--axi-border-panel) solid var(--axi-ink-line);
  border-radius: var(--axi-radius);
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
  /* A panel owns its padding so every consumer doesn't have to invent one;
     override per instance with --axi-panel-pad where a tighter or looser
     fit is genuinely called for. */
  padding: var(--axi-panel-pad, 26px);
}

/* ---------- button ---------- */
.axi-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 20px;
  border: var(--axi-border-control) solid var(--axi-ink-line);
  border-radius: 8px;
  background: var(--axi-ground);
  color: var(--axi-text-dim);
  font: var(--axi-t-label);
  letter-spacing: var(--axi-ls-label);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: transform .1s, box-shadow .1s;
}
/* Hover lifts. Nothing in this language fades or glows: the block deepens and
   the element moves against it, which reads instantly even in peripheral
   vision and costs no colour. */
.axi-btn:hover {
  color: var(--axi-text);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
  transform: translate(-2px, -2px);
}
.axi-btn--primary {
  background: var(--axi-accent);
  color: var(--axi-accent-ink);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
}
/* The one button that rests with a block already under it, so the generic
   hover's translate alone would move element and block together and leave the
   lower-right edge where it started - the "grows rather than lifts" failure
   rule 4 warns about. It lifts the way a panel does: the block deepens too. */
.axi-btn--primary:hover {
  color: var(--axi-accent-ink);
  box-shadow: var(--axi-offset-control-hover) var(--axi-offset-control-hover) 0 var(--axi-ink-line);
}
.axi-btn--ghost { background: transparent; }
.axi-btn--dashed { background: transparent; border-style: dashed; }

/* ---------- pill ---------- */
/* A filter toggle. Its pressed state fills with whatever colour the consumer
   has put on it, so the control reads as the thing it filters to rather than
   as a generic "selected". Consumers set that colour with --axi-pill-fill,
   on the pill or on any ancestor - it is read through a fallback and never
   declared on the component, which is what keeps both placements working. */
.axi-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 10px 9px;
  border: var(--axi-border-control) solid var(--axi-ink-line);
  border-radius: 8px;
  background: var(--axi-ground);
  color: var(--axi-text-dim);
  font: var(--axi-t-label);
  letter-spacing: var(--axi-ls-label);
  text-transform: uppercase;
  cursor: pointer;
  transition: transform .1s, box-shadow .1s;
}
.axi-pill:hover {
  color: var(--axi-text);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
  transform: translate(-2px, -2px);
}
.axi-pill[aria-pressed="true"] {
  background: var(--axi-pill-fill, var(--axi-accent));
  color: var(--axi-accent-ink);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
}
/* Spelled out rather than left to source order: `.axi-pill:hover` and
   `.axi-pill[aria-pressed="true"]` have identical specificity, so without this
   rule a pressed pill under the cursor kept its pressed block while still
   taking the hover's translate - a slide, not a lift, decided by which rule
   happened to come last in the file. A pressed pill keeps its fill and lifts. */
.axi-pill[aria-pressed="true"]:hover {
  background: var(--axi-pill-fill, var(--axi-accent));
  color: var(--axi-accent-ink);
  box-shadow: var(--axi-offset-control-hover) var(--axi-offset-control-hover) 0 var(--axi-ink-line);
  transform: translate(-2px, -2px);
}

/* ---------- chip ---------- */
/* Filled asserts, outlined annotates. The unmodified chip is a neutral fact;
   the status modifiers assert a value; --meta is the only outlined variant and
   is drawn in the reserved cool ink, so a reader can tell commentary from
   data without reading either. */
.axi-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 9px;
  border: var(--axi-border-control) solid var(--axi-ink-line);
  border-radius: var(--axi-radius-sm);
  background: var(--axi-surface-raised);
  color: var(--axi-text-dim);
  font: var(--axi-t-micro);
  letter-spacing: var(--axi-ls-micro);
  text-transform: uppercase;
}
.axi-chip--ok { background: var(--axi-ok); color: var(--axi-ink-line); }
.axi-chip--warn { background: var(--axi-warn); color: var(--axi-ink-line); }
.axi-chip--danger { background: var(--axi-danger); color: var(--axi-ink-line); }
.axi-chip--accent { background: var(--axi-accent); color: var(--axi-accent-ink); }
.axi-chip--meta {
  background: transparent;
  border-color: var(--axi-meta);
  color: var(--axi-meta);
}

/* ---------- count badge ---------- */
.axi-badge-count {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 99px;
  background: var(--axi-accent);
  color: var(--axi-accent-ink);
  font: var(--axi-t-micro);
}

/* ---------- diamond ---------- */
/* The family motif: a rotated outlined square. Used as a bullet, a status dot
   and, scaled up behind a glyph, the brand sigil. */
.axi-diamond {
  display: inline-block;
  width: 9px; height: 9px; flex: none;
  transform: rotate(45deg);
  background: var(--axi-meta);
  border: var(--axi-border-control) solid var(--axi-ink-line);
}
.axi-diamond--accent { background: var(--axi-accent); }
.axi-diamond--ok { background: var(--axi-ok); }
.axi-diamond--warn { background: var(--axi-warn); }
.axi-diamond--danger { background: var(--axi-danger); }

/* ---------- input ---------- */
.axi-input {
  width: 100%;
  padding: 11px 12px;
  background: var(--axi-ground);
  border: var(--axi-border-control) solid var(--axi-ink-line);
  border-radius: 8px;
  color: var(--axi-text);
  font: var(--axi-t-label);
  font-size: 14px;
}
.axi-input::placeholder { color: var(--axi-text-faint); font-weight: 500; }

/* A search input with the magnifier drawn inside it. The glyph is the
   consumer's; this only reserves the room and positions it. */
.axi-search { position: relative; }
.axi-search .axi-input { padding-left: 38px; }
.axi-search__icon {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--axi-accent); font-weight: 900; pointer-events: none;
}

/* ---------- select ---------- */
/* The closed box is ours everywhere: strip the native control and draw the
   caret, so a select sits alongside the other controls as just another
   outlined chip instead of announcing the OS. */
.axi-select {
  appearance: none;
  padding: 10px 30px 10px 9px;
  border: var(--axi-border-control) solid var(--axi-ink-line);
  border-radius: 8px;
  background-color: var(--axi-ground);
  color: var(--axi-text-dim);
  font: var(--axi-t-label);
  font-size: 12.5px;
  letter-spacing: var(--axi-ls-label);
  text-transform: uppercase;
  cursor: pointer;
  /* Two triangles meeting: a caret with no image and no icon font. */
  background-image:
    linear-gradient(45deg, transparent 50%, var(--axi-accent) 50%),
    linear-gradient(135deg, var(--axi-accent) 50%, transparent 50%);
  background-position: calc(100% - 16px) calc(50% + 2px), calc(100% - 11px) calc(50% + 2px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  transition: transform .1s, box-shadow .1s;
}
.axi-select:hover {
  color: var(--axi-text);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
  transform: translate(-2px, -2px);
}

/* The popup stays OS chrome until a browser lets us style it. Where one does
   (Chromium's base-select), the list is drawn with the same outline and offset
   block as our own popovers, so both dropdown kinds read as one family; where
   it does not, the closed box above is still ours and the list is native. */
@supports (appearance: base-select) {
  /* base-select draws its own ::picker-icon, so the hand-drawn caret above
     would be a second arrow. */
  .axi-select, .axi-select::picker(select) { appearance: base-select; }
  .axi-select { background-image: none; padding-right: 9px; }
  .axi-select::picker-icon { color: var(--axi-accent); transition: none; }
  .axi-select::picker(select) {
    margin-top: 9px; padding: 6px;
    border: var(--axi-border-panel) solid var(--axi-ink-line);
    border-radius: 9px;
    background: var(--axi-surface-raised);
    box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
  }
  .axi-select option {
    display: flex; align-items: center; gap: 9px;
    padding: 8px 9px;
    border-radius: var(--axi-radius-sm);
    background: transparent;
    color: var(--axi-text-dim);
    font: var(--axi-t-label);
    font-size: 12.5px;
    letter-spacing: var(--axi-ls-label);
    text-transform: uppercase;
  }
  .axi-select option:hover, .axi-select option:focus {
    background: var(--axi-ground); color: var(--axi-text);
  }
  /* The page-wide focus ring sits 2px outside its element; inside a picker
     that is 2px into the neighbouring row, so pull it back in. */
  .axi-select option:focus-visible { outline-offset: -3px; }
  .axi-select option:checked { color: var(--axi-text); }
  .axi-select option::checkmark { content: "\2713"; color: var(--axi-accent); font-weight: 900; }
}

/* --- layout.css --- */
/* axi design language - layout.
   Three measures, one grid, two spacing helpers. Deliberately small: a
   layout system large enough to express any page is a framework, and every
   property here can reach for plain CSS grid the moment it needs something
   these do not cover. */

/* The page wrapper. --axi-page is the default because most axi surfaces are
   browsing views; the two modifiers exist because prose and dense catalogs
   genuinely disagree about measure, and hard-coding either default made one
   of them wrong. */
.axi-page {
  max-width: var(--axi-page);
  margin-inline: auto;
  /* The gutter is a knob because measures nest: a --narrow prose column or a
     --wide grid placed inside a page that has already paid the gutter would
     otherwise pay it twice, with no way to say so but an inline
     `padding-inline: 0`. Set --axi-page-pad: 0 on the inner one. */
  padding-inline: var(--axi-page-pad, var(--axi-gutter));
}
/* Prose. Beyond this measure a line of body text gets hard to track back to
   the start of the next one. */
.axi-page--narrow { max-width: var(--axi-page-narrow); }
/* Dense card grids, where width spent on margins is a column not shown. */
.axi-page--wide { max-width: var(--axi-page-wide); }

/* Auto-fill card grid. The minimum column width is per-instance rather than
   global: a grid of ten app cards and a grid of sixty catalog entries want
   genuinely different minimums, and both are this same component. */
.axi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--axi-grid-min, 300px), 1fr));
  gap: var(--axi-gutter);
}

/* Vertical rhythm between siblings, set per-instance. */
.axi-stack { display: flex; flex-direction: column; gap: var(--axi-stack-gap, 12px); }
/* A horizontal run that wraps rather than overflowing. */
.axi-row { display: flex; align-items: center; flex-wrap: wrap; gap: var(--axi-row-gap, 10px); }

@media (max-width: 640px) {
  /* The fallback must match the resting rule's (var(--axi-gutter), 18px) -
     README.md documents --axi-page-pad's fallback as --axi-gutter, and a
     literal here silently ignores a consumer's --axi-gutter override on
     mobile, which is the one viewport where the gutter matters most. */
  .axi-page { padding-inline: var(--axi-page-pad, var(--axi-gutter)); }
  .axi-grid { grid-template-columns: 1fr; }
}

/* --- shells.css --- */
/* axi design language - shells.
   Assembled components. Each is built from the primitives rather than
   redefining them, so a change to .axi-panel moves the notice, the toolbar,
   the popover and the card together. */

/* The small uppercase heading that labels a panel's contents. */
.axi-eyebrow {
  margin: 0 0 14px;
  font: var(--axi-t-eyebrow);
  letter-spacing: var(--axi-ls-eyebrow);
  text-transform: uppercase;
  color: var(--axi-text-faint);
}

/* ---------- masthead ---------- */
.axi-mast {
  position: sticky; top: 0; z-index: 40;
  background: var(--axi-ground);
  border-bottom: var(--axi-border-panel) solid var(--axi-ink-line);
}
.axi-mast__in {
  max-width: var(--axi-page); margin-inline: auto;
  padding-inline: var(--axi-gutter);
  display: flex; align-items: center; gap: 22px; min-height: 66px; flex-wrap: wrap;
}
.axi-brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
/* The sigil is the diamond motif scaled up with a glyph on top. The diamond is
   a rotated pseudo-element behind the text rather than a rotated box, so the
   glyph stays upright. isolation keeps the negative z-index inside the sigil
   instead of dropping it behind the masthead. */
.axi-sigil {
  position: relative; isolation: isolate;
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  color: var(--axi-accent-ink); font-weight: 900; font-size: 13px;
}
.axi-sigil::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  transform: rotate(45deg);
  border-radius: 5px;
  background: var(--axi-accent);
  border: var(--axi-border-control) solid var(--axi-ink-line);
}
.axi-brand__name { font: 800 17px/1.15 var(--axi-sans); letter-spacing: -.02em; }
.axi-brand__name small {
  display: block;
  font: 700 10.5px/1.4 var(--axi-sans);
  color: var(--axi-text-faint);
  letter-spacing: .09em; text-transform: uppercase;
}
.axi-tabs { display: flex; gap: 8px; margin-left: auto; }
.axi-tabs a {
  padding: 8px 16px;
  font: 800 13.5px/1 var(--axi-sans);
  color: var(--axi-text-dim); text-decoration: none;
  border: var(--axi-border-control) solid transparent;
  border-radius: 8px;
}
.axi-tabs a:hover { color: var(--axi-text); border-color: var(--axi-rule); }
/* The current tab is filled and blocked - the same treatment a pressed pill
   gets, because it is the same idea: this one is on. */
.axi-tabs a[aria-current="page"] {
  background: var(--axi-accent);
  color: var(--axi-accent-ink);
  border-color: var(--axi-ink-line);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
}

/* ---------- notice ---------- */
.axi-notice {
  display: flex; gap: 14px; padding: 15px 18px;
  background: var(--axi-surface);
  border: var(--axi-border-panel) solid var(--axi-ink-line);
  border-radius: var(--axi-radius);
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
}
.axi-notice__icon {
  flex: none; width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: var(--axi-radius-sm);
  background: var(--axi-accent); color: var(--axi-accent-ink);
  font-size: 14px; font-weight: 900;
  border: var(--axi-border-control) solid var(--axi-ink-line);
}
.axi-notice p { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
.axi-notice b { color: var(--axi-accent); font-weight: 800; }

/* ---------- toolbar ---------- */
.axi-toolbar {
  display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  padding: 14px;
  background: var(--axi-surface);
  border: var(--axi-border-panel) solid var(--axi-ink-line);
  border-radius: var(--axi-radius);
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
}

/* ---------- menu popover ---------- */
/* A disclosure, not a permanent row of controls. The trigger is an .axi-btn
   with aria-expanded; this styles the panel it opens. */
.axi-menu { position: relative; }
.axi-menu__pop {
  /* Above .axi-mast's z-index: 40. Both sit in the root stacking context, so
     a toolbar scrolled under the sticky masthead would otherwise open its
     menu behind it. */
  position: absolute; top: calc(100% + 9px); left: 0; z-index: 41;
  width: var(--axi-menu-width, 310px);
  max-height: 340px; overflow-y: auto; padding: 8px;
  background: var(--axi-surface-raised);
  border: var(--axi-border-panel) solid var(--axi-ink-line);
  border-radius: 9px;
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
}
.axi-menu__pop[hidden] { display: none; }
.axi-menu__pop label {
  display: flex; gap: 9px; align-items: flex-start;
  padding: 7px 8px; border-radius: var(--axi-radius-sm);
  font-size: 13px; font-weight: 600; color: var(--axi-text-dim); cursor: pointer;
}
.axi-menu__pop label:hover { background: var(--axi-ground); color: var(--axi-text); }
.axi-menu__pop input { margin: 3px 0 0; accent-color: var(--axi-accent); }

/* ---------- card ---------- */
.axi-card {
  position: relative; overflow: hidden;
  width: 100%; text-align: left;
  display: flex; flex-direction: column; gap: 11px;
  padding: 22px 16px 14px;
  background: var(--axi-surface);
  border: var(--axi-border-panel) solid var(--axi-ink-line);
  border-radius: var(--axi-radius);
  box-shadow: var(--axi-offset-panel) var(--axi-offset-panel) 0 var(--axi-ink-line);
  font: inherit; color: inherit; text-decoration: none; cursor: pointer;
  transition: transform .1s, box-shadow .1s;
}
.axi-card:hover {
  transform: translate(-3px, -3px);
  box-shadow: var(--axi-offset-panel-hover) var(--axi-offset-panel-hover) 0 var(--axi-ink-line);
}
/* The top strip is drawn from the card itself so a renderer does not have to
   emit an extra element for it. It is opt-in: a card only gets a strip when
   the consumer has a real value to encode in it, because a coloured strip that
   means nothing is decoration impersonating data. */
.axi-card--strip::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 9px;
  background: var(--axi-card-strip, var(--axi-accent));
  border-bottom: var(--axi-border-panel) solid var(--axi-ink-line);
}
.axi-card__head { display: flex; gap: 12px; align-items: flex-start; }
.axi-card__glyph {
  flex: none; width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 8px;
  border: var(--axi-border-control) solid var(--axi-ink-line);
  background: var(--axi-surface-raised);
  color: var(--axi-text);
  font: 900 15px/1 var(--axi-sans);
}
.axi-card__title { flex: 1; min-width: 0; }
.axi-card__title .axi-card__name {
  display: block;
  font: var(--axi-t-h3); letter-spacing: var(--axi-ls-h3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.axi-card__title .axi-card__kind {
  display: block; margin-top: 3px;
  font: 700 11px/1.3 var(--axi-sans);
  color: var(--axi-text-faint);
  letter-spacing: .08em; text-transform: uppercase;
}
.axi-card p { margin: 0; font: var(--axi-t-small); color: var(--axi-text-dim); }
/* Pushed to the bottom so that in an equalised grid row every card's meta
   strip lands on the same line and the rules read as continuous across the
   grid. The cost is empty space on cards with less content. */
.axi-card__meta {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: center;
  margin-top: auto; padding-top: 12px;
  border-top: var(--axi-border-control) solid var(--axi-rule);
  font: 700 12px/1 var(--axi-sans);
  color: var(--axi-text-faint);
}
.axi-card__go { margin-left: auto; color: var(--axi-accent); font-weight: 900; text-transform: uppercase; letter-spacing: .05em; }
.axi-card:hover .axi-card__go { color: var(--axi-text); }

/* ---------- drawer ---------- */
.axi-scrim { position: fixed; inset: 0; z-index: 50; background: var(--axi-scrim); border: 0; }
.axi-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 51;
  width: min(var(--axi-drawer-width, 560px), 100vw);
  display: flex; flex-direction: column;
  background: var(--axi-surface);
  border-left: var(--axi-border-panel) solid var(--axi-ink-line);
}
.axi-drawer[hidden], .axi-scrim[hidden] { display: none; }
.axi-drawer__head {
  position: relative;
  padding: 20px 24px 18px;
  border-bottom: var(--axi-border-panel) solid var(--axi-ink-line);
}
.axi-drawer__head h2 {
  margin: 2px 0 0; padding-right: 44px;
  font: var(--axi-t-h2); letter-spacing: var(--axi-ls-h2);
}
.axi-drawer__close {
  position: absolute; top: 16px; right: 18px;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 8px;
  background: var(--axi-ground);
  border: var(--axi-border-control) solid var(--axi-ink-line);
  color: var(--axi-text-dim);
  font-size: 15px; font-weight: 900; cursor: pointer;
  transition: transform .1s, box-shadow .1s;
}
.axi-drawer__close:hover {
  background: var(--axi-accent); color: var(--axi-accent-ink);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
  transform: translate(-2px, -2px);
}
.axi-drawer__body { overflow-y: auto; padding: 18px 24px 30px; }

/* ---------- pull-quote ---------- */
/* A cited quotation. The accent rule down the left is the only place in the
   language where colour marks a block rather than filling a chip - a quote is
   someone else's words and wants to look borrowed. */
.axi-quote {
  margin: 0;
  padding: 11px 13px;
  border-left: var(--axi-border-panel) solid var(--axi-accent);
  background: var(--axi-ground);
  border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
}
.axi-quote p { margin: 0; font-size: 13px; font-style: italic; color: var(--axi-text-dim); line-height: 1.5; }
.axi-quote cite {
  display: block; margin-top: 7px;
  font-style: normal; font-size: 11.5px; font-weight: 700;
  color: var(--axi-accent);
}

@media (max-width: 640px) {
  .axi-mast__in { min-height: 0; padding: 10px 16px; gap: 12px; }
  .axi-tabs { margin-left: 0; width: 100%; overflow-x: auto; }
  /* The offset blocks eat horizontal room a phone does not have, so the panel
     step drops to the control step here rather than to an invented number.
     The card's hover has to come down with it - left alone it kept deepening
     to the panel hover block, so a card on a hover-emulating touch device
     jumped further than it does on the desktop it was tuned for. */
  .axi-notice, .axi-toolbar, .axi-card {
    box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
  }
  .axi-card:hover {
    box-shadow: var(--axi-offset-control-hover) var(--axi-offset-control-hover) 0 var(--axi-ink-line);
  }
}

/* --- prose.css --- */
/* axi design language - prose.
   A single class wrapping rendered markdown, so a content pipeline can emit
   plain HTML with no classes at all and still land inside the language.

   This layer has no ancestor in the source site, which was all cards and
   controls. The restraint it needs is different: long-form reading wants
   generous leading and quiet rules, and the offset blocks that make a card
   feel physical would make a paragraph feel shouted. So nothing in here is
   blocked except the code block and the table, which are genuinely objects
   dropped into the text rather than part of its flow. */
.axi-prose { color: var(--axi-text-dim); font: var(--axi-t-body); }

.axi-prose > :first-child { margin-top: 0; }
.axi-prose > :last-child { margin-bottom: 0; }

.axi-prose h1 { margin: 0 0 18px; font: var(--axi-t-h1); letter-spacing: var(--axi-ls-h1); color: var(--axi-text); }
.axi-prose h2 {
  margin: 38px 0 14px; padding-top: 22px;
  border-top: var(--axi-border-control) solid var(--axi-rule);
  font: var(--axi-t-h2); letter-spacing: var(--axi-ls-h2); color: var(--axi-text);
}
.axi-prose h3 { margin: 28px 0 10px; font: var(--axi-t-h3); letter-spacing: var(--axi-ls-h3); color: var(--axi-text); }
.axi-prose h4 {
  margin: 22px 0 8px;
  font: var(--axi-t-eyebrow); letter-spacing: var(--axi-ls-eyebrow);
  text-transform: uppercase; color: var(--axi-text-faint);
}

.axi-prose p { margin: 0 0 16px; }
.axi-prose strong { color: var(--axi-text); font-weight: 800; }
/* Prose is the one place a link should announce itself: a reader scanning an
   article is looking for them, and inheriting the body ink would hide them. */
.axi-prose a { color: var(--axi-accent); font-weight: 600; text-underline-offset: 2px; }
.axi-prose a:hover { color: var(--axi-text); }

.axi-prose ul, .axi-prose ol { margin: 0 0 16px; padding-left: 22px; }
.axi-prose li { margin: 0 0 7px; }
/* The family motif as a bullet. Drawn with ::marker's replacement rather than
   a background image so it inherits the list's own indentation. */
.axi-prose ul { list-style: none; padding-left: 20px; }
.axi-prose ul > li { position: relative; }
.axi-prose ul > li::before {
  content: ""; position: absolute; left: -20px; top: .55em;
  width: 7px; height: 7px; transform: rotate(45deg);
  background: var(--axi-accent);
  border: var(--axi-border-hairline) solid var(--axi-ink-line);
}

.axi-prose code {
  font-family: var(--axi-mono); font-size: .88em;
  background: var(--axi-ground); color: var(--axi-text);
  border: var(--axi-border-hairline) solid var(--axi-ink-line);
  border-radius: 4px; padding: 1px 5px;
}
.axi-prose pre {
  margin: 0 0 18px; padding: 14px 16px; overflow-x: auto;
  background: var(--axi-ground);
  border: var(--axi-border-control) solid var(--axi-ink-line);
  border-radius: var(--axi-radius-sm);
  box-shadow: var(--axi-offset-control) var(--axi-offset-control) 0 var(--axi-ink-line);
}
.axi-prose pre code { background: none; border: 0; padding: 0; font-size: 12.5px; line-height: 1.6; }

.axi-prose blockquote {
  margin: 0 0 18px;
  padding: 11px 13px;
  border-left: var(--axi-border-panel) solid var(--axi-accent);
  background: var(--axi-ground);
  border-radius: 0 var(--axi-radius-sm) var(--axi-radius-sm) 0;
}
.axi-prose blockquote p { margin: 0; font-style: italic; }

.axi-prose table {
  width: 100%; margin: 0 0 18px;
  border-collapse: collapse;
  border: var(--axi-border-control) solid var(--axi-ink-line);
  border-radius: var(--axi-radius-sm);
  overflow: hidden;
  font-size: 13.5px;
}
.axi-prose th {
  text-align: left; padding: 10px 12px;
  background: var(--axi-surface-raised); color: var(--axi-text);
  font: var(--axi-t-micro); letter-spacing: var(--axi-ls-micro); text-transform: uppercase;
}
.axi-prose td { padding: 10px 12px; border-top: var(--axi-border-hairline) solid var(--axi-rule); }

.axi-prose hr {
  margin: 30px 0; height: 0;
  border: 0; border-top: var(--axi-border-control) solid var(--axi-rule);
}

.axi-prose img { max-width: 100%; height: auto; border-radius: var(--axi-radius-sm); }
