/* Brand Kit §4 - Components. Every value here is traceable to Pulse Brand Kit.md. */

/* Section eyebrow: [sparkle] [gap 9px] EYEBROW TEXT */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 9px;
}
.eyebrow__label {
  font: 400 11px/1 var(--font-mono);
  letter-spacing: 0.2em;
  color: var(--ink-eyebrow);
}
.eyebrow__label--on-charcoal { color: var(--purple-300); }

/* Metadata label: mono, uppercase, 0.14em, 10px. Separator " · ". */
.metadata {
  font: 400 10px/1.6 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink-muted);
}
.metadata--on-charcoal { color: var(--ink-on-charcoal-55); }

/* Hairlines and dividers */
.rule-top { border-top: 1px solid var(--rule-bone-16); }
.rule-top--list { border-top: 1px solid var(--rule-bone-18); }
.rule-top--charcoal { border-top: 1px solid var(--rule-charcoal-14); }
.rule-bottom--table-head { border-bottom: 1px solid var(--rule-bone-30); }
.rule-bottom--table-row { border-bottom: 1px solid var(--rule-bone-14); }
.rule-bottom--header { border-bottom: 1px solid var(--rule-bone-12); }
.rule-bottom--charcoal { border-bottom: 1px solid var(--rule-charcoal-14); }

blockquote.pull-quote {
  margin: 0;
  padding: 4px 0 4px 24px;
  border-left: 2px solid var(--amber-deep);
}

/* Status flag, not a badge: the site marks state with mono metadata and a
   rule rather than an outlined container. */
.not-built-pill {
  align-self: flex-start;
  font: 400 10px/1 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink-signal);
  border-left: 2px solid var(--signal);
  padding: 3px 0 3px 12px;
}

/* Buttons and links (Brand Kit §4) */
.btn-primary {
  display: inline-flex;
  align-items: center;
  font: 400 13px/1 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--charcoal);
  background: var(--amber);
  padding: 15px 22px;
  border: 0;
  cursor: pointer;
  transition: transform var(--dur-hover) var(--ease);
}
.btn-primary:hover { transform: translateY(-3px); }

.btn-secondary {
  display: inline-flex;
  align-items: center;
  font: 400 13px/1 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-on-charcoal);
  background: transparent;
  border: 1px solid rgba(var(--bone-rgb), 0.4);
  padding: 15px 22px;
  cursor: pointer;
  transition: transform var(--dur-hover) var(--ease), border-color var(--dur-hover) var(--ease);
}
.btn-secondary:hover { border-color: var(--amber); transform: translateY(-3px); }

.btn-form-submit {
  align-self: flex-start;
  font: 400 12px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: transparent;
  color: var(--ink-primary);
  border: 1px solid var(--rule-bone-30);
  padding: 15px 22px;
  cursor: pointer;
  transition: color var(--dur-hover) var(--ease), border-color var(--dur-hover) var(--ease);
}
.btn-form-submit:hover { color: var(--purple-500); border-color: var(--purple-500); }

.link-action {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  align-self: flex-start;
  text-decoration: none;
  font: 400 15px/1.4 var(--font-sans);
  color: var(--ink-primary);
  border-bottom: 1px solid var(--rule-bone-30);
  padding-bottom: 5px;
  transition: color var(--dur-hover) var(--ease), border-color var(--dur-hover) var(--ease);
}
.link-action:hover { color: var(--purple-500); border-bottom-color: var(--purple-500); }
.link-action__arrow {
  display: inline-block;
  transition: transform var(--dur-hover) var(--ease);
}
.link-action:hover .link-action__arrow { transform: translateX(4px); }

.link-standalone {
  color: var(--ink-primary);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-bone-30);
  transition: color var(--dur-hover) var(--ease), border-color var(--dur-hover) var(--ease);
}
.link-standalone:hover { color: var(--purple-500); border-bottom-color: var(--purple-500); }

.link-on-dark {
  color: var(--ink-on-charcoal);
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease);
}
.link-on-dark:hover { color: var(--amber); }
.link-on-dark--underline {
  border-bottom: 1px solid rgba(var(--bone-rgb), 0.3);
  align-self: flex-start;
}
.link-on-dark--underline:hover { border-bottom-color: var(--amber); }

.link-ghost {
  font: 400 12px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-body);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-bone-30);
  padding-bottom: 4px;
  background: none;
  cursor: pointer;
  transition: color var(--dur-hover) var(--ease), border-color var(--dur-hover) var(--ease);
}
.link-ghost:hover { color: var(--ink-primary); }

/* Nav item */
.nav-link {
  font: 400 13px/1 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 9px 12px;
  border-bottom: 2px solid transparent;
  color: var(--ink-body);
  transition: color var(--dur-nav) var(--ease), border-color var(--dur-nav) var(--ease);
}
.nav-link:hover { color: var(--ink-primary); border-bottom-color: rgba(var(--charcoal-rgb), 0.35); }
.nav-link[aria-current="page"] { color: var(--ink-primary); border-bottom-color: var(--amber-deep); }
@media (max-width: 640px) {
  /* Five items need tighter padding to fit a phone-width header on one line. */
  .nav-link { padding: 9px 5px; }
}

/* Filter chip: state is an 8px LED marker + ink weight, no border/fill */
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: none;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
  font: 400 14px/1.2 var(--font-sans);
  color: rgba(var(--bone-rgb), 0.6);
  transition: color var(--dur-hover) var(--ease);
}
.filter-chip:hover { color: var(--ink-on-charcoal); }
.filter-chip[aria-pressed="true"] { color: var(--ink-on-charcoal); }
.filter-chip__dot {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 1px;
  flex: 0 0 auto;
  background: rgba(var(--amber-dim-rgb), 0.55);
  box-shadow: none;
  transition: background var(--dur-hover) var(--ease), box-shadow var(--dur-hover) var(--ease);
}
.filter-chip[aria-pressed="true"] .filter-chip__dot {
  background: var(--amber);
  box-shadow: 0 0 7px rgba(var(--amber-rgb), 0.5);
}

/* Elements toggled via the `hidden` attribute that also need a non-block
   display when shown. Author-origin `display` rules always beat the UA
   stylesheet's `[hidden]{display:none}` regardless of specificity (origin
   is checked before specificity in the cascade), inline styles included -
   so anything shown with flex/grid must restate `display:none` itself
   scoped to [hidden], or the element never actually hides. */
.lightbox { display: flex; flex-direction: column; }
.lightbox[hidden] { display: none; }
.pipeline-panel { display: flex; flex-direction: column; gap: 10px; }
.pipeline-panel[hidden] { display: none; }
.contact-success { display: flex; flex-direction: column; gap: 12px; padding: 22px 0; }
.contact-success[hidden] { display: none; }
.contact-form { display: flex; flex-direction: column; gap: 18px; }
.contact-form[hidden] { display: none; }
.media-group { display: flex; }
.media-group[hidden] { display: none; }

/* Media grid: every tile the same size regardless of source aspect ratio,
   so captions across a row start at the same y-position. */
.media-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--card-grid-gap);
}
/* Caps a tile's width so a sparse row (one or two items) can't stretch a
   thumbnail past the size it would be in a full row. */
.media-item {
  max-width: 360px;
}
.media-thumb {
  display: flex;
  width: 100%;
  aspect-ratio: 16 / 10;
  border: 1px solid var(--rule-bone-16);
  position: relative;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  transition: transform var(--dur-hover) var(--ease);
}
.media-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Per-item override: set --thumb-pos inline when the interesting content isn't centred. */
  object-position: var(--thumb-pos, center);
  display: block;
}

/* Journal index thumbnails: same 16:10 .media-thumb crop, fixed widths.
   The featured entry keeps its larger, distinct treatment. */
.journal-featured__thumb {
  flex: 0 1 440px;
  min-width: 0;
}
.journal-entry__link {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr) auto;
  column-gap: 28px;
  row-gap: 12px;
  align-items: center;
  padding: 28px 0;
  text-decoration: none;
  color: var(--ink-primary);
}
@media (max-width: 640px) {
  .journal-entry__link { grid-template-columns: 1fr; }
  .journal-entry__link > .metadata { text-align: left !important; }
}
/* Posts with no image of their own: the dimmed motif on a charcoal plate,
   same low-intensity treatment as the team page. */
.media-thumb--motif { background: var(--charcoal); }
.media-thumb--motif .pixel-grid { opacity: 0.45; }

/* In-post image figures */
.article-body figure img { width: 100%; display: block; border: 1px solid var(--rule-bone-16); }

/* Lightbox chrome. Everything here used to be inline on the markup, which
   put it out of reach of the mobile overrides at the bottom of this block. */
.lightbox {
  position: fixed;
  inset: 0;
  /* dvh so a phone's collapsing address bar cannot leave the dialog taller
     than the space actually on screen. */
  height: 100dvh;
  z-index: 80;
  background: rgba(var(--charcoal-rgb), 0.94);
  animation: om-fade var(--dur-lightbox) var(--ease);
}
.lightbox__bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--rule-charcoal-18);
  flex: 0 0 auto;
}
.lightbox__close {
  font: 400 12px/1 var(--font-mono);
  letter-spacing: 0.14em;
  background: none;
  border: 0;
  padding: 8px 0;
  cursor: pointer;
}
.lightbox__backdrop {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 28px 24px;
  min-height: 0;
}
.lightbox-nav {
  font: 400 20px/1 var(--font-mono);
  background: none;
  border: 0;
  padding: 14px 10px;
  cursor: pointer;
  flex: 0 0 auto;
}
.lightbox__slot {
  font: 400 11px/1.7 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink-on-charcoal-62);
  text-align: center;
}
.lightbox__footer {
  padding: 0 24px 28px;
  display: flex;
  justify-content: center;
  flex: 0 0 auto;
}
.lightbox__caption {
  font: 400 14px/1.6 var(--font-mono);
  color: var(--ink-on-charcoal);
  text-align: center;
  max-width: 60ch;
}

/* Lightbox stage: fixed viewport-relative box; media scales to fit inside
   without cropping, regardless of its own aspect ratio. */
.lightbox-stage {
  width: min(90vw, 960px);
  height: min(85vh, 680px);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
}

@media (max-width: 899px) {
  /* On a phone the bar, a fixed 85vh stage and a full-length caption add up
     to more than the screen, and the centred stage overflowed off the top
     where it could not be scrolled back to. The stage becomes the flexible
     element instead, so it takes whatever is left and never exceeds it. */
  .lightbox-stage {
    width: 100%;
    height: auto;
    min-height: 0;
    min-width: 0;
    flex: 1 1 auto;
    align-self: stretch;
  }
  .lightbox__backdrop { gap: 4px; padding: 16px 12px; }
  /* 44px tap targets: the arrows were 32px wide, the close button 28 tall. */
  .lightbox-nav {
    min-width: 44px;
    min-height: 44px;
    padding: 10px;
  }
  .lightbox__close {
    padding: 15px 0;
    min-height: 44px;
  }
  /* No Esc key to press on a touch device. */
  .lightbox__close-key { display: none; }
  /* Long captions were eating ~200px of a 553px screen. */
  .lightbox__caption {
    font-size: 12px;
    max-height: 4.8em;
    overflow-y: auto;
  }
  .lightbox__footer { padding: 0 16px 16px; }
}
.lightbox-stage img,
.lightbox-stage video {
  max-width: 100%;
  max-height: 100%;
  display: block;
}
.lightbox-stage img[hidden],
.lightbox-stage video[hidden] {
  display: none;
}

/* Anchor targets. The header is sticky, so a jump to #roadmap otherwise
   lands the section's eyebrow and heading behind it - 58px of it on
   desktop and 113px on a phone, where the header wraps onto two rows. */
main section[id] { scroll-margin-top: 74px; }
@media (max-width: 899px) {
  main section[id] { scroll-margin-top: 126px; }
}

/* Page index ("on this page", project page). Desktop: a sticky sidebar
   rail beside the article. Below 900px the two columns can no longer sit
   side by side, and a sticky element in a wrapped flex line still sticks
   to its whole flex container, so it would float down the page on top of
   the article. Below that width it becomes a static, full-width band of
   wrapped links above the content instead: all eight sections stay one
   tap away, which a collapsed dropdown or a hidden nav would not give. */
.page-index {
  position: sticky;
  top: var(--sticky-offset);
  align-self: flex-start;
  flex: 0 1 200px;
  min-width: 180px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.page-index__label {
  padding-bottom: 8px;
  border-bottom: 1px solid var(--rule-bone-16);
}
.page-index__link {
  border-bottom: 0;
  padding-bottom: 0;
  font-size: 11px;
  letter-spacing: 0.1em;
}
@media (max-width: 899px) {
  .page-index {
    position: static;
    flex: 1 1 100%;
    width: 100%;
    min-width: 0;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 18px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--rule-bone-16);
  }
  .page-index__label {
    flex: 1 1 100%;
    padding-bottom: 6px;
    border-bottom: 0;
  }
  /* 44px tall tap targets in a row that still reads as one band. */
  .page-index__link {
    padding: 13px 0;
    line-height: 18px;
  }
}

/* Inference pipeline diagram (project page). No containers: each stage is a
   pixel-grid glyph on its charcoal plate plus a label, and the structure
   comes from the hairline rules between branches and the arrows between
   stages, the same way the rest of the site builds groupings. */
.pipeline-stage {
  display: flex;
  align-items: center;
  gap: 12px;
}
.pipeline-stage__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.pipeline-stage__num {
  font: 400 10px/1 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink-eyebrow);
}
.pipeline-stage__name {
  font: 500 15px/1.3 var(--font-sans);
  color: var(--ink-primary);
}
.pipeline-stage__state {
  font: 400 10px/1.5 var(--font-mono);
  letter-spacing: 0.1em;
  color: var(--ink-muted);
}
.pipeline-stage__state--live { color: var(--ink-eyebrow); }
/* Dimming the decorative glyph only: labels stay at full-opacity ink, since
   the palette forbids alpha-muted type. */
.pipeline-stage__glyph--planned { opacity: 0.45; }
.pipeline-arrow {
  font: 400 14px/1 var(--font-mono);
  color: var(--ink-eyebrow);
}
.pipeline-branch {
  font: 400 10px/1.5 var(--font-mono);
  letter-spacing: 0.1em;
  color: var(--ink-eyebrow);
  min-width: 132px;
}

/* Vertical curved roadmap timeline (project page). The SVG path is drawn
   at runtime from the real position of each .roadmap-timeline__node, so
   the CSS only has to place the nodes and let content flow around them;
   see js/roadmap-timeline.js for the curve and scroll-draw animation. */
.roadmap-timeline {
  position: relative;
}
.roadmap-timeline__svg {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;
}
.roadmap-timeline__path--done {
  fill: none;
  stroke: var(--purple-400);
  stroke-width: 2;
  stroke-linecap: round;
}
.roadmap-timeline__path--upcoming {
  fill: none;
  stroke: var(--ink-muted);
  stroke-width: 2;
  stroke-linecap: round;
  opacity: 0.4;
}
.roadmap-timeline__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.roadmap-timeline__row {
  display: grid;
  grid-template-columns: 1fr 56px 1fr;
  align-items: center;
  column-gap: 20px;
  min-height: 68px;
}
.roadmap-timeline__row[data-side="left"] .roadmap-timeline__content {
  grid-column: 1;
  text-align: right;
  align-items: flex-end;
}
.roadmap-timeline__row[data-side="right"] .roadmap-timeline__content {
  grid-column: 3;
  text-align: left;
  align-items: flex-start;
}
.roadmap-timeline__content {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 0;
}
.roadmap-timeline__node {
  grid-column: 2;
  width: 12px;
  height: 12px;
  border-radius: 999px;
  background: var(--ink-muted);
  opacity: 0.4;
  position: relative;
  z-index: 1;
}
.roadmap-timeline__row[data-side="left"] .roadmap-timeline__node { justify-self: start; }
.roadmap-timeline__row[data-side="right"] .roadmap-timeline__node { justify-self: end; }
.roadmap-timeline__row[data-status="done"] .roadmap-timeline__node {
  background: var(--purple-400);
  opacity: 1;
}
.roadmap-timeline__row[data-status="current"] .roadmap-timeline__node {
  width: 18px;
  height: 18px;
  background: var(--bone);
  border: 2px solid var(--purple-400);
  opacity: 1;
}
.roadmap-timeline__row[data-status="current"] .roadmap-timeline__node::after {
  content: "";
  position: absolute;
  inset: -9px;
  border-radius: 999px;
  border: 1px solid var(--purple-400);
  animation: roadmap-breathe var(--dur-breath) var(--ease) infinite;
}
@keyframes roadmap-breathe {
  0%, 100% { transform: scale(1); opacity: 0.55; }
  50% { transform: scale(1.4); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .roadmap-timeline__row[data-status="current"] .roadmap-timeline__node::after {
    animation: none;
    opacity: 0.55;
  }
}
@media (max-width: 640px) {
  .roadmap-timeline__row {
    grid-template-columns: 32px 1fr;
    column-gap: 16px;
  }
  .roadmap-timeline__row[data-side="left"] .roadmap-timeline__content,
  .roadmap-timeline__row[data-side="right"] .roadmap-timeline__content {
    grid-column: 2;
    text-align: left;
    align-items: flex-start;
  }
  .roadmap-timeline__row[data-side="left"] .roadmap-timeline__node,
  .roadmap-timeline__row[data-side="right"] .roadmap-timeline__node {
    grid-column: 1;
    justify-self: center;
  }
}

/* Hover utilities for elements whose hover effect can't live in an inline style */
.hover-shift:hover { transform: translateX(4px); }
.hover-lift:hover { transform: translateY(-3px); }

.lightbox-nav {
  color: rgba(var(--bone-rgb), 0.7);
  transition: color var(--dur-hover) var(--ease);
}
.lightbox-nav:hover { color: var(--amber); }

.social-link {
  color: var(--ink-muted);
  transition: color var(--dur-hover) var(--ease);
}
.social-link:hover { color: var(--purple-500); }

/* Touch targets. The type stays at its Brand Kit size (§2: metadata 10px,
   filter 14px); only the hit area grows, and only on touch-width screens.
   At their design sizes these are 10px and 25px tall, which is fine under
   a cursor and not under a fingertip. */
@media (max-width: 899px) {
  .social-link {
    display: inline-flex;
    align-items: center;
    padding: 11px 0;
  }
  .filter-chip { padding: 10px 0; }
  /* Footer page list: a stack of standalone nav links, not inline prose,
     so each one gets a full row to be tapped in. */
  .footer-nav .link-on-dark { padding: 6px 0; }
}

/* Stat block: recurring bordered content block used across page grids */
.stat-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
  border-top: 1px solid var(--rule-bone-16);
  padding-top: 20px;
  min-width: 0;
}

/* Status list inside a stat block (home page, "where it is now"). Each row
   is a mono state label over the item, so the state is always spelled out
   in words: the tick and the half-disc, and the darker ink on DONE, only
   reinforce it. Same label treatment as .pipeline-stage__state. */
.status-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.status-list li {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 0;
  border-top: 1px solid var(--rule-bone-12);
}
.status-list li:first-child { border-top: 0; padding-top: 2px; }
.status-list__state {
  font: 400 10px/1.5 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink-muted);
}
.status-list__state--done { color: var(--ink-eyebrow); }
.status-list__item {
  font: 400 16px/1.5 var(--font-sans);
  color: var(--ink-body);
}

/* LED pixel grid - charcoal plate rule: a grid never sits directly on bone */
.pixel-grid {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--charcoal);
  border-radius: 2px;
}

/* MDN header lockup */
.lockup {
  display: flex;
  align-items: center;
  gap: 14px;
  text-decoration: none;
  color: var(--ink-primary);
}
.lockup__divider {
  width: 1px;
  height: 28px;
  background: var(--rule-bone-18);
  display: block;
}
.lockup__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* The lockup is centered on the header row as a bounding box, but its
     visual weight sits higher: "Pulse" (19px/500) reads as the anchor,
     with the lighter 10px mono sub-line hanging below it. Nudge the
     stack up so the nav's single-line optical center lines up with
     "Pulse" itself rather than the two-line block's geometric middle. */
  transform: translateY(-2px);
}

.header-status-wrap {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  justify-content: center;
}
.header-status {
  text-transform: uppercase;
  white-space: nowrap;
}
@media (max-width: 899px) {
  /* !important: an inline display on this element would otherwise always
     win over this media query, regardless of viewport width. */
  .header-status-wrap { display: none !important; }
}
.lockup__title {
  font: 500 19px/1 var(--font-sans);
  letter-spacing: -0.02em;
}
.lockup__sub {
  font: 400 10px/1 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink-muted);
}

/* Image placeholder texture (Brand Kit §6) */
.placeholder-tile {
  background: repeating-linear-gradient(135deg, var(--bone-2) 0 8px, var(--placeholder-bone-alt) 8px 16px);
}
.placeholder-tile--dark {
  background: repeating-linear-gradient(135deg, var(--placeholder-charcoal) 0 10px, var(--placeholder-charcoal-alt) 10px 20px);
}

/* Article body: prose styling for markdown-rendered journal post content */
.article-body h2 { margin: 44px 0 16px; font: 500 clamp(24px,3vw,30px)/1.2 var(--font-sans); letter-spacing: -0.02em; }
.article-body h3 { margin: 36px 0 14px; font: 500 22px/1.3 var(--font-sans); letter-spacing: -0.015em; }
.article-body p { margin: 0 0 22px; font: 400 17px/1.7 var(--font-sans); color: var(--ink-body); }
.article-body > p:first-of-type { font: 400 19px/1.7 var(--font-sans); color: var(--ink-primary); }
.article-body ul, .article-body ol { margin: 0 0 26px; padding-left: 22px; display: flex; flex-direction: column; gap: 12px; }
.article-body li { font: 400 17px/1.7 var(--font-sans); color: var(--ink-body); }
.article-body strong { font-weight: 500; color: var(--ink-primary); }
.article-body code { font: 400 15px/1.4 var(--font-mono); background: var(--bone-2); padding: 2px 6px; }
.article-body pre { margin: 0; padding: 22px; background: var(--charcoal); color: var(--ink-on-charcoal); overflow-x: auto; font: 400 13px/1.75 var(--font-mono); }
.article-body pre code { background: none; padding: 0; font: inherit; }
.article-body figure { margin: 0 0 28px; padding: 0; }
.article-body figcaption { margin-top: 12px; font: 400 10px/1.6 var(--font-mono); letter-spacing: 0.14em; color: var(--ink-muted); }
/* See eleventy.config.js: every markdown table is wrapped in this so the
   min-width below scrolls within the block rather than widening the page.
   The two background pairs are the standard local/scroll shadow trick: the
   bone-coloured covers travel with the content, the dark edge shadows stay
   pinned, so a shadow shows only on a side that has more table off-screen.
   Without it a phone reader has no way to tell a column is hidden. */
.table-scroll {
  overflow-x: auto;
  margin: 0 0 26px;
  -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(to right, var(--bone) 30%, rgba(var(--bone-rgb), 0)) left center,
    linear-gradient(to left, var(--bone) 30%, rgba(var(--bone-rgb), 0)) right center,
    radial-gradient(farthest-side at 0 50%, rgba(var(--charcoal-rgb), 0.16), rgba(var(--charcoal-rgb), 0)) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(var(--charcoal-rgb), 0.16), rgba(var(--charcoal-rgb), 0)) right center;
  background-repeat: no-repeat;
  background-size: 40px 100%, 40px 100%, 14px 100%, 14px 100%;
  background-attachment: local, local, scroll, scroll;
}
.article-body table { width: 100%; border-collapse: collapse; min-width: 460px; }
.article-body th { text-align: left; font: 400 10px/1 var(--font-mono); letter-spacing: 0.14em; color: var(--ink-muted); padding: 0 16px 12px 0; border-bottom: 1px solid var(--rule-bone-30); }
.article-body td { padding: 14px 16px 14px 0; border-bottom: 1px solid var(--rule-bone-14); font: 400 15px/1.6 var(--font-sans); color: var(--ink-primary); }
.article-body td:not(:first-child) { font: 400 13px/1.6 var(--font-mono); color: var(--ink-muted); }

/* Footer attribution card */
.attribution-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: flex-start;
  text-decoration: none;
  padding: 22px;
  border: 1px solid var(--purple-400);
  border-radius: 3px;
  background: transparent;
  box-shadow: 0 0 28px rgba(var(--purple-400-rgb), 0.28), inset 0 0 22px rgba(var(--purple-400-rgb), 0.16);
  transition: box-shadow var(--dur-hover) var(--ease);
}
.attribution-card:hover {
  box-shadow: 0 0 40px rgba(var(--purple-400-rgb), 0.42), inset 0 0 30px rgba(var(--purple-400-rgb), 0.24);
}
.attribution-card__copy {
  font: 400 12px/1.7 var(--font-mono);
  letter-spacing: 0.08em;
  color: var(--purple-300);
}
