/*
 * code.css
 * Inline code, code blocks, <kbd>, and the syntax highlighting theme.
 *
 * ── Why the theme has no classes ──────────────────────────────────
 *
 * Highlighted markup comes from `glow()` in @frontierjs/utils, which marks
 * each token with the element that already means it — <em> for a string,
 * <sup> for a comment, <b> for an identifier — and wraps the block in
 * `<code language="css">`. So the whole theme keys off elements and one
 * attribute, and ships no class at all: nothing to add to vocabulary.js,
 * nothing for a consumer to remember, and any highlighter that emits the
 * same shape is themed by this file for free.
 *
 * The colors are the package's own tones, read through a `--code-*`
 * override with the tone as the fallback rather than aliased at :root. An
 * alias would resolve once against :root and inherit past every .theme-*
 * (ruled 2026-08-02, the same trap as --ring), so highlighted code would
 * stay in the default palette in every other theme. Written this way it
 * retints with the theme, and a theme that wants its own code palette sets
 * the --code-* variables directly.
 *
 * ── Why the tones are clamped before they are used ────────────────
 *
 * A tone is tuned as a FILL behind white text. As text on a surface it is
 * a different job and mostly fails it: measured across the eight shipped
 * themes, the raw tones came in as low as 1.65:1, and only one theme had
 * all six roles above AA. So each one is passed through the tone-as-text
 * lightness window — hue and chroma untouched, lightness clamped into
 * --tone-l-min/max. tones.css owns that decision and tokens.css holds the
 * bounds; a dark theme that forgets to invert them renders dark code on a
 * dark block, which is why every shipped theme is pinned by `code: every
 * token clears AA in theme-*`.
 *
 * The clamp is written out per role here rather than read from
 * --tone-ink, because --tone-ink derives from --bg-mix — one tone per
 * element — and this palette needs six roles at once off six different
 * theme colors, one of them hue-shifted.
 */

/* One tone → one readable text color. Written out per role rather than
   shared, because a custom property cannot take the tone as an argument. */

/*
 * ── Kbd — a key the user is meant to press ────────────────────────
 *
 *   Press <kbd>⌘</kbd><kbd>K</kbd> to search
 *   <kbd class="kbd">Esc</kbd>
 *
 * Styled on the element as well as the class, because <kbd> already means
 * exactly this and the class exists only for markup that cannot use it.
 *
 * The heavier bottom border is the whole "keycap" effect — a box-shadow
 * would be more literal but would not survive a dark theme, where the
 * shadow disappears and the cap flattens.
 */
kbd, .kbd {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;

  min-inline-size: 1.5em;
  padding:         0.125em 0.375em;

  font-family: var(--font-mono);
  /* em, so a kbd inside small print shrinks with it. */
  font-size:   0.8125em;
  font-weight: 500;
  line-height: 1.4;
  color:       var(--ink);

  background:              var(--surface-sunken);
  border:                  var(--border-width) solid var(--rule-strong);
  /* The cap is the doubled edge, not a literal 2px — a heavy theme thickens
     the box and would otherwise flatten the key it is drawing. */
  border-block-end-width:  calc(var(--border-width) * 2);
  border-radius:           4px;
}

/*
 * ── Code ──────────────────────────────────────────────────────────
 *
 *   an inline <code>identifier</code>
 *
 *   <pre class="code"><code>bun run test</code></pre>
 *
 * <pre> is what makes a code block preserve whitespace; the class only
 * dresses it. The nested <code> is not optional decoration either — it is
 * what says "this is code" rather than "this is preformatted text", and
 * screen readers and search engines both use the distinction.
 *
 * A code block must scroll in its own box. Without `overflow-x`, one long
 * line pushes its grid track wider than the viewport and takes the whole
 * page sideways with it — the same failure `.table-wrap` exists to
 * prevent, and the reason `.screen` sets min-inline-size: 0.
 */
code, .code-inline {
  font-family: var(--font-mono);
  font-size:   0.875em;
  padding:     0.125em 0.375em;
  border-radius: 4px;
  background:  color-mix(in srgb, var(--ink) 6%, transparent);
  color:       var(--ink);
}

.code {
  display:    block;
  overflow-x: auto;
  margin:     0;
  padding:    var(--space-xl) var(--space-2xl);

  /*
   * --code-bg / --code-text with the surface pair as the fallback, for the
   * same reason the syntax colors below are written that way: an alias at
   * :root resolves once and inherits past every .theme-*, so a themed block
   * would keep the default palette. Read here, a theme or a consumer that
   * wants its own code palette sets the two variables and nothing else.
   */
  background:    var(--code-bg, var(--surface-sunken));
  border:        var(--border-width) solid var(--rule);
  border-radius: var(--card-radius);

  font-family: var(--font-mono);
  font-size:   var(--text-sm);
  line-height: var(--leading-relaxed);
  color:       var(--code-text, var(--ink));

  /* Long lines scroll; they do not silently wrap and renumber themselves. */
  white-space: pre;
  tab-size:    2;

  /*
   * The block's own horizontal padding, republished as a variable so a
   * full-width child — a diff stripe below — can bleed back out to the
   * edge without this file's padding being restated there. It has to track
   * the padding-inline above; a literal drifts from it silently and the
   * stripes stop reaching the edge.
   */
  --code-pad: var(--space-2xl);
}

/*
 * A <code> inside a <pre> is a code block, not inline code, and must drop
 * the inline treatment entirely.
 *
 * Keyed on the element, not on `.code`. `code` is styled by element above,
 * so `<pre><code>` — the markup every markdown renderer emits and every
 * highlighter expects — was picking up the inline background whenever the
 * <pre> carried some other class. An inline box that wraps paints one
 * fragment per line, so the result is a code block with a slightly darker
 * stripe behind each line of text, and it looks like a rendering fault
 * rather than a stylesheet decision.
 */
pre code,
pre .code-inline,
.code > code,
.code .code-inline {
  padding:       0;
  background:    none;
  border-radius: 0;
  font-size:     inherit;
}

/* ── The highlighted block ────────────────────────────────────────── */

/*
 * `<code language="…">` stands on its own: it is a block, it preserves
 * whitespace and it scrolls, so it does not need a <pre> around it to be
 * readable. Wrapping it in one is still the right markup and costs nothing
 * — the rule below hands the type back to the container in that case.
 */
code[language] {
  display:     block;
  white-space: pre;
  overflow-x:  auto;
  tab-size:    2;

  padding:       0;
  background:    none;
  border-radius: 0;

  font-family: var(--font-mono);
  font-size:   var(--text-sm);
  line-height: var(--leading-relaxed);
  color:       var(--code-ink, var(--ink));
}

/* Nested, the container owns the type — otherwise a code block inside
   small print would jump back up to the default rung. */
pre code[language] {
  font-size:   inherit;
  line-height: inherit;
}

/*
 * Every token element is neutralized first. <b>, <em>, <strong>, <i>,
 * <sup>, <u>, <ins>, <del> and <dfn> all carry a default weight, slant,
 * decoration or baseline shift, and glow uses them as color carriers
 * rather than for their usual emphasis — so italic strings, superscript
 * comments and struck-through diff lines are what you get without this.
 */
code[language] :is(b, i, em, strong, sup, label, u, ins, del, dfn, mark, span) {
  font-weight:     inherit;
  font-style:      normal;
  font-size:       inherit;
  text-decoration: none;
  vertical-align:  baseline;
  color:           inherit;
  background:      none;
}

/* ── The palette ──────────────────────────────────────────────────── */

/* comment — the softest thing in the block, it is not the code */
code[language] sup    { color: var(--code-comment, var(--ink-mute)); }

/* punctuation and every other lone character: quieter than an identifier,
   but a step stronger than a comment. Level with it, braces and semicolons
   read as prose and the eye stops finding the structure. */
code[language] i      { color: var(--code-punct, var(--ink-soft)); }

/* identifier — variable, property, function, key */
code[language] b {
  color: var(--code-name,
    oklch(from var(--color-primary) clamp(var(--tone-l-min), l, var(--tone-l-max)) c h));
}

/* value — string, number, CSS custom property */
code[language] em {
  color: var(--code-value,
    oklch(from var(--color-success) clamp(var(--tone-l-min), l, var(--tone-l-max)) c h));
}

/*
 * keyword, tag name, hex color.
 *
 * A hue step off the theme's primary rather than a sixth literal: the
 * package has no violet tone, and every palette that does have one puts it
 * about here. Derived, so it follows a retheme like the other five.
 */
code[language] strong {
  color: var(--code-keyword,
    oklch(from var(--color-primary)
      clamp(var(--tone-l-min), l, var(--tone-l-max)) c calc(h + 75)));
}

/* @rule, decorator, !important — the things that are neither */
code[language] label {
  color: var(--code-special,
    oklch(from var(--color-danger) clamp(var(--tone-l-min), l, var(--tone-l-max)) c h));
  font-weight: 600;
}

/* ── Marks the author put there on purpose ────────────────────────── */

/* •text• in the source — draw the eye to one run inside a line */
code[language] mark {
  background:    var(--code-marked, color-mix(in srgb, var(--color-warning) 25%, transparent));
  border-radius: 3px;
  /* Negative margin against equal padding, so the highlight has breathing
     room without pushing the monospace grid out of alignment. */
  margin:  -0.15em -0.3em;
  padding: 0.15em 0.3em;
}

/* ••text•• — an error, underlined the way a spell-checker does */
code[language] u {
  text-decoration:            underline wavy var(--code-error, var(--color-danger));
  text-decoration-thickness:  0.1em;
  text-underline-offset:      0.25em;
}

/*
 * Whole-line callouts: `+ ` inserted, `- ` removed, `> ` noted.
 *
 * `inline-size: max-content` with `min-inline-size: 100%` is what makes the
 * stripe reach the end of the LONGEST line rather than stopping at the
 * visible edge — a block child of a scrolling box is only as wide as the
 * box, so without it a highlighted line loses its background the moment you
 * scroll right. The negative margin bleeds it back out over the container's
 * own padding, which the container publishes as --code-pad.
 */
code[language] :is(ins, del, dfn) {
  display:          block;
  inline-size:      max-content;
  min-inline-size:  100%;
  margin-inline:    calc(var(--code-pad, 0px) * -1);
  padding-inline:   var(--code-pad, 0px);
  border-inline-start: 2px solid var(--code-line-rule);
  background:       var(--code-line-bg);
}

code[language] ins {
  --code-line-rule: var(--code-ins, var(--color-success));
  --code-line-bg:   color-mix(in srgb, var(--code-ins, var(--color-success)) 14%, transparent);
}
code[language] del {
  --code-line-rule: var(--code-del, var(--color-danger));
  --code-line-bg:   color-mix(in srgb, var(--code-del, var(--color-danger)) 14%, transparent);
}
code[language] dfn {
  --code-line-rule: var(--code-note, var(--color-primary));
  --code-line-bg:   color-mix(in srgb, var(--code-note, var(--color-primary)) 14%, transparent);
}

/* ── Line numbers ─────────────────────────────────────────────────── */

/*
 * glow's `numbered` option wraps each line in a <span>; the numbers are a
 * counter rather than content, so selecting the block and copying it gives
 * you the code without them.
 */
code[language]:has(> span) { counter-reset: code-line 0; }

code[language] > span { counter-increment: code-line 1; }

code[language] > span::before {
  content:       counter(code-line);
  display:       inline-block;
  inline-size:   2.5em;
  margin-inline-end: 1em;
  text-align:    end;
  color:         var(--code-gutter, var(--ink-mute));
  /* Not part of the code — a drag-select must not pick it up. */
  user-select:   none;
}
