/* =============================================================================
   Foremast Design Standard v1.0.0 — reference implementation
   -----------------------------------------------------------------------------
   Framework-agnostic. Drop this file into any project (Zola, React, Angular,
   Astro, Hugo, a plain HTML page) and the identity comes with it.

   Cascade layers, in order of increasing precedence:

     fm.tokens      custom properties on :root
     fm.reset       box model, media defaults, motion opt-out
     fm.base        html/body, focus, selection — nothing element-specific
     fm.prose       document typography, scoped to .fm-prose
     fm.components  .fm-* component classes
     fm.utilities   a deliberately small set of layout helpers

   Everything an application author writes lands *after* these layers and wins
   without specificity fights. To override inside a layer, use @layer fm.overrides
   (declared last, empty here, for consumers).

   Element selectors appear only inside .fm-prose, and are wrapped in :where() so
   they carry zero specificity. Nothing in this file styles a bare <h1>, <a>,
   <table> or <button> at document scope. That is what makes it safe in an SPA.
   ========================================================================== */

@layer fm.tokens, fm.reset, fm.base, fm.prose, fm.components, fm.utilities,
  fm.overrides;

/* =============================================================================
   fm.tokens
   ========================================================================== */

@layer fm.tokens {
  /* ---------------------------------------------------------------------------
     Themes
     ---------------------------------------------------------------------------
     Two schemes, one system. Dark ("night watch") is canonical — the phosphor
     screen on the bridge at night. Light ("chart table") is its daytime
     complement — ink and pencil on a paper chart. They share every non-color
     token: the same scale, the same rhythm, the same ASCII language. Only the
     twenty-one color tokens below change.

     Selection order, lowest precedence first:

       1. :root                                  -> dark (the default, and the
                                                    fallback for any engine that
                                                    cannot evaluate the others)
       2. @media (prefers-color-scheme: light)   -> light, unless pinned to dark
       3. [data-fm-theme="light"|"dark"]         -> explicit, always wins

     To pin a project to dark-only, put data-fm-theme="dark" on <html> and stop
     shipping a toggle. Nothing else changes.

     Every `color-scheme` below is declared twice. The second declaration adds
     the `only` keyword, which forbids the user agent from applying its own
     automatic darkening or lightening on top of ours — Chrome's Auto Dark
     Theme, and the equivalent in Samsung Internet, Opera and Vivaldi. Engines
     that do not understand `only` drop the second declaration and keep the
     first, so nothing is lost. See 01-tokens.md § Browser and extension
     overrides for the rest of the story, including the Dark Reader lock.
     ------------------------------------------------------------------------ */

  /* -- dark: night watch (canonical) ---------------------------------------- */
  :root {
    color-scheme: dark;
    color-scheme: only dark;

    --fm-color-bg: #0b0f17;
    --fm-color-surface: #0e1620;
    --fm-color-scrim: rgb(11 15 23 / 0.82);

    --fm-color-text: #9ab8a8;
    --fm-color-text-dim: #628a74;
    --fm-color-text-bright: #c8ddd4;

    --fm-color-accent: #2cb870;
    --fm-color-accent-dim: #1a7046;
    --fm-color-link: #4db890;
    --fm-color-link-hover: #80d4b0;

    --fm-color-border: #1c3028;
    --fm-color-border-strong: #4a705d;
    --fm-color-selection: #18382a;

    --fm-color-danger: #cf6b6b;
    --fm-color-danger-dim: #9a4c4c;
    --fm-color-warning: #c9a15c;
    --fm-color-warning-dim: #8a6c36;
    --fm-color-info: #6fa8c4;
    --fm-color-info-dim: #4a7488;
    --fm-color-success: var(--fm-color-accent);
    --fm-color-success-dim: var(--fm-color-accent-dim);
  }

  /* -- light: chart table ----------------------------------------------------
     Buff chart paper, sea-ink text, the same phosphor green darkened until it
     reads as pencil-and-ink rather than glow. `surface` is *lighter* than `bg`
     in both themes: an inset panel is always a lift away from the page.     */
  @media (prefers-color-scheme: light) {
    :root:not([data-fm-theme="dark"]) {
      color-scheme: light;
      color-scheme: only light;

      --fm-color-bg: #ece8dc;
      --fm-color-surface: #f6f3ec;
      --fm-color-scrim: rgb(30 58 52 / 0.55);

      --fm-color-text: #1e3a34;
      --fm-color-text-dim: #4a6a5c;
      --fm-color-text-bright: #0e1c23;

      --fm-color-accent: #0d6b3f;
      --fm-color-accent-dim: #3d8a63;
      --fm-color-link: #136b52;
      --fm-color-link-hover: #093f31;

      --fm-color-border: #d5cfbe;
      --fm-color-border-strong: #7e786a;
      --fm-color-selection: #cfe4d6;

      --fm-color-danger: #9b2f2f;
      --fm-color-danger-dim: #b96a6a;
      --fm-color-warning: #7d5a0e;
      --fm-color-warning-dim: #9c7c33;
      --fm-color-info: #1b5674;
      --fm-color-info-dim: #4f87a3;
    }
  }

  /* Explicit selection. Same values; declared separately because a media query
     and an attribute selector cannot be combined in one rule. */
  :root[data-fm-theme="light"] {
    color-scheme: light;
    color-scheme: only light;

    --fm-color-bg: #ece8dc;
    --fm-color-surface: #f6f3ec;
    --fm-color-scrim: rgb(30 58 52 / 0.55);

    --fm-color-text: #1e3a34;
    --fm-color-text-dim: #4a6a5c;
    --fm-color-text-bright: #0e1c23;

    --fm-color-accent: #0d6b3f;
    --fm-color-accent-dim: #3d8a63;
    --fm-color-link: #136b52;
    --fm-color-link-hover: #093f31;

    --fm-color-border: #d5cfbe;
    --fm-color-border-strong: #7e786a;
    --fm-color-selection: #cfe4d6;

    --fm-color-danger: #9b2f2f;
    --fm-color-danger-dim: #b96a6a;
    --fm-color-warning: #7d5a0e;
    --fm-color-warning-dim: #9c7c33;
    --fm-color-info: #1b5674;
    --fm-color-info-dim: #4f87a3;
  }

  :root[data-fm-theme="dark"] {
    color-scheme: dark;
    color-scheme: only dark;
  }

  /* -- shared tokens: identical in both themes ------------------------------ */
  :root {
    /* -- type --------------------------------------------------------------- */
    --fm-font-mono:
      "Consolas", "Menlo", "DejaVu Sans Mono", "Lucida Console", monospace;
    --fm-weight-normal: 400;
    --fm-weight-bold: 700;
    --fm-leading-reading: 1.65;
    --fm-leading-heading: 1.3;
    --fm-leading-ui: 1.4;
    --fm-tracking-title: 0.05em;

    --fm-size-root: 15px;
    --fm-size-1: 0.8rem;
    --fm-size-2: 0.85rem;
    --fm-size-3: 0.875rem;
    --fm-size-4: 0.9rem;
    --fm-size-5: 0.95rem;
    --fm-size-6: 1rem;
    --fm-size-7: 1.05rem;
    --fm-size-8: 1.15rem;
    --fm-size-9: 1.2rem;
    --fm-size-10: 1.4rem;
    --fm-size-inline-code: 0.88em;

    /* role aliases — prefer these in component code */
    --fm-size-body: var(--fm-size-6);
    --fm-size-ui: var(--fm-size-4);
    --fm-size-meta: var(--fm-size-2);
    --fm-size-caption: var(--fm-size-1);
    --fm-size-code: var(--fm-size-3);

    /* -- space (block rhythm, rem) ------------------------------------------ */
    --fm-space-0: 0;
    --fm-space-1: 0.2rem;
    --fm-space-2: 0.4rem;
    --fm-space-3: 0.6rem;
    --fm-space-4: 0.75rem;
    --fm-space-5: 1rem;
    --fm-space-6: 1.5rem;
    --fm-space-7: 2rem;
    --fm-space-8: 3rem;

    /* -- inline space (character cells) ------------------------------------- */
    --fm-inline-1: 1ch;
    --fm-inline-2: 1.5ch;
    --fm-inline-3: 2ch;
    --fm-inline-4: 3ch;
    --fm-inline-5: 4ch;

    /* -- measure ------------------------------------------------------------ */
    --fm-measure-document: 78ch;
    --fm-measure-narrow: 60ch;
    --fm-measure-app: 120ch;
    --fm-measure-sidebar: 24ch;
    --fm-measure-touch: 44px;
    --fm-gutter: var(--fm-space-6);

    /* -- lines -------------------------------------------------------------- */
    --fm-border-hairline: 1px;
    --fm-border-rule: 2px;
    --fm-radius: 0;
    --fm-radius-max: 2px;

    /* -- stacking ----------------------------------------------------------- */
    --fm-z-base: 0;
    --fm-z-sticky: 100;
    --fm-z-dropdown: 200;
    --fm-z-scrim: 300;
    --fm-z-modal: 400;
    --fm-z-toast: 500;
    --fm-z-skip: 600;

    /* -- motion budget ------------------------------------------------------ */
    --fm-duration-instant: 0ms;
    --fm-duration-fade: 120ms;
    --fm-duration-tick: 250ms;
    --fm-duration-cycle: 1000ms;
    --fm-ease-step: steps(4, end);

    /* -- misc --------------------------------------------------------------- */
    --fm-opacity-disabled: 0.45;

    /* -- safe areas (standalone PWA) ---------------------------------------- */
    --fm-safe-top: env(safe-area-inset-top, 0px);
    --fm-safe-right: env(safe-area-inset-right, 0px);
    --fm-safe-bottom: env(safe-area-inset-bottom, 0px);
    --fm-safe-left: env(safe-area-inset-left, 0px);
  }

  @media (max-width: 600px) {
    :root {
      --fm-size-root: 14px;
      --fm-gutter: var(--fm-space-5);
    }
  }

  /* Under reduced-motion every looping affordance collapses to a static glyph. */
  @media (prefers-reduced-motion: reduce) {
    :root {
      --fm-duration-fade: 0ms;
      --fm-duration-tick: 0ms;
      --fm-duration-cycle: 0ms;
    }
  }
}

/* =============================================================================
   fm.reset
   ========================================================================== */

@layer fm.reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }

  img,
  svg,
  video,
  canvas {
    max-width: 100%;
    height: auto;
  }

  input,
  button,
  textarea,
  select {
    font: inherit;
    color: inherit;
  }

  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* =============================================================================
   fm.base
   ========================================================================== */

@layer fm.base {
  /* `color-scheme` is set per theme in fm.tokens. It is what makes native
     scrollbars, selects, date pickers and the canvas follow the theme —
     without it those controls render light on a dark page in every framework.
     It is also declared with `only` there, which is what keeps a browser's own
     forced-dark pass from re-tinting a page that already answered the
     question. */

  html {
    font-family: var(--fm-font-mono);
    font-size: var(--fm-size-root);
    line-height: var(--fm-leading-reading);
    background-color: var(--fm-color-bg);
    color: var(--fm-color-text);
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
  }

  body {
    min-height: 100vh;
    min-height: 100dvh;
    background-color: var(--fm-color-bg);
  }

  ::selection {
    background: var(--fm-color-selection);
    color: var(--fm-color-text-bright);
  }

  /* One focus treatment for the whole system. Never remove it without a
     replacement of equal or better visibility. */
  :focus-visible {
    outline: var(--fm-border-hairline) solid var(--fm-color-accent);
    outline-offset: 2px;
  }

  [hidden] {
    display: none !important;
  }
}

/* =============================================================================
   fm.prose — document typography, scoped

   Put `fm-prose` on the element that wraps authored/markdown content. On a
   content site that is <body> or <main>. In an application it is *only* the
   region rendering long-form copy, so app chrome is never hijacked.
   ========================================================================== */

@layer fm.prose {
  .fm-prose {
    max-width: var(--fm-measure-document);
    font-size: var(--fm-size-body);
    line-height: var(--fm-leading-reading);
    color: var(--fm-color-text);
  }

  .fm-prose :where(h1, h2, h3, h4, h5, h6) {
    color: var(--fm-color-text-bright);
    font-weight: var(--fm-weight-normal);
    line-height: var(--fm-leading-heading);
    margin: var(--fm-space-6) 0 var(--fm-space-3);
  }

  .fm-prose :where(:first-child) {
    margin-top: 0;
  }

  /* Sigils. Declared twice: plain for older engines, then with `/ ''` alt text
     so screen readers skip the ASCII decoration. */
  .fm-prose :where(h1) {
    font-size: var(--fm-size-10);
  }
  .fm-prose :where(h1)::before {
    content: "#  ";
    content: "#  " / "";
    color: var(--fm-color-accent);
  }
  .fm-prose :where(h2) {
    font-size: var(--fm-size-9);
  }
  .fm-prose :where(h2)::before {
    content: "## ";
    content: "## " / "";
    color: var(--fm-color-accent);
  }
  .fm-prose :where(h3) {
    font-size: var(--fm-size-7);
  }
  .fm-prose :where(h3)::before {
    content: "### ";
    content: "### " / "";
    color: var(--fm-color-accent);
  }
  .fm-prose :where(h4) {
    font-size: var(--fm-size-6);
  }
  .fm-prose :where(h4)::before {
    content: "#### ";
    content: "#### " / "";
    color: var(--fm-color-accent);
  }
  .fm-prose :where(h5) {
    font-size: var(--fm-size-5);
  }
  .fm-prose :where(h5)::before {
    content: "##### ";
    content: "##### " / "";
    color: var(--fm-color-accent);
  }
  .fm-prose :where(h6) {
    font-size: var(--fm-size-4);
  }
  .fm-prose :where(h6)::before {
    content: "###### ";
    content: "###### " / "";
    color: var(--fm-color-accent);
  }

  .fm-prose :where(p) {
    margin: var(--fm-space-4) 0;
  }

  .fm-prose :where(a) {
    color: var(--fm-color-link);
    text-decoration: none;
  }
  .fm-prose :where(a):hover {
    color: var(--fm-color-link-hover);
    text-decoration: underline;
  }

  .fm-prose :where(strong) {
    color: var(--fm-color-text-bright);
    font-weight: var(--fm-weight-bold);
  }
  .fm-prose :where(em) {
    color: var(--fm-color-text-bright);
    font-style: italic;
  }

  .fm-prose :where(hr) {
    border: none;
    border-top: var(--fm-border-hairline) solid var(--fm-color-border);
    margin: var(--fm-space-3) 0;
  }

  /* -- lists: hanging indent, so wrapped lines align past the dash ---------- */
  .fm-prose :where(ul, ol) {
    margin: var(--fm-space-4) 0;
    padding-left: var(--fm-inline-3);
  }
  .fm-prose :where(ul) {
    list-style: none;
  }
  .fm-prose :where(ul) > li {
    position: relative;
    padding-left: var(--fm-inline-3);
  }
  .fm-prose :where(ul) > li::before {
    content: "- ";
    content: "- " / "";
    color: var(--fm-color-accent-dim);
    position: absolute;
    left: 0;
  }
  .fm-prose :where(ol) {
    list-style: decimal;
  }
  .fm-prose :where(ol) > li::marker {
    color: var(--fm-color-accent-dim);
  }
  .fm-prose :where(li) {
    margin: var(--fm-space-1) 0;
  }

  .fm-prose :where(dl) {
    margin: var(--fm-space-4) 0;
  }
  .fm-prose :where(dt) {
    color: var(--fm-color-text-bright);
  }
  .fm-prose :where(dd) {
    color: var(--fm-color-text-dim);
    padding-left: var(--fm-inline-3);
  }

  /* -- code ---------------------------------------------------------------- */
  .fm-prose :where(code) {
    font-family: var(--fm-font-mono);
    font-size: var(--fm-size-inline-code);
    background: var(--fm-color-surface);
    color: var(--fm-color-text-bright);
    padding: 0.1em 0.35em;
    border-radius: var(--fm-radius-max);
  }
  .fm-prose :where(pre) {
    background: var(--fm-color-surface);
    border-left: var(--fm-border-rule) solid var(--fm-color-accent-dim);
    padding: var(--fm-space-5) 1.25rem;
    margin: var(--fm-space-5) 0;
    overflow-x: auto;
  }
  .fm-prose :where(pre) :where(code) {
    background: none;
    padding: 0;
    border-radius: 0;
    color: inherit;
    font-size: var(--fm-size-code);
  }

  /* -- blockquote ---------------------------------------------------------- */
  .fm-prose :where(blockquote) {
    margin: var(--fm-space-5) 0;
    padding: var(--fm-space-2) 0 var(--fm-space-2) var(--fm-inline-2);
    border-left: var(--fm-border-rule) solid var(--fm-color-accent-dim);
    color: var(--fm-color-text-dim);
    font-style: italic;
  }

  /* -- table --------------------------------------------------------------- */
  .fm-prose :where(table) {
    width: 100%;
    border-collapse: collapse;
    margin: var(--fm-space-5) 0;
    font-size: var(--fm-size-ui);
  }
  .fm-prose :where(th) {
    color: var(--fm-color-text-bright);
    font-weight: var(--fm-weight-normal);
    text-align: left;
  }
  .fm-prose :where(th, td) {
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: var(--fm-space-2) var(--fm-space-4);
  }
  .fm-prose :where(tbody tr):hover :where(td) {
    background: var(--fm-color-surface);
  }
  /* A prose table that overflows must scroll inside its own wrapper. */
  .fm-prose .fm-table-scroll {
    overflow-x: auto;
    margin: var(--fm-space-5) 0;
  }
  .fm-prose .fm-table-scroll > table {
    margin: 0;
  }

  /* -- media --------------------------------------------------------------- */
  .fm-prose :where(img) {
    display: block;
    border: var(--fm-border-hairline) solid var(--fm-color-border);
    margin: var(--fm-space-5) 0;
  }
  .fm-prose :where(figure) {
    margin: var(--fm-space-5) 0;
  }
  .fm-prose :where(figcaption) {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-caption);
  }
  .fm-prose :where(figcaption)::before {
    content: "// ";
    content: "// " / "";
  }
}

/* =============================================================================
   fm.components
   ========================================================================== */

@layer fm.components {
  /* -- prose immunity --------------------------------------------------------
     Component lists keep their own shape when nested inside .fm-prose. Without
     this, the prose `- ` bullet and hanging indent leak into entry lists,
     menus, panels and tag rows. Declared first so components that define their
     own ::before (breadcrumbs, menu markers) can still override it below.    */
  :is(
      .fm-entries,
      .fm-tags,
      .fm-menu,
      .fm-palette__results,
      .fm-panel,
      .fm-toasts
    )
    :where(li) {
    padding-left: 0;
  }
  :is(
      .fm-entries,
      .fm-tags,
      .fm-menu,
      .fm-palette__results,
      .fm-panel,
      .fm-toasts
    )
    :where(li)::before {
    content: none;
  }

  /* -- skip link ----------------------------------------------------------- */
  .fm-skip {
    position: absolute;
    left: -9999px;
    z-index: var(--fm-z-skip);
    background: var(--fm-color-surface);
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    color: var(--fm-color-accent);
    padding: var(--fm-space-2) var(--fm-space-4);
    font-size: var(--fm-size-ui);
    text-decoration: none;
  }
  .fm-skip:focus {
    left: var(--fm-gutter);
    top: var(--fm-space-2);
  }

  /* -- document page ------------------------------------------------------- */
  .fm-page {
    max-width: var(--fm-measure-document);
    margin-inline: auto;
    padding: var(--fm-space-7) var(--fm-gutter);
    padding-top: calc(var(--fm-space-7) + var(--fm-safe-top));
    padding-bottom: calc(var(--fm-space-7) + var(--fm-safe-bottom));
    min-height: 100dvh;
  }
  @media (max-width: 600px) {
    .fm-page {
      padding: var(--fm-gutter);
    }
  }

  /* -- app shell ------------------------------------------------------------
     Grid areas: masthead / rail / view / statusline. Below breakpoint.lg the
     rail leaves the grid and becomes a drawer (see .fm-drawer).             */
  .fm-shell {
    display: grid;
    grid-template-columns: var(--fm-measure-sidebar) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "masthead masthead"
      "rail     view"
      "statusline statusline";
    min-height: 100dvh;
    max-width: var(--fm-measure-app);
    margin-inline: auto;
    padding-left: var(--fm-safe-left);
    padding-right: var(--fm-safe-right);
  }
  .fm-shell__masthead {
    grid-area: masthead;
    position: sticky;
    top: 0;
    z-index: var(--fm-z-sticky);
    background: var(--fm-color-bg);
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: calc(var(--fm-space-3) + var(--fm-safe-top)) var(--fm-gutter)
      var(--fm-space-3);
  }
  .fm-shell__rail {
    grid-area: rail;
    border-right: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: var(--fm-space-5) var(--fm-gutter);
    overflow-y: auto;
  }
  .fm-shell__view {
    grid-area: view;
    padding: var(--fm-space-5) var(--fm-gutter);
    overflow-y: auto;
    min-width: 0;
  }
  .fm-shell__statusline {
    grid-area: statusline;
    position: sticky;
    bottom: 0;
    z-index: var(--fm-z-sticky);
  }
  @media (max-width: 900px) {
    .fm-shell {
      grid-template-columns: minmax(0, 1fr);
      grid-template-areas:
        "masthead"
        "view"
        "statusline";
    }
    .fm-shell__rail {
      display: none;
    }
  }

  /* -- masthead / site header ---------------------------------------------- */
  .fm-masthead {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--fm-space-2);
  }
  .fm-masthead__title {
    color: var(--fm-color-text-bright);
    font-size: var(--fm-size-8);
    font-weight: var(--fm-weight-bold);
    letter-spacing: var(--fm-tracking-title);
    text-decoration: none;
  }
  .fm-masthead__title:hover {
    color: var(--fm-color-accent);
  }
  .fm-masthead__tagline {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
  }
  .fm-masthead__tagline::before {
    content: "// ";
    content: "// " / "";
  }
  @media (max-width: 600px) {
    .fm-masthead {
      flex-direction: column;
    }
  }

  /* -- nav ------------------------------------------------------------------
     Brackets are supplied by CSS so screen readers are not read "left bracket
     home right bracket". Markup carries the bare label.                     */
  .fm-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fm-space-2);
    line-height: var(--fm-leading-ui);
  }
  .fm-nav__item {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-ui);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    min-height: var(--fm-measure-touch);
    padding-inline: var(--fm-space-1);
  }
  .fm-nav__item::before {
    content: "[";
    content: "[" / "";
  }
  .fm-nav__item::after {
    content: "]";
    content: "]" / "";
  }
  .fm-nav__item:hover,
  .fm-nav__item[aria-current] {
    color: var(--fm-color-accent);
  }
  /* Vertical rail nav: full-width rows, no touch-target padding needed. */
  .fm-nav--rail {
    flex-direction: column;
    gap: 0;
  }
  .fm-nav--rail .fm-nav__item {
    min-height: 0;
    padding: var(--fm-space-1) 0;
  }

  .fm-rule {
    border: none;
    border-top: var(--fm-border-hairline) solid var(--fm-color-border);
    margin: var(--fm-space-3) 0;
  }

  /* -- section label -------------------------------------------------------- */
  .fm-label {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
    margin: var(--fm-space-6) 0 var(--fm-space-2);
  }
  .fm-label::before {
    content: "// ";
    content: "// " / "";
  }

  /* -- empty state ---------------------------------------------------------- */
  .fm-empty {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
    margin: var(--fm-space-5) 0;
    max-width: var(--fm-measure-narrow);
  }
  .fm-empty::before {
    content: "// ";
    content: "// " / "";
  }

  /* -- entry list ----------------------------------------------------------- */
  .fm-entries {
    list-style: none;
    padding: 0;
    margin: var(--fm-space-5) 0;
  }
  .fm-entries > li {
    margin: 1.1rem 0;
    padding-bottom: 1.1rem;
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-border);
  }
  .fm-entries > li:last-child {
    border-bottom: none;
  }
  .fm-entry__date {
    display: block;
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
  }
  .fm-entry__title {
    display: block;
    color: var(--fm-color-text-bright);
    font-size: var(--fm-size-7);
    text-decoration: none;
    margin: var(--fm-space-1) 0;
  }
  .fm-entry__title::before {
    content: ">> ";
    content: ">> " / "";
    color: var(--fm-color-accent-dim);
  }
  .fm-entry__title:hover {
    color: var(--fm-color-accent);
  }
  .fm-entry__desc {
    display: block;
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-code);
  }

  /* -- tags ----------------------------------------------------------------- */
  .fm-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fm-space-2);
    margin: var(--fm-space-2) 0;
  }
  .fm-tag {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
    text-decoration: none;
  }
  .fm-tag::before {
    content: "[";
    content: "[" / "";
  }
  .fm-tag::after {
    content: "]";
    content: "]" / "";
  }
  .fm-tag:hover {
    color: var(--fm-color-accent);
  }

  /* -- badge (non-interactive status chip) ---------------------------------- */
  .fm-badge {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
    white-space: nowrap;
  }
  .fm-badge::before {
    content: "[";
    content: "[" / "";
  }
  .fm-badge::after {
    content: "]";
    content: "]" / "";
  }
  .fm-badge[data-tone="danger"] {
    color: var(--fm-color-danger);
  }
  .fm-badge[data-tone="warning"] {
    color: var(--fm-color-warning);
  }
  .fm-badge[data-tone="info"] {
    color: var(--fm-color-info);
  }
  .fm-badge[data-tone="success"] {
    color: var(--fm-color-success);
  }

  /* -- pagination ----------------------------------------------------------- */
  .fm-pagination {
    display: flex;
    align-items: center;
    gap: var(--fm-space-5);
    margin: var(--fm-space-7) 0;
  }
  .fm-pagination a {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-ui);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    min-height: var(--fm-measure-touch);
  }
  .fm-pagination a::before {
    content: "[";
    content: "[" / "";
  }
  .fm-pagination a::after {
    content: "]";
    content: "]" / "";
  }
  .fm-pagination a:hover {
    color: var(--fm-color-accent);
  }
  .fm-pagination__count {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
  }

  /* -- breadcrumbs ---------------------------------------------------------- */
  .fm-crumbs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fm-space-2);
    list-style: none;
    padding: 0;
    font-size: var(--fm-size-meta);
    color: var(--fm-color-text-dim);
  }
  .fm-crumbs a {
    color: var(--fm-color-text-dim);
    text-decoration: none;
  }
  .fm-crumbs a:hover {
    color: var(--fm-color-accent);
  }
  .fm-crumbs li + li::before {
    content: "/ ";
    content: "/ " / "";
    color: var(--fm-color-accent-dim);
  }
  .fm-crumbs [aria-current] {
    color: var(--fm-color-text-bright);
  }

  /* -- panel (card, sidebar box, TOC) --------------------------------------- */
  .fm-panel {
    background: var(--fm-color-surface);
    border: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: var(--fm-space-4) var(--fm-space-5);
    margin: var(--fm-space-5) 0;
    font-size: var(--fm-size-code);
  }
  .fm-panel__title {
    color: var(--fm-color-text-dim);
    margin-bottom: var(--fm-space-2);
  }
  .fm-panel__title::before {
    content: "// ";
    content: "// " / "";
  }
  .fm-panel a {
    color: var(--fm-color-text-dim);
    text-decoration: none;
  }
  .fm-panel a:hover {
    color: var(--fm-color-accent);
  }
  .fm-panel ul {
    list-style: none;
    padding-left: var(--fm-inline-2);
  }
  .fm-panel li {
    margin: 0.15rem 0;
  }

  /* -- readout (key/value pairs) -------------------------------------------- */
  .fm-readout {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: var(--fm-space-1) var(--fm-inline-3);
    font-size: var(--fm-size-ui);
    margin: var(--fm-space-5) 0;
  }
  .fm-readout dt {
    color: var(--fm-color-text-dim);
  }
  .fm-readout dt::after {
    content: " ..";
    content: " .." / "";
    color: var(--fm-color-accent-dim);
  }
  .fm-readout dd {
    color: var(--fm-color-text-bright);
  }

  /* -- callout / inline alert ------------------------------------------------ */
  .fm-callout {
    border-left: var(--fm-border-rule) solid var(--fm-color-info-dim);
    background: var(--fm-color-surface);
    padding: var(--fm-space-4) var(--fm-space-5);
    margin: var(--fm-space-5) 0;
    font-size: var(--fm-size-code);
    max-width: var(--fm-measure-document);
  }
  .fm-callout__title {
    color: var(--fm-color-info);
    margin-bottom: var(--fm-space-1);
  }
  .fm-callout__title::before {
    content: "[i] ";
    content: "[i] " / "";
  }
  .fm-callout[data-tone="warning"] {
    border-left-color: var(--fm-color-warning-dim);
  }
  .fm-callout[data-tone="warning"] .fm-callout__title {
    color: var(--fm-color-warning);
  }
  .fm-callout[data-tone="warning"] .fm-callout__title::before {
    content: "[!] ";
    content: "[!] " / "";
  }
  .fm-callout[data-tone="danger"] {
    border-left-color: var(--fm-color-danger-dim);
  }
  .fm-callout[data-tone="danger"] .fm-callout__title {
    color: var(--fm-color-danger);
  }
  .fm-callout[data-tone="danger"] .fm-callout__title::before {
    content: "[x] ";
    content: "[x] " / "";
  }
  .fm-callout[data-tone="success"] {
    border-left-color: var(--fm-color-success-dim);
  }
  .fm-callout[data-tone="success"] .fm-callout__title {
    color: var(--fm-color-success);
  }
  .fm-callout[data-tone="success"] .fm-callout__title::before {
    content: "[ok] ";
    content: "[ok] " / "";
  }

  /* -- button ----------------------------------------------------------------
     Brackets come from CSS. Markup: <button class="fm-btn">submit</button>  */
  .fm-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* No gap: the ::before/::after brackets are flex items and must hug the
       label. Space between a glyph and a label belongs in the text content. */
    gap: 0;
    min-height: var(--fm-measure-touch);
    padding: var(--fm-space-2) var(--fm-space-4);
    background: transparent;
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    border-radius: var(--fm-radius);
    color: var(--fm-color-text-dim);
    font-family: var(--fm-font-mono);
    font-size: var(--fm-size-ui);
    line-height: var(--fm-leading-ui);
    text-decoration: none;
    cursor: pointer;
  }
  .fm-btn::before {
    content: "[";
    content: "[" / "";
  }
  .fm-btn::after {
    content: "]";
    content: "]" / "";
  }
  .fm-btn:hover:not([disabled], [aria-disabled="true"]) {
    color: var(--fm-color-accent);
    border-color: var(--fm-color-accent);
  }
  .fm-btn--primary {
    color: var(--fm-color-accent);
    border-color: var(--fm-color-accent-dim);
  }
  .fm-btn--danger {
    color: var(--fm-color-danger);
    border-color: var(--fm-color-danger-dim);
  }
  .fm-btn--danger:hover:not([disabled], [aria-disabled="true"]) {
    color: var(--fm-color-danger);
    border-color: var(--fm-color-danger);
  }
  /* Ghost: no frame. Only for dense toolbars where frames would create noise. */
  .fm-btn--ghost {
    border-color: transparent;
    min-height: 0;
    padding: 0 var(--fm-space-1);
  }
  .fm-btn[disabled],
  .fm-btn[aria-disabled="true"] {
    opacity: var(--fm-opacity-disabled);
    cursor: not-allowed;
  }
  .fm-btn[aria-busy="true"] {
    color: var(--fm-color-text-dim);
    cursor: progress;
  }

  /* -- form fields ----------------------------------------------------------- */
  .fm-field {
    display: block;
    margin: var(--fm-space-5) 0;
    max-width: var(--fm-measure-narrow);
  }
  .fm-field__label {
    display: block;
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
    margin-bottom: var(--fm-space-1);
  }
  .fm-field__label::before {
    content: "// ";
    content: "// " / "";
  }
  .fm-field__label[data-required]::after {
    content: " *";
    color: var(--fm-color-accent);
  }
  .fm-input,
  .fm-textarea,
  .fm-select {
    width: 100%;
    min-height: var(--fm-measure-touch);
    background: var(--fm-color-surface);
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    border-radius: var(--fm-radius);
    color: var(--fm-color-text);
    font-family: var(--fm-font-mono);
    font-size: var(--fm-size-ui);
    padding: var(--fm-space-2) var(--fm-space-3);
  }
  .fm-textarea {
    min-height: 6lh;
    resize: vertical;
  }
  .fm-input::placeholder,
  .fm-textarea::placeholder {
    color: var(--fm-color-text-dim);
    opacity: 1;
  }
  .fm-input:focus-visible,
  .fm-textarea:focus-visible,
  .fm-select:focus-visible {
    border-color: var(--fm-color-accent);
  }
  .fm-input[aria-invalid="true"],
  .fm-textarea[aria-invalid="true"],
  .fm-select[aria-invalid="true"] {
    border-color: var(--fm-color-danger);
  }
  .fm-input[disabled],
  .fm-textarea[disabled],
  .fm-select[disabled] {
    opacity: var(--fm-opacity-disabled);
    cursor: not-allowed;
  }
  .fm-field__help {
    display: block;
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
    margin-top: var(--fm-space-1);
  }
  .fm-field__error {
    display: block;
    color: var(--fm-color-danger);
    font-size: var(--fm-size-meta);
    margin-top: var(--fm-space-1);
  }
  .fm-field__error::before {
    content: "[!] ";
    content: "[!] " / "";
  }

  /* Checkbox / radio: the native control is replaced by an ASCII cell. The
     input stays in the accessibility tree, visually hidden but focusable. */
  .fm-check {
    display: inline-flex;
    align-items: center;
    gap: var(--fm-inline-1);
    min-height: var(--fm-measure-touch);
    color: var(--fm-color-text);
    font-size: var(--fm-size-ui);
    cursor: pointer;
  }
  .fm-check > input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .fm-check > input + span::before {
    content: "[ ] ";
    content: "[ ] " / "";
    color: var(--fm-color-text-dim);
  }
  .fm-check > input:checked + span::before {
    content: "[x] ";
    content: "[x] " / "";
    color: var(--fm-color-accent);
  }
  .fm-check > input[type="radio"] + span::before {
    content: "( ) ";
    content: "( ) " / "";
  }
  .fm-check > input[type="radio"]:checked + span::before {
    content: "(*) ";
    content: "(*) " / "";
  }
  .fm-check > input:focus-visible + span {
    outline: var(--fm-border-hairline) solid var(--fm-color-accent);
    outline-offset: 2px;
  }
  .fm-check > input[disabled] + span {
    opacity: var(--fm-opacity-disabled);
  }

  /* -- data table ------------------------------------------------------------ */
  .fm-table-wrap {
    overflow: auto;
    max-height: 100%;
    border: var(--fm-border-hairline) solid var(--fm-color-border);
  }
  .fm-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fm-size-ui);
    line-height: var(--fm-leading-ui);
  }
  .fm-table th {
    position: sticky;
    top: 0;
    z-index: var(--fm-z-sticky);
    background: var(--fm-color-surface);
    color: var(--fm-color-text-bright);
    font-weight: var(--fm-weight-normal);
    text-align: left;
  }
  .fm-table th,
  .fm-table td {
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: var(--fm-space-2) var(--fm-space-4);
    white-space: nowrap;
  }
  .fm-table td[data-numeric],
  .fm-table th[data-numeric] {
    text-align: right;
    font-variant-numeric: tabular-nums;
  }
  .fm-table tbody tr:hover td {
    background: var(--fm-color-surface);
  }
  .fm-table tbody tr[aria-selected="true"] td {
    background: var(--fm-color-selection);
    color: var(--fm-color-text-bright);
  }
  .fm-table th[aria-sort] {
    cursor: pointer;
    color: var(--fm-color-accent);
  }
  .fm-table th[aria-sort="ascending"]::after {
    content: " [^]";
    content: " [^]" / "";
  }
  .fm-table th[aria-sort="descending"]::after {
    content: " [v]";
    content: " [v]" / "";
  }
  .fm-table th[aria-sort="none"]::after {
    content: " [-]";
    content: " [-]" / "";
    color: var(--fm-color-text-dim);
  }

  /* -- tabs ------------------------------------------------------------------ */
  .fm-tabs {
    display: flex;
    gap: var(--fm-space-2);
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-border);
  }
  .fm-tab {
    background: none;
    border: none;
    border-bottom: var(--fm-border-rule) solid transparent;
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-ui);
    padding: var(--fm-space-2) var(--fm-space-3);
    min-height: var(--fm-measure-touch);
    cursor: pointer;
  }
  .fm-tab:hover {
    color: var(--fm-color-accent);
  }
  .fm-tab[aria-selected="true"] {
    color: var(--fm-color-text-bright);
    border-bottom-color: var(--fm-color-accent);
  }

  /* -- menu / dropdown -------------------------------------------------------- */
  .fm-menu {
    position: absolute;
    z-index: var(--fm-z-dropdown);
    min-width: 20ch;
    background: var(--fm-color-bg);
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    padding: var(--fm-space-1) 0;
    list-style: none;
    font-size: var(--fm-size-ui);
  }
  .fm-menu__item {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    color: var(--fm-color-text);
    padding: var(--fm-space-1) var(--fm-space-4);
    text-decoration: none;
    cursor: pointer;
  }
  .fm-menu__item:hover,
  .fm-menu__item:focus-visible,
  .fm-menu__item[data-active] {
    background: var(--fm-color-surface);
    color: var(--fm-color-accent);
    outline: none;
  }
  .fm-menu__item::before {
    content: "  ";
    content: "  " / "";
  }
  .fm-menu__item[aria-checked="true"]::before,
  .fm-menu__item[data-active]::before {
    content: "> ";
    content: "> " / "";
    color: var(--fm-color-accent);
  }
  .fm-menu__sep {
    border: none;
    border-top: var(--fm-border-hairline) solid var(--fm-color-border);
    margin: var(--fm-space-1) 0;
  }

  /* -- dialog ---------------------------------------------------------------- */
  .fm-dialog {
    width: min(var(--fm-measure-narrow), calc(100vw - 2 * var(--fm-gutter)));
    max-height: 80dvh;
    padding: 0;
    background: var(--fm-color-bg);
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    border-radius: var(--fm-radius);
    color: var(--fm-color-text);
    z-index: var(--fm-z-modal);
  }
  .fm-dialog::backdrop {
    background: var(--fm-color-scrim);
  }
  .fm-dialog__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--fm-inline-3);
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: var(--fm-space-3) var(--fm-space-5);
  }
  .fm-dialog__title {
    color: var(--fm-color-text-bright);
    font-size: var(--fm-size-7);
    font-weight: var(--fm-weight-normal);
  }
  .fm-dialog__title::before {
    content: "## ";
    content: "## " / "";
    color: var(--fm-color-accent);
  }
  .fm-dialog__body {
    padding: var(--fm-space-5);
    overflow-y: auto;
    font-size: var(--fm-size-ui);
  }
  .fm-dialog__foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--fm-space-4);
    border-top: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: var(--fm-space-3) var(--fm-space-5);
  }

  /* -- drawer (mobile rail) ---------------------------------------------------- */
  .fm-drawer {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: var(--fm-z-modal);
    width: min(var(--fm-measure-sidebar), 80vw);
    background: var(--fm-color-bg);
    border-right: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    padding: calc(var(--fm-space-5) + var(--fm-safe-top)) var(--fm-gutter)
      calc(var(--fm-space-5) + var(--fm-safe-bottom));
    overflow-y: auto;
  }
  .fm-scrim {
    position: fixed;
    inset: 0;
    z-index: var(--fm-z-scrim);
    background: var(--fm-color-scrim);
  }

  /* -- command palette --------------------------------------------------------- */
  .fm-palette {
    position: fixed;
    top: 10vh;
    left: 50%;
    translate: -50% 0;
    z-index: var(--fm-z-modal);
    width: min(var(--fm-measure-document), calc(100vw - 2 * var(--fm-gutter)));
    background: var(--fm-color-bg);
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
  }
  .fm-palette__prompt {
    display: flex;
    align-items: center;
    gap: var(--fm-inline-1);
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-border);
    padding: var(--fm-space-2) var(--fm-space-4);
  }
  .fm-palette__prompt::before {
    content: ">";
    color: var(--fm-color-accent);
  }
  .fm-palette__prompt input {
    flex: 1;
    background: none;
    border: none;
    outline: none;
    color: var(--fm-color-text-bright);
    font-size: var(--fm-size-ui);
  }
  .fm-palette__results {
    list-style: none;
    max-height: 50dvh;
    overflow-y: auto;
    font-size: var(--fm-size-ui);
  }

  /* -- toast ------------------------------------------------------------------- */
  .fm-toasts {
    position: fixed;
    z-index: var(--fm-z-toast);
    inset-block-end: calc(var(--fm-space-5) + var(--fm-safe-bottom));
    inset-inline-end: calc(var(--fm-space-5) + var(--fm-safe-right));
    display: flex;
    flex-direction: column;
    gap: var(--fm-space-2);
    width: min(var(--fm-measure-narrow), calc(100vw - 2 * var(--fm-gutter)));
  }
  .fm-toast {
    background: var(--fm-color-bg);
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    border-left-width: var(--fm-border-rule);
    border-left-color: var(--fm-color-accent-dim);
    padding: var(--fm-space-2) var(--fm-space-4);
    font-size: var(--fm-size-ui);
    color: var(--fm-color-text);
  }
  .fm-toast[data-tone="danger"] {
    border-left-color: var(--fm-color-danger-dim);
  }
  .fm-toast[data-tone="warning"] {
    border-left-color: var(--fm-color-warning-dim);
  }
  .fm-toast[data-tone="info"] {
    border-left-color: var(--fm-color-info-dim);
  }

  /* -- banner (offline, update available, degraded) ----------------------------- */
  .fm-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--fm-inline-3);
    background: var(--fm-color-surface);
    border-bottom: var(--fm-border-hairline) solid var(--fm-color-warning-dim);
    color: var(--fm-color-warning);
    font-size: var(--fm-size-meta);
    padding: var(--fm-space-2) var(--fm-gutter);
  }
  .fm-banner::before {
    content: "[!] ";
    content: "[!] " / "";
  }
  .fm-banner[data-tone="info"] {
    border-bottom-color: var(--fm-color-info-dim);
    color: var(--fm-color-info);
  }
  .fm-banner[data-tone="info"]::before {
    content: "[i] ";
    content: "[i] " / "";
  }

  /* -- status line --------------------------------------------------------------
     The bottom rule of an application. Left/right slots, dense, never wraps.   */
  .fm-statusline {
    display: flex;
    justify-content: space-between;
    gap: var(--fm-inline-3);
    background: var(--fm-color-surface);
    border-top: var(--fm-border-hairline) solid var(--fm-color-border);
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
    line-height: var(--fm-leading-ui);
    padding: var(--fm-space-1) var(--fm-gutter);
    padding-bottom: calc(var(--fm-space-1) + var(--fm-safe-bottom));
    overflow: hidden;
    white-space: nowrap;
  }
  .fm-statusline__slot {
    display: flex;
    gap: var(--fm-inline-3);
    min-width: 0;
  }

  /* -- tooltip -------------------------------------------------------------------- */
  .fm-tooltip {
    position: absolute;
    z-index: var(--fm-z-dropdown);
    background: var(--fm-color-bg);
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    color: var(--fm-color-text);
    font-size: var(--fm-size-meta);
    padding: var(--fm-space-1) var(--fm-space-3);
    max-width: 40ch;
  }

  /* -- progress ---------------------------------------------------------------------
     Determinate: an ASCII gauge driven by --fm-progress (0..1).                    */
  .fm-progress {
    display: inline-flex;
    align-items: baseline;
    gap: var(--fm-inline-1);
    font-size: var(--fm-size-ui);
    color: var(--fm-color-text-dim);
  }
  .fm-progress__gauge {
    position: relative;
    display: inline-block;
    width: 20ch;
    height: 1lh;
    border: var(--fm-border-hairline) solid var(--fm-color-border-strong);
    overflow: hidden;
  }
  .fm-progress__gauge::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: calc(var(--fm-progress, 0) * 100%);
    background: var(--fm-color-accent-dim);
  }
  .fm-progress--indeterminate .fm-progress__gauge::before {
    inline-size: 25%;
    animation: fm-sweep var(--fm-duration-cycle) var(--fm-ease-step) infinite;
  }
  @keyframes fm-sweep {
    from {
      inset-inline-start: 0;
    }
    to {
      inset-inline-start: 75%;
    }
  }

  /* -- spinner ------------------------------------------------------------------------
     CSS-only, no JS: an ellipsis clipped in whole character cells. Under
     reduced motion --fm-duration-cycle is 0 and it renders a static "...".   */
  .fm-spinner {
    display: inline-block;
    width: 3ch;
    overflow: hidden;
    vertical-align: bottom;
    white-space: nowrap;
    color: var(--fm-color-text-dim);
  }
  .fm-spinner::before {
    content: "...";
    content: "..." / "";
  }
  @media (prefers-reduced-motion: no-preference) {
    .fm-spinner::before {
      display: inline-block;
      width: 3ch;
      overflow: hidden;
      animation: fm-ellipsis var(--fm-duration-cycle) var(--fm-ease-step)
        infinite;
    }
    @keyframes fm-ellipsis {
      from {
        width: 0;
      }
      to {
        width: 3ch;
      }
    }
  }

  /* -- skeleton -------------------------------------------------------------------------
     Static block placeholders. No shimmer — motion is not permitted here.  */
  .fm-skeleton {
    display: block;
    background: var(--fm-color-surface);
    border-left: var(--fm-border-rule) solid var(--fm-color-border);
    height: 1lh;
    margin: var(--fm-space-1) 0;
    width: 100%;
  }
  .fm-skeleton--short {
    width: 40%;
  }

  /* -- async region ---------------------------------------------------------------------
     Any container that swaps between loaded / loading / error content.     */
  [data-state="loading"] {
    color: var(--fm-color-text-dim);
  }
  [aria-busy="true"] {
    cursor: progress;
  }

  /* -- error state ------------------------------------------------------------------------ */
  .fm-error {
    border-left: var(--fm-border-rule) solid var(--fm-color-danger-dim);
    padding: var(--fm-space-4) var(--fm-space-5);
    color: var(--fm-color-text);
    font-size: var(--fm-size-ui);
    max-width: var(--fm-measure-document);
  }
  .fm-error__title {
    color: var(--fm-color-danger);
    font-weight: var(--fm-weight-normal);
    font-size: var(--fm-size-7);
  }
  .fm-error__title::before {
    content: "[!] ";
    content: "[!] " / "";
  }
  .fm-error__detail {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-code);
    white-space: pre-wrap;
    margin-top: var(--fm-space-2);
  }

  /* -- live region ---------------------------------------------------------------------------
     Visually hidden but announced. Route changes, save state, async results. */
  .fm-live {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* -- footer ---------------------------------------------------------------------------------- */
  .fm-footer {
    margin-top: var(--fm-space-8);
  }
  .fm-footer__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fm-space-4);
    margin: var(--fm-space-2) 0;
  }
  .fm-footer__links a {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-ui);
    text-decoration: none;
  }
  .fm-footer__links a::before {
    content: "[";
    content: "[" / "";
  }
  .fm-footer__links a::after {
    content: "]";
    content: "]" / "";
  }
  .fm-footer__links a:hover {
    color: var(--fm-color-accent);
  }
  .fm-footer__copy {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-caption);
  }
  .fm-footer__copy::before {
    content: "// ";
    content: "// " / "";
  }

  /* -- theme toggle -----------------------------------------------------------------------------
     Three states in one native radio group. The inputs stay in the
     accessibility tree — visually hidden but focusable — so arrow-key
     movement, mutual exclusion and the "2 of 3" announcement all come from the
     platform instead of from script. Script's whole job is to check the right
     one, persist the choice, and reveal the control (it ships `hidden`, so a
     reader without JavaScript is not offered a switch that does nothing).   */
  .fm-theme {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--fm-space-2);
    border: none;
  }
  .fm-theme[hidden] {
    display: none;
  }
  .fm-theme__label {
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-meta);
  }
  .fm-theme__label::before {
    content: "// ";
    content: "// " / "";
  }
  .fm-theme__opt {
    display: inline-flex;
    align-items: center;
    min-height: var(--fm-measure-touch);
    padding-inline: var(--fm-space-1);
    color: var(--fm-color-text-dim);
    font-size: var(--fm-size-ui);
    cursor: pointer;
  }
  .fm-theme__opt > input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .fm-theme__opt > input + span::before {
    content: "[";
    content: "[" / "";
  }
  .fm-theme__opt > input + span::after {
    content: "]";
    content: "]" / "";
  }
  .fm-theme__opt:hover {
    color: var(--fm-color-text);
  }
  /* Accent *and* weight: in a monospace face bold costs no advance width, so
     the selected option gains a second, non-color channel without reflowing
     the row as the choice moves. */
  .fm-theme__opt > input:checked + span {
    color: var(--fm-color-accent);
    font-weight: var(--fm-weight-bold);
  }
  .fm-theme__opt > input:focus-visible + span {
    outline: var(--fm-border-hairline) solid var(--fm-color-accent);
    outline-offset: 2px;
  }
}

/* =============================================================================
   fm.utilities — deliberately minimal. If you need a fifth utility, you
   probably need a component.
   ========================================================================== */

@layer fm.utilities {
  .fm-stack > * + * {
    margin-block-start: var(--fm-stack-gap, var(--fm-space-4));
  }
  .fm-row {
    display: flex;
    align-items: center;
    gap: var(--fm-row-gap, var(--fm-inline-3));
    flex-wrap: wrap;
  }
  .fm-measure {
    max-width: var(--fm-measure-document);
  }
  .fm-measure--narrow {
    max-width: var(--fm-measure-narrow);
  }
  .fm-dim {
    color: var(--fm-color-text-dim);
  }
  .fm-bright {
    color: var(--fm-color-text-bright);
  }
  .fm-scroll-x {
    overflow-x: auto;
  }
  .fm-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* =============================================================================
   Standalone / installed PWA adjustments
   ========================================================================== */

@media (display-mode: standalone), (display-mode: fullscreen) {
  @layer fm.components {
    /* The OS chrome is gone; the masthead becomes the title bar and must clear
       the status bar / notch. */
    .fm-shell__masthead {
      padding-top: calc(var(--fm-space-3) + var(--fm-safe-top));
      -webkit-app-region: drag;
    }
    .fm-shell__masthead a,
    .fm-shell__masthead button {
      -webkit-app-region: no-drag;
    }
    /* No external-link affordances make sense inside an installed app frame. */
    .fm-page {
      min-height: 100dvh;
    }
  }
}

/* =============================================================================
   Print — always the chart-table palette, on real paper. Identical ink values
   to the light theme; only the two surfaces go white so a page doesn't print
   as a solid buff rectangle.
   ========================================================================== */

@media print {
  @layer fm.overrides {
    :root {
      color-scheme: light;
      --fm-color-bg: #ffffff;
      --fm-color-surface: #ffffff;
      --fm-color-text: #1e3a34;
      --fm-color-text-dim: #4a6a5c;
      --fm-color-text-bright: #0e1c23;
      --fm-color-accent: #0d6b3f;
      --fm-color-accent-dim: #3d8a63;
      --fm-color-link: #136b52;
      --fm-color-link-hover: #093f31;
      --fm-color-border: #d5cfbe;
      --fm-color-border-strong: #7e786a;
    }
    .fm-shell__rail,
    .fm-statusline,
    .fm-toasts,
    .fm-banner,
    .fm-skip,
    .fm-pagination {
      display: none;
    }
    .fm-prose :where(a)::after {
      content: " (" attr(href) ")";
      font-size: var(--fm-size-1);
      color: var(--fm-color-text-dim);
    }
  }
}
