/* @canonical/styles-typography — Cap-unit engine (default)
 *
 * No JS font extraction required. Uses the CSS `cap` unit and `mod()` for
 * baseline alignment.
 *
 * Consumer contract:
 *   :root { --baseline-height: <length>; }
 *     (default 0.25rem with tokens.css loaded; required without it)
 *   element { --font-size: <length>; --line-height: <length>; }
 *   (fallback: --line-height-multiplier: <number> if --line-height is unset)
 *
 * Browser floor: Chrome 125, Safari 17.2, Firefox 118 — a different feature
 * binds each column, and this engine uses two:
 *
 *   mod()            Chrome 125, Safari 15.4, Firefox 118
 *   cap unit         Chrome 118, Safari 17.2, Firefox 97
 *
 * Chrome and Firefox are held by `mod()`, Safari by the `cap` unit.
 *
 * The grid unit:
 *   --baseline-height may be a length in any unit — 0.5rem and 8px are both
 *   fine, and rem is the usual choice because the grid then follows the reader's
 *   own font size. This file reads it bare, without a fallback of its own: the
 *   default lives once, in tokens.css's `ds.tokens` block, as
 *   `:where(:root) { --baseline-height: 0.25rem }`. Zero weight, so any real
 *   declaration wins — @canonical/styles declares it at :root in spacing.css,
 *   and an application may declare it on any element.
 *
 *   Linked on its own this file has no default. No engine here imports
 *   anything at all — the composition lives in index.css — so a
 *   stylesheet taking one engine alone either declares --baseline-height itself
 *   or imports `./tokens.css` beside it. Without one of those the nudges here
 *   resolve to nothing and the engine does not run.
 *
 * Layers:
 *   Every rule below sits in `ds.typography`. Unlayered, these rules outranked
 *   every layered rule in the page whatever the selectors on either side, and
 *   tied with an application's own `p` rule on specificity so that load order
 *   decided the winner one property at a time. In a layer the design system
 *   loses to the application's own layered CSS, deliberately, and beats the
 *   layers below it, also deliberately.
 *
 *   The rules select elements by name, so they apply to the whole document,
 *   which is what a design system's typography is for.
 */

@layer ds.typography {
  /* Reset */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  .p,
  .code {
    margin-block: 0;
  }

  /* Baseline alignment — cap-unit engine
   *
   * Each text element's total height snaps to a multiple of --baseline-height.
   * The engine achieves this by adding padding nudges inside the element box:
   *
   *   padding-block-start = start nudge (pushes the first baseline onto the grid)
   *   padding-block-end   = end nudge   (fills the remainder so total block size
   *                                      is a whole number of baseline units)
   *
   * Both nudges are padding (not margin) so the element's border-box block size
   * participates correctly in flex and grid layouts. margin-block-end is reserved
   * solely for --space-after (editorial element-owned spacing, default 0).
   */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  .p,
  .code {
    --computed-line-height: var(
      --line-height,
      calc(var(--baseline-height) * var(--line-height-multiplier))
    );
    line-height: var(--computed-line-height);
    font-size: var(--font-size);

    --baseline-position: calc((var(--computed-line-height) + 1cap) / 2);
    --start-nudge: calc(
      var(--baseline-height) -
      mod(var(--baseline-position), var(--baseline-height))
    );
    --end-nudge: calc(var(--baseline-height) - var(--start-nudge));
  }

  /* Block-level baseline nudges.
   *
   * The padding-block nudges snap a BLOCK element's box onto the baseline grid.
   * They must not apply to inline `.code` (e.g. InlineCode's <code> or
   * KeyboardKey's <kbd>) nested in a paragraph — there the parent line already
   * owns the baseline, and adding the nudge double-pads the top, breaking
   * alignment. So bare `.code` is inline-only (typography, no nudges); combine
   * `.p.code` for a standalone monospace code *block* that needs the nudges.
   */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  .p {
    /* Nudges as padding — inside the box for flex/grid compatibility */
    padding-block-start: var(--start-nudge);
    padding-block-end: var(--end-nudge);

    /* Element-owned spacing — only active in .editorial contexts */
    margin-block-end: calc(var(--space-after, 0) * var(--baseline-height));
  }
}
