/* mdbook-listings — callout overlay styles.
 *
 * The HTML splicer strips `// CALLOUT: <label> <body>` lines from the
 * rendered listing and emits a sibling `<div class="callout-overlay">`
 * containing one `<div class="callout-entry">` per marker. Each entry
 * carries `--callout-line` (post-strip 1-based line) and
 * `--callout-listing-lines` (the pre's total line count).
 *
 * The em-based formula was load-bearing on a fragile assumption:
 * that the overlay's per-line height (1.5em at 0.875em font) matches
 * the rendered pre's per-line height. mdbook's pre uses
 * `line-height: normal` (~1.13 for monospace), so the assumed 21px
 * was off from the true ~18px by 3px per line. For a 600-line diff
 * the cumulative drift pulled badges 1800px above their intended
 * line — landing inside whatever sibling pre happened to sit there.
 *
 * Fix: an inline `<script>` emitted after each overlay measures the
 * previous pre's rendered height, divides by listing-lines, and sets
 * `--callout-line-px` on the overlay. The fallback `1.5em` keeps
 * pre-script behaviour for any environment where JS doesn't run.
 *
 * Sentinel string used by unit tests to confirm the bundled bytes are
 * the expected build-time asset: mdbook-listings-css-v7
 */

/* Listing caption line, emitted by the numbering pass immediately before a
 * listing's code block when numbering is on and/or the directive carries a
 * caption. Renders as "Listing N.M — caption" above the <pre>. */
.listing-caption {
  margin: 1em 0 -0.5em;
  font-size: 0.9em;
  font-weight: 600;
  color: var(--fg, #333);
}

.callout-overlay {
  position: relative;
  height: 0;
  margin-top: 0;
  pointer-events: none;
  z-index: 2;
}

.callout-entry {
  position: absolute;
  right: 0;
  top: calc(
    (var(--callout-line, 1) - 1 - var(--callout-listing-lines, 0))
      * var(--callout-line-px, 1.5em)
      - calc(var(--callout-line-px, 1.5em) / 2)
  );
  height: var(--callout-line-px, 1.5em);
  pointer-events: none;
}

.callout-badge {
  position: absolute;
  right: 0;
  top: 0;
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Pill: a stadium border-radius larger than any half-height keeps the
   * shape consistent whether the badge holds a bare ordinal (`1`) or a
   * listing-scoped one (`5.3.1`) — wider, but the same rounded shape. The
   * badge grows horizontally with its text and never wraps. */
  min-width: 1.5em;
  height: 1.5em;
  padding: 0 0.5em;
  border: 1px solid currentColor;
  border-radius: 999px;
  white-space: nowrap;
  background: var(--bg, #fff);
  color: var(--fg, #333);
  font-family: var(--mono-font, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.85em;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
}

.callout-badge:hover,
.callout-badge:focus-visible {
  background: var(--fg, #333);
  color: var(--bg, #fff);
  outline: none;
}

/* Visually distinguish bare anchor badges from interactive popover badges.
 * Scoped to .callout-entry (listing-side overlay) — cross-ref <a> badges
 * in chapter prose are often the only ELEMENT child of their <p> parent
 * (text nodes don't count for `:only-child`), so an unscoped selector
 * would accidentally mute every inline cross-ref. */
.callout-entry .callout-badge:only-child {
  cursor: default;               /* No pointer finger on hover */
  background: transparent;       /* Hollow look */
  border: 1px dashed currentColor;
  opacity: 0.6;                  /* Slightly muted */
}

/* Bare badges shouldn't invert on hover since they aren't interactive */
.callout-entry .callout-badge:only-child:hover,
.callout-entry .callout-badge:only-child:focus-visible {
  background: transparent;
  color: var(--fg, #333);
}

/* Body box: absolutely positioned to the RIGHT of the badge (in the
 * un-annotated gutter beyond the listing's right edge), vertically
 * centred on the entry (and therefore on the badge). Opening to the
 * right is ch.6 slice 3 — opening to the left covered the very code
 * line the body annotates, which defeated the inline-callout point. */
.callout-body {
  position: absolute;
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
  width: max-content;
  /* Border-box so `max-width` constrains the popover's actual visible
   * extent (including padding + border) — not just its inner content
   * area. With default content-box, a `max-width: 28em` popover is
   * visually ~22px wider than the JS clamp expects, and the right
   * edge ends up under the scroll container's scrollbar. */
  box-sizing: border-box;
  /* JS clamps `max-width` inline when the right gutter is narrower
   * than 28em; default kicks in only when there's room for the
   * full-width popover. */
  max-width: 28em;
  padding: 0.5em 0.75em;
  border: 1px solid var(--theme-popup-border, #ccc);
  border-radius: 0.25em;
  background: var(--theme-popup-bg, #fff);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  font-size: 0.9em;
  pointer-events: auto;
  white-space: normal;

  /* Base state (transition OUT): Box is clipped to the LEFT edge (arrow
   * is still visible due to negative left inset, but box is 0-width),
   * text is transparent, and visibility is hidden. */
  visibility: hidden;
  clip-path: inset(-2em 100% -2em -2em);
  color: transparent;

  /* Transition OUT choreography:
   * 1. color fades out instantly
   * 2. clip-path retracts box rightward (waits 0.15s for text to fade)
   * 3. visibility becomes hidden (waits 0.35s for both to finish) */
  transition: color 0.15s ease-out, clip-path 0.2s ease-out 0.15s, visibility 0s 0.35s;
}

/* Left-pointing triangle arrow on the body's left edge, pointing back
 * at the badge. ::after draws the border colour; ::before fills it
 * with the body background so the arrow looks outlined rather than
 * solid. */
.callout-body::after {
  content: '';
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border-top: 0.35em solid transparent;
  border-bottom: 0.35em solid transparent;
  border-right: 0.5em solid var(--theme-popup-border, #ccc);
}

.callout-body::before {
  content: '';
  position: absolute;
  right: calc(100% - 1px);
  top: 50%;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border-top: calc(0.35em - 1px) solid transparent;
  border-bottom: calc(0.35em - 1px) solid transparent;
  border-right: calc(0.5em - 1px) solid var(--theme-popup-bg, #fff);
  z-index: 1;
}

/* Hover/focus state (transition IN): Adjacent-sibling selector works because
 * button precedes div in DOM order inside .callout-entry. */
.callout-entry .callout-badge:hover + .callout-body,
.callout-entry .callout-badge:focus-visible + .callout-body {
  visibility: visible;
  clip-path: inset(-2em -2em -2em -2em);
  color: var(--fg, #333);

  /* Transition IN choreography:
   * 1. visibility becomes visible instantly
   * 2. clip-path expands box (draws line instantly)
   * 3. color fades in (waits 0.2s for box to draw) */
  transition: visibility 0s, clip-path 0.2s ease-out, color 0.15s ease-out 0.2s;
}

/* mdbook's book.js runs highlight.js across every `<code>` on the
 * page at load, adding `.hljs` to the outer `<code>` and wrapping
 * the inner text in `<span class="hljs-…">` elements that hold
 * their own theme colours. Those colours override `color: inherit`
 * and don't pick up the parent's transition, so they "appear
 * instantly / vanish last" while the rest of the body fades. Match
 * the body's color animation on every hljs element so the popover
 * fades uniformly. */
.callout-body [class*="hljs"] {
  color: transparent;
  transition: color 0.15s ease-out;
}
.callout-entry .callout-badge:hover + .callout-body [class*="hljs"],
.callout-entry .callout-badge:focus-visible + .callout-body [class*="hljs"] {
  color: var(--fg, #333);
  transition: color 0.15s ease-out 0.2s;
}

/* Cross-reference badge in chapter prose (slice 5). Same circle shape
 * as the listing-side badge but static-positioned inline with text. */
.callout-badge.callout-ref {
  position: static;
  margin: 0 0.15em;
}

/* Narrow-viewport fallback (ch.6 slice 3, JS-applied class): the JS
 * adds `callout-entry--left-popover` when the right-side gutter is
 * smaller than the threshold. Revert the right-opening defaults so
 * the popover opens LEFT (over the listing) — accepted as the lesser
 * evil vs. a popover that spills off the viewport entirely. */
.callout-entry--left-popover .callout-body {
  left: auto;
  right: 2em;
  clip-path: inset(-2em -2em -2em 100%);
}
.callout-entry--left-popover .callout-badge:hover + .callout-body,
.callout-entry--left-popover .callout-badge:focus-visible + .callout-body {
  clip-path: inset(-2em -2em -2em -2em);
}
.callout-entry--left-popover .callout-body::after {
  right: auto;
  left: 100%;
  border-right: 0;
  border-left: 0.5em solid var(--theme-popup-border, #ccc);
}
.callout-entry--left-popover .callout-body::before {
  right: auto;
  left: calc(100% - 1px);
  border-right: 0;
  border-left: calc(0.5em - 1px) solid var(--theme-popup-bg, #fff);
}
