:root {
  --color-bg: #ffffff;
  --color-text: #1a1a1a;
  --color-accent: #3355ff;
  --callout-info: #3355ff;
  --callout-warning: #b45309;
  --callout-danger: #dc2626;
  --callout-success: #16a34a;
  --content-width: 42.5rem;
  --gap-sm: 0.5rem;
  --gap-md: 1.5rem;
  --gap-lg: 3rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #111214;
    --color-text: #eaeaea;
    --color-accent: #7c9aff;
    --callout-info: #7c9aff;
    --callout-warning: #f0a828;
    --callout-danger: #f16565;
    --callout-success: #4ade80;
  }
}

/**
 * The --color-*, --callout-*, --content-width, and --gap-sm/md/lg custom
 * properties used to be hand-written here directly. As of spec §10 item 12
 * (the `tokens` collection, Style tier), they're generated at build time
 * instead —
 * site/build.ts::writeTokensCss reads tokens/tokens.json from the content
 * repo (falling back to lib/tokens.ts's DEFAULT_TOKENS, which hold
 * exactly the values this block used to hardcode, if that file doesn't
 * exist), and prepends the resulting `:root { ... }` +
 * `@media (prefers-color-scheme: dark)` block to this exact file in
 * dist/ — this source file is never served as-is. See lib/tokens.ts's
 * own module docstring for why fonts stay here, hand-written, rather
 * than becoming tokens too: a font-family value isn't safe to let a
 * Designer point at an arbitrary string the way a validated color/length
 * is, and site/config.ts's FONTS array is the actual mechanism deciding
 * which font files get self-hosted at all.
 */
:root {
  --font-sans: 'IBM Plex Sans', system-ui, sans-serif;
  --font-heading: 'Space Grotesk', system-ui, sans-serif;
  /* docs.css's 3-column max-width (280px nav rail + 2.5rem gap +
     content-width + 2.5rem gap + 280px TOC rail) — hoisted here, not a
     docs.css-local value, once header.css needed the same "wide
     container" width too (the header should span as wide as the docs
     layout does, not stay pinned to the narrower single-column
     content-width). A fixed value, not itself a Style-tier token: it's
     structural (derived from the rail widths docs.css hardcodes), not
     something a Designer edits directly the way content-width is. */
  --wide-content-width: 1320px;
}

html {
  /* Reserves the scrollbar gutter on every page, whether or not that
     page's content is actually tall enough to scroll — without this, a
     shorter page (no scrollbar) is a few pixels wider than a taller one
     (scrollbar present), and the centered header (margin: 0 auto) visibly
     shifts by that amount when navigating between them. */
  scrollbar-gutter: stable;
}

html,
body {
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
}

body {
  /* Copy default, on body specifically, not html — html's font-size is
     what rem actually resolves against, so putting it there would have
     silently rescaled every rem-based measurement site-wide (spacing,
     rem-sized headings, the docs grid's column math) to an 18/16 ratio
     instead of just changing body text size, which is all this is meant
     to do. Confirmed live: that's exactly what happened on the first pass
     of this rule, caught by the docs grid's columns coming out at 765px
     instead of the intended 680px cap. */
  font-size: 18px;
  line-height: 26px;
  margin: 0;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-heading);
}

/* Every element browsers render bold by default, restyled to this site's
   deliberately subtler weight (600, not the standard 700) — one rule
   here rather than scattered per-component overrides, so a future
   bold-by-default element doesn't need rediscovering this. A
   per-deployment styling choice, not a Carapace default forced on every
   adopter: site/config.ts's FONTS only fetches weight 600 (not 700) for
   exactly this reason — an adopter who wants standard bold restores 700
   in both places. Without this, the browser would request 700 by
   default, not find it, and silently synthetic-embolden the 600 file
   instead of using it cleanly. */
h1,
h2,
h3,
h4,
h5,
h6,
strong,
b,
th,
.site-header a {
  font-weight: 600;
}

/* :focus-visible (not :focus) so mouse clicks don't get a visible ring,
   only keyboard/assistive-tech navigation does — the standard modern
   pattern, not a compromise. Every interactive element in this site is a
   plain <a>, so one rule covers all of them. */
a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Button atom — a real <button> or an <a> styled as one. Content-authored
   HTML only ever gets the two variant classes (lib/sanitize.ts's
   allowedClasses for <a> — link-mode only, no bare .button and no real
   <button> tag: an interactive control needs client-side state/JS to do
   anything, which static MCP-authored content has no way to attach — same
   reasoning that ruled out form controls entirely). .button itself is
   template-code-only (site-cta and similar). Box styles (size, shape, no
   color) are grouped across all three selectors so button-primary/
   button-secondary are each fully self-sufficient alone — a content author
   never needs to remember to also add the bare .button class. */
.button,
.button-primary,
.button-secondary {
  display: inline-block;
  padding: 0.45rem 0.9rem;
  border: 1px solid transparent;
  border-radius: 0.3rem;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.button-primary {
  background: var(--color-accent);
  color: var(--color-bg);
}

.button-primary:hover {
  /* Overrides the general a:hover color swap above — that pattern (text
     turns accent-colored) doesn't apply to a filled button, which already
     uses the accent color as its background. */
  opacity: 0.85;
  color: var(--color-bg);
}

.button-secondary {
  background: transparent;
  color: var(--color-accent);
  border-color: var(--color-accent);
}

.button-secondary:hover {
  background: var(--color-accent);
  color: var(--color-bg);
}

/* Breadcrumb — shared across post/docs/docs-reference pages (site/breadcrumb.ts)
   rather than a docs-only style, now that blog posts render one too. Muted on
   purpose (opacity, inherited link color, not --color-accent) — it's
   wayfinding chrome above the real h1, not a place to compete with it. */
.breadcrumb {
  font-size: 0.9rem;
  opacity: 0.7;
  margin-bottom: 0.5rem;
}

.breadcrumb a {
  color: inherit;
}

.breadcrumb-separator {
  margin: 0 0.4em;
}

/* Was duplicated identically in post.css and docs.css — consolidated here,
   same reasoning as breadcrumb/figure/aside above. */
img {
  max-width: 100%;
  height: auto;
}

/* carousel's whole point is a fixed-size item (e.g. a 1200px screenshot)
   that does NOT shrink to fit its container — the carousel scrolls instead
   of the image resizing. Higher specificity than the bare `img` rule above
   (same file, selector with more parts wins), so this correctly overrides
   it rather than needing !important or a cross-file specificity fight. */
carousel > img {
  max-width: none;
  width: auto;
}

/* Standard semantic HTML (lib/sanitize.ts allowlists these like any other
   plain tag — no describeComponents() entry, no props). Shared here rather
   than duplicated per content-type CSS (post.css/docs.css) the way
   blockquote/callout styling currently is — post and docs content should
   look the same, not risk one gaining figure/aside styling and the other
   quietly not. */
figure {
  margin: 1.5rem 0;
}

figcaption {
  margin-top: 0.5rem;
  font-size: 0.9rem;
  opacity: 0.7;
}

aside {
  margin: 1.5rem 0;
  padding: 0.75rem 1rem;
  border-left: 3px solid var(--color-text);
  opacity: 0.85;
  font-size: 0.95rem;
}

/* Layout primitives (lib/sanitize.ts reserves these tags; lib/components.ts
   describes them to the MCP) — real elements styled directly by tag name and
   attribute, no build-time transform. Structural props only (direction, gap,
   width) — color/background is deliberately never a per-instance attribute
   here, see lib/components.ts's own module comment for why. */

stack,
wrap,
carousel {
  display: flex;
  gap: var(--gap-md);
}

stack {
  flex-direction: column;
}

stack[direction='row'] {
  flex-direction: row;
}

wrap {
  flex-wrap: wrap;
}

card {
  display: block;
  padding: 1.25rem;
  border: 1px solid color-mix(in srgb, var(--color-text) 15%, transparent);
  border-radius: 0.5rem;
}

/* Native touch scrolling, no JS — overflow-x + scroll-snap is the entire
   mechanism. -webkit-overflow-scrolling: touch is Safari-only (harmless
   elsewhere) and keeps momentum scrolling smooth on iOS specifically. */
carousel {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  /* Room for a visible scrollbar (desktop) without it overlapping content. */
  padding-bottom: 0.5rem;
}

carousel > * {
  flex: 0 0 auto;
  scroll-snap-align: start;
}

[gap='sm'] {
  gap: var(--gap-sm);
}

[gap='lg'] {
  gap: var(--gap-lg);
}

/* column — only meaningful as a direct child of stack[direction="row"] or
   wrap, both flex containers; flex-basis + grow:0 gives it a real,
   deliberate width instead of participating in natural flex sizing. */
column {
  display: block;
  flex: 0 1 auto;
}

column[width='half'] {
  flex-basis: 50%;
}

column[width='third'] {
  flex-basis: 33.3333%;
}

column[width='two-thirds'] {
  flex-basis: 66.6667%;
}

column[width='quarter'] {
  flex-basis: 25%;
}

column[width='three-quarters'] {
  flex-basis: 75%;
}

column[width='two-fifths'] {
  flex-basis: 40%;
}

column[width='three-fifths'] {
  flex-basis: 60%;
}

/* section — a width preset, not a layout container in its own right (no
   display/flex changes here). standard/wide reuse the exact custom
   properties every other page's own width already comes from, so a
   section never introduces a width that doesn't already exist somewhere
   else on the site. */
section[width='standard'] {
  max-width: var(--content-width);
  margin: 0 auto;
}

section[width='wide'] {
  max-width: var(--wide-content-width);
  margin: 0 auto;
}

/* The standard "break out of a centered container to the viewport edge"
   trick: 100vw at 50% of the viewport, pulled back left by half its own
   width, cancels out whatever margin/padding the surrounding content
   column has, regardless of what that column's own width happens to be. */
section[width='full-bleed'] {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-bg);
  color: var(--color-text);
  padding: 0.75rem 1rem;
  z-index: 100;
}

.skip-link:focus-visible {
  left: 0.5rem;
  top: 0.5rem;
}

/* Screen-reader-only text — visible content stays visual-only (e.g. a
   placeholder), the accessible name still needs a real associated label. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
