/* ==========================================================================
   Workshop — global styles
   --------------------------------------------------------------------------
   Visual system implementing the design direction in
   docs/design-direction.md and the component contract in
   docs/component-inventory.md.

   Metaphor: maker's notebook. Cream paper, ink text, multi-color category
   accents. Light-first; dark mode opt-in via [data-theme="dark"] on <html>.
   ========================================================================== */

/* ==========================================================================
   1. Fonts (self-hosted)
   ========================================================================== */

/* General Sans — display + UI face. Fontshare Free License. */
@font-face {
  font-family: "General Sans";
  src: url("/fonts/general-sans/GeneralSans-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("/fonts/general-sans/GeneralSans-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("/fonts/general-sans/GeneralSans-Semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("/fonts/general-sans/GeneralSans-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* JetBrains Mono — code + diagram labels + meta lines. OFL. */
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono/JetBrainsMono-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   2. Tokens
   --------------------------------------------------------------------------
   Every visual choice expressed as a CSS custom property. Rewriting the
   theme is a matter of editing this block.
   ========================================================================== */

:root {
  /* Surface — cream paper aesthetic */
  --paper: #fbf7f0;
  --paper-deep: #f4eee2;
  --paper-edge: #e8dfcc;
  --graph: #d9d0bc;

  /* Text — warm near-black */
  --ink: #1a1814;
  --ink-muted: #5c564a;
  --ink-faint: #8c8576;

  /* Category accents */
  --cat-compute: #2e5bff;
  --cat-storage: #e5447d;
  --cat-ai: #2f8a3f;          /* slightly darkened from #3FA34D for AA contrast on cream */
  --cat-media: #d97706;       /* slightly darkened amber for AA contrast */
  --cat-connectivity: #7a4fd8;

  /* Soft tints — used for hover backgrounds, derived via color-mix */
  --cat-compute-soft: color-mix(in srgb, var(--cat-compute) 12%, transparent);
  --cat-storage-soft: color-mix(in srgb, var(--cat-storage) 12%, transparent);
  --cat-ai-soft: color-mix(in srgb, var(--cat-ai) 12%, transparent);
  --cat-media-soft: color-mix(in srgb, var(--cat-media) 12%, transparent);
  --cat-connectivity-soft: color-mix(in srgb, var(--cat-connectivity) 12%, transparent);

  /* Page-level "current" accent.
     Default to Storage (the homepage's house color). Pages and
     individual mega-menu columns override this via the .accent--*
     utility classes defined right after :root — NOT via inline
     style="..." attributes. Inline styles would require relaxing
     our strict CSP (style-src 'self' without 'unsafe-inline'),
     which we'd rather not do. */
  --cat-current: var(--cat-storage);
  --cat-current-soft: var(--cat-storage-soft);

  /* Semantic */
  --ok: var(--cat-ai);
  --soon: var(--ink-faint);
  --idea: var(--cat-connectivity);
  --note: #ffe9a8;

  /* Type */
  --font-sans: "General Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, "Liberation Mono",
    Menlo, monospace;

  /* Space — 4px base unit */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-12: 48px;
  --space-16: 64px;
  --space-24: 96px;

  /* Layout */
  --container: 1200px;
  --container-article: 860px;

  /* Radius */
  --radius: 10px;
  --radius-sm: 6px;
  --radius-tab: 8px;

  /* Motion */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-hover: 150ms;
  --dur-fade: 180ms;
  --dur-draw: 600ms;

  /* Shadows */
  --shadow-card: 0 1px 2px rgba(26, 24, 20, 0.04),
    0 4px 12px rgba(26, 24, 20, 0.04);
  --shadow-note: 2px 4px 12px rgba(26, 24, 20, 0.12);
  --shadow-menu: 0 8px 30px rgba(26, 24, 20, 0.12),
    0 1px 2px rgba(26, 24, 20, 0.05);
}

/* ─── Accent utility classes ──────────────────────────────────
   Apply one of these to any element to redefine --cat-current (and
   its -soft variant) for that element and its descendants. Used in
   two places today:
     1. <main class="site-main accent--compute"> — sets the page
        accent so eyebrows, code-tab fills, buttons, diagram accents,
        and inline links pick up the right color.
     2. <section class="mega-menu__column accent--storage"> — sets a
        single mega-menu column's accent so its category heading +
        hover states + active outlines match its category.
   The roadmap dialog gets its accent via JS
   (element.style.setProperty), which CSP allows; only HTML style=""
   attributes are blocked. Keeping these as classes lets us hold the
   line on `style-src 'self'` (no 'unsafe-inline'). */
.accent--compute {
  --cat-current: var(--cat-compute);
  --cat-current-soft: var(--cat-compute-soft);
}
.accent--storage {
  --cat-current: var(--cat-storage);
  --cat-current-soft: var(--cat-storage-soft);
}
.accent--ai {
  --cat-current: var(--cat-ai);
  --cat-current-soft: var(--cat-ai-soft);
}
.accent--media {
  --cat-current: var(--cat-media);
  --cat-current-soft: var(--cat-media-soft);
}
.accent--connectivity {
  --cat-current: var(--cat-connectivity);
  --cat-current-soft: var(--cat-connectivity-soft);
}

/* Dark theme overrides — applied when <html data-theme="dark">.
   IMPORTANT: scoped to `html[...]` rather than the bare attribute
   selector. The KV theme-toggle <button>s carry data-theme="light"|"dark"
   as a button-target attribute (read by kv-demo.js); a bare
   [data-theme="dark"] selector would match those buttons too and
   leak dark-mode --ink onto them, breaking light-mode rendering. */
html[data-theme="dark"] {
  --paper: #171511;
  --paper-deep: #1f1c16;
  --paper-edge: #2c2820;
  --graph: #2a261e;
  --ink: #f0ebe0;
  --ink-muted: #a89f8b;
  --ink-faint: #766e5d;
  --note: #3d3520;
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3),
    0 4px 12px rgba(0, 0, 0, 0.25);
  --shadow-note: 2px 4px 12px rgba(0, 0, 0, 0.35);
  --shadow-menu: 0 8px 30px rgba(0, 0, 0, 0.45),
    0 1px 2px rgba(0, 0, 0, 0.25);
}

/* ==========================================================================
   3. Reset / base
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background-color: var(--paper);
  /* Subtle dot grid — the workshop / engineer's notebook texture */
  background-image: radial-gradient(
    circle,
    var(--graph) 1px,
    transparent 1.4px
  );
  background-size: 24px 24px;
  background-position: 0 0;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.65;
  /* Smooth color shift when the KV demo flips light/dark */
  transition: background-color 200ms var(--ease), color 200ms var(--ease);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a {
  color: var(--cat-current);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

p {
  margin: 0 0 var(--space-4);
}

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--space-6);
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
}

h1 {
  /* type step: h1 */
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
}
h2 {
  /* type step: h2 */
  font-size: clamp(1.5rem, 3vw, 2.25rem);
}
h3 {
  /* type step: h3 */
  font-size: 1.25rem;
  line-height: 1.3;
}

ul,
ol {
  margin: 0 0 var(--space-4);
  padding-left: var(--space-6);
}

/* ==========================================================================
   4. Eyebrow (shared caps label)
   ========================================================================== */

.eyebrow {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cat-current);
  margin: 0 0 var(--space-3);
}

/* ==========================================================================
   5. Site shell — header / main / footer
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--paper-edge);
}

.site-header__inner {
  max-width: var(--container);
  margin: 0 auto;
  padding: var(--space-4) var(--space-6);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.site-header__brand {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.site-header__brand:hover {
  text-decoration: none;
  color: var(--cat-current);
}

.site-header__nav {
  position: relative;
}

.site-main {
  max-width: var(--container);
  margin: 0 auto;
  padding: var(--space-12) var(--space-6) var(--space-24);
}

.site-footer {
  max-width: var(--container);
  margin: 0 auto;
  padding: var(--space-8) var(--space-6) var(--space-12);
  border-top: 1px solid var(--paper-edge);
  text-align: center;
  color: var(--ink-muted);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
}

.site-footer__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  color: var(--cat-current);
  font-size: 2rem;
  font-weight: 700;
  line-height: 1;
}

.site-footer__line {
  font-size: 0.85rem;
  margin: 0;
  font-style: italic;
}

/* ==========================================================================
   6. Mega-menu
   --------------------------------------------------------------------------
   Two contexts:
     .mega-menu--floating  — header dropdown, absolute, hidden by default
     .mega-menu--embedded  — inline on the homepage, always visible
   Internal layout is identical.
   ========================================================================== */

.mega-menu__trigger {
  appearance: none;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-4);
  font: inherit;
  font-family: var(--font-sans);
  font-weight: 500;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  transition: border-color var(--dur-hover) var(--ease),
    background var(--dur-hover) var(--ease);
}
.mega-menu__trigger:hover,
.mega-menu__trigger[aria-expanded="true"] {
  border-color: var(--cat-current);
  background: var(--paper-deep);
}
.mega-menu__chevron {
  font-size: 0.7rem;
  color: var(--ink-faint);
  transition: transform var(--dur-hover) var(--ease);
}
.mega-menu__trigger[aria-expanded="true"] .mega-menu__chevron {
  transform: rotate(180deg);
}

/* Floating panel sits below the trigger */
.mega-menu--floating {
  position: absolute;
  top: calc(100% + var(--space-3));
  right: 0;
  background: var(--paper);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  padding: var(--space-6);
  box-shadow: var(--shadow-menu);
  /* Width matches the homepage catalog's content area exactly so the
     dropdown's left/right edges line up with the embedded catalog
     below it. Derived from the same site container minus the
     site-main horizontal padding (--container - 2 * --space-6) so
     alignment stays correct if either value changes. min(96vw, …)
     keeps narrow viewports unaffected. */
  width: min(96vw, calc(var(--container) - 2 * var(--space-6)));
  /* Cap the height to the viewport (minus a header allowance) and let
     the panel scroll internally on overflow. overscroll-behavior keeps
     scroll input contained — when the panel hits its scroll boundary,
     the body underneath does NOT scroll-chain. Body scroll is also
     locked in main.js while the panel is open. */
  max-height: calc(100dvh - 5rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  animation: fade-in var(--dur-fade) var(--ease);
}
.mega-menu--floating[hidden] {
  display: none;
}

@keyframes fade-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Embedded panel — used on homepage as a section */
.mega-menu--embedded {
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 0;
}

/* Shared internal grid — 5 columns desktop, collapses on narrow screens */
.mega-menu__grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-6);
}

@media (max-width: 1100px) {
  .mega-menu__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (max-width: 720px) {
  .mega-menu__grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .mega-menu--floating {
    width: min(96vw, 560px);
  }
}
@media (max-width: 480px) {
  .mega-menu__grid {
    grid-template-columns: 1fr;
  }
}

.mega-menu__column {
  /* Each column sets --cat-current inline (e.g. style="--cat-current:..").
     All children read from it, so the entire column is themed by one value. */
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.mega-menu__category {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cat-current);
  margin: 0 0 var(--space-2);
}

.mega-menu__items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.mega-menu__item {
  display: grid;
  /* Fixed pill column so the pill's right edge lines up identically
     across every row regardless of pill variant. The previous `auto`
     value let the column shrink-wrap to each pill's intrinsic width,
     producing visibly uneven right edges on mobile. 3em is just wide
     enough to fit the pill's own min-width (4.5em at the pill's
     0.65rem font ≈ 47px) while reclaiming a few pixels for the
     blurb column on desktop. */
  grid-template-columns: 18px 1fr 3em;
  grid-template-rows: auto auto;
  grid-template-areas:
    "icon  name  pill"
    "icon  blurb pill";
  align-items: center;
  /* Tighter column-gap and horizontal padding reclaim ~16px of
     horizontal space per row for the blurb, letting more entries
     fit their tagline on one line. The negative horizontal margin
     mirrors the padding so the hover background still extends the
     same distance past the row's text content. */
  column-gap: var(--space-2);
  padding: var(--space-2);
  margin: 0 calc(-1 * var(--space-2));
  border-radius: var(--radius-sm);
  color: var(--ink);
  /* Force both variants — live <a> and upcoming <button> — to span
     the full column width. Without this the <button> default shrinks
     to its grid content, rendering narrower than the <a> on the same
     row and pushing its pill's right edge inward. box-sizing keeps
     the padding inside the 100% width so the row doesn't overflow. */
  width: 100%;
  box-sizing: border-box;
  transition: background var(--dur-hover) var(--ease),
    color var(--dur-hover) var(--ease);
}
.mega-menu__item:hover {
  background: var(--cat-current-soft);
  text-decoration: none;
}
.mega-menu__item:hover .mega-menu__icon {
  color: var(--cat-current);
}
.mega-menu__item.is-active {
  background: var(--cat-current-soft);
}
.mega-menu__item.is-active .mega-menu__icon {
  color: var(--cat-current);
}

/* Soon / idea items render as <button>. Reset button defaults so they
   visually match the <a> rows, then desaturate and switch the cursor
   to communicate "interactive but not primary — info available." */
.mega-menu__item--upcoming {
  /* Reset native <button> styling. Width-equalisation between the
     <a> and <button> variants happens at the shared .mega-menu__item
     rule above (width: 100% + box-sizing: border-box) — without it,
     the <button> renders narrower than the <a> and the pills don't
     line up across rows. */
  background: none;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: help;
  color: var(--ink-muted);
}
.mega-menu__item--upcoming .mega-menu__name {
  color: var(--ink-muted);
}
.mega-menu__item--upcoming .mega-menu__blurb {
  color: var(--ink-faint);
}
.mega-menu__item--upcoming:hover {
  /* Neutral warm tint, NOT the category accent — the accent is reserved
     for live, navigable items so the affordance stays honest. */
  background: var(--paper-edge);
}
.mega-menu__item--upcoming:hover .mega-menu__name {
  color: var(--ink);
}
.mega-menu__item--upcoming:focus-visible {
  outline: 2px solid var(--cat-current);
  outline-offset: -2px;
}

.mega-menu__icon {
  grid-area: icon;
  width: 18px;
  height: 18px;
  color: var(--ink-faint);
  transition: color var(--dur-hover) var(--ease);
  align-self: start;
  margin-top: 3px;
}

.mega-menu__name {
  grid-area: name;
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.3;
}

.mega-menu__blurb {
  grid-area: blurb;
  font-size: 0.8rem;
  color: var(--ink-muted);
  line-height: 1.3;
}

/* ==========================================================================
   7. Status pill
   ========================================================================== */

.pill {
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px var(--space-2);
  border-radius: 999px;
  white-space: nowrap;
  align-self: start;
  /* Anchor every pill to the right edge of its (fixed-width) grid
     cell. Belt-and-braces with the fixed pill column on
     .mega-menu__item — guarantees right-edge alignment across rows
     even if the column-template changes later. */
  justify-self: end;
  /* Force all three variants (LIVE / SOON / IDEA) to render at the
     same width. The bordered SOON / IDEA variants are intrinsically
     slightly wider than the borderless LIVE; this floor sizes LIVE
     up to match. text-align: center keeps the label centered when
     the box exceeds its content width. */
  min-width: 4.5em;
  text-align: center;
  margin-top: 2px;
  grid-area: pill;
}
.pill--live {
  background: var(--ok);
  color: #fff;
}
.pill--soon {
  border: 1px solid var(--paper-edge);
  color: var(--ink-faint);
  background: transparent;
  /* Compensate for border so pill doesn't grow taller than its siblings */
  padding: 1px calc(var(--space-2) - 1px);
}
.pill--idea {
  border: 1px solid var(--idea);
  color: var(--idea);
  background: transparent;
  padding: 1px calc(var(--space-2) - 1px);
}

/* ==========================================================================
   8. Hero — display variant (homepage)
   ========================================================================== */

.hero {
  margin-bottom: var(--space-16);
}

.hero--display {
  padding: var(--space-12) 0 var(--space-8);
  /* Soft glow behind the hero in the page's accent color */
  background: radial-gradient(
    ellipse 60% 80% at 30% 30%,
    var(--cat-current-soft),
    transparent 70%
  );
}

.hero__title {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: clamp(3rem, 8vw, 6rem);
  line-height: 1.02;
  letter-spacing: -0.03em;
  margin: 0 0 var(--space-2);
  color: var(--ink);
}

.hero--display .hero__title {
  display: inline-block;
  position: relative;
}

.hero__period {
  color: var(--cat-current);
  /* The dot is the strongest single brand signal — give it a tiny extra
     visual weight so it pops against the dark headline */
  display: inline-block;
}

/* The hand-drawn underline. SVG sits inside the hero block, positioned
   under "Build" via inline transform/positioning in home.ts. */
.hero__underline {
  display: block;
  width: clamp(120px, 22vw, 240px);
  height: 14px;
  margin: -8px 0 var(--space-6);
  color: var(--cat-current);
  overflow: visible;
}
.hero__underline path {
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Drawing animation — set up here; first-paint trigger via animation. */
  stroke-dasharray: 280;
  stroke-dashoffset: 280;
  animation: draw-underline var(--dur-draw) var(--ease) 200ms forwards;
}

@keyframes draw-underline {
  to { stroke-dashoffset: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__underline path {
    animation: none;
    stroke-dashoffset: 0;
  }
}

.hero__lede {
  font-size: 1.2rem;
  line-height: 1.55;
  color: var(--ink-muted);
  max-width: 56ch;
  margin: 0 0 var(--space-6);
}

.hero__meta {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--ink-faint);
  margin: 0;
}
.hero__meta-tag {
  color: var(--cat-current);
  font-weight: 500;
  margin-right: var(--space-1);
}

/* The "Cloudflare's Developer Platform" tail of the meta line.
   Cloudflare orange + semibold, linked to their docs. Sticks out
   intentionally — a small visual nod to the underlying infra. */
.hero__meta-cf {
  color: #f6821f; /* Cloudflare brand orange */
  font-weight: 600;
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease);
}
.hero__meta-cf:hover {
  text-decoration: underline;
  color: #f6821f;
}

/* ==========================================================================
   9. Hero — product page variant
   ========================================================================== */

.hero--product {
  padding: var(--space-8) 0 var(--space-6);
  margin-bottom: var(--space-8);
}

.hero--product .hero__title {
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  letter-spacing: -0.02em;
  font-weight: 600;
}

/* ==========================================================================
   10. Section — generic wrapper for homepage sections
   ========================================================================== */

.section {
  margin: 0 0 var(--space-16);
}

.section__title {
  margin-bottom: var(--space-6);
}

.section__lede {
  color: var(--ink-muted);
  font-size: 1.05rem;
  max-width: 60ch;
  margin: 0 0 var(--space-8);
}

/* ==========================================================================
   11. Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  border: 1.5px solid transparent;
  transition: background var(--dur-hover) var(--ease),
    border-color var(--dur-hover) var(--ease),
    color var(--dur-hover) var(--ease);
  text-decoration: none;
}
.btn:hover {
  text-decoration: none;
}

.btn--primary {
  background: var(--cat-current);
  color: #fff;
  border-color: var(--cat-current);
}
.btn--primary:hover {
  background: color-mix(in srgb, var(--cat-current) 85%, var(--ink));
  border-color: color-mix(in srgb, var(--cat-current) 85%, var(--ink));
}

.btn--secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}
.btn--secondary:hover {
  background: var(--ink);
  color: var(--paper);
}

.btn--ghost {
  background: transparent;
  color: var(--cat-current);
  border-color: transparent;
  padding: var(--space-3) var(--space-3);
}
.btn--ghost:hover {
  text-decoration: underline;
}

/* ==========================================================================
   12. Code block — figure/figcaption with file-path tab
   ========================================================================== */

.code {
  margin: var(--space-6) 0 var(--space-8);
  display: flex;
  flex-direction: column;
}

.code__tab {
  display: inline-flex;
  align-self: flex-start;
  background: var(--cat-current);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: var(--space-1) var(--space-3);
  border-top-left-radius: var(--radius-tab);
  border-top-right-radius: var(--radius-tab);
  /* Fuse with the pre below */
  margin-bottom: -1px;
  position: relative;
  z-index: 1;
}

.code__pre {
  background: #1f1c16;
  color: #f0ebe0;
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  /* Fuse the top-left corner with the tab */
  border-top-left-radius: 0;
  padding: var(--space-4) var(--space-5);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 0.875rem;
  line-height: 1.6;
  margin: 0;
}
.code__pre code {
  font-family: inherit;
  font-size: inherit;
  background: none;
  border: none;
  padding: 0;
}

/* Inline code */
:not(pre) > code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--ink);
}

/* ==========================================================================
   13. Sticky-note callout
   ========================================================================== */

.note {
  background: var(--note);
  color: var(--ink);
  padding: var(--space-4) var(--space-5);
  border-radius: 2px;
  box-shadow: var(--shadow-note);
  transform: rotate(-0.6deg);
  max-width: 520px;
  margin: var(--space-6) 0 var(--space-8);
  position: relative;
  font-size: 0.95rem;
}

.note__label {
  display: block;
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 var(--space-2);
}

.note__body {
  margin: 0;
  line-height: 1.5;
}

.note--placeholder {
  background: color-mix(in srgb, var(--note) 50%, var(--paper-deep));
  transform: rotate(0.4deg);
}
.note--placeholder .note__label {
  color: var(--ink-faint);
}

.note code {
  /* Note's inline code stays warm rather than going dark */
  background: rgba(0, 0, 0, 0.06);
  border-color: rgba(0, 0, 0, 0.08);
}

/* ==========================================================================
   14. Theme toggle (KV demo)
   ========================================================================== */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  margin: var(--space-4) 0 var(--space-8);
}
.theme-toggle__label {
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.theme-toggle__btn {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink);
  font: inherit;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 500;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all var(--dur-hover) var(--ease);
}
.theme-toggle__btn:hover {
  border-color: var(--cat-current);
}
.theme-toggle__btn.is-active {
  background: var(--cat-current);
  border-color: var(--cat-current);
  color: #fff;
}

/* ==========================================================================
   14b. Workers demos (fingerprint / stream / HTMLRewriter)
   --------------------------------------------------------------------------
   Three small demo components, all on the /workers page. Visual language
   matches the rest of the site: paper-deep card surface, mono labels,
   accent only where it earns it (buttons, key-value keys).
   ========================================================================== */

/* ─── Fingerprint panel ─── */
.fingerprint {
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  padding: var(--space-5);
  margin: var(--space-4) 0 var(--space-6);
}
.fingerprint__list {
  display: grid;
  /* Two columns on desktop, collapses to one on narrow screens via @media. */
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2) var(--space-6);
  margin: 0 0 var(--space-4);
  padding: 0;
}
.fingerprint__row {
  display: grid;
  grid-template-columns: 110px 1fr;
  align-items: baseline;
  gap: var(--space-3);
  border-bottom: 1px dotted var(--paper-edge);
  padding: var(--space-2) 0;
}
.fingerprint__key {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}
.fingerprint__val {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--ink);
  margin: 0;
  /* Long ASN org names can otherwise blow out the column */
  word-break: break-word;
}
.fingerprint__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.fingerprint__timestamp {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-faint);
}
@media (max-width: 600px) {
  .fingerprint__list {
    grid-template-columns: 1fr;
  }
}

/* ─── HTMLRewriter demo ─── */
.rewrite-demo {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: var(--space-4) 0 var(--space-6);
}
.rewrite-demo__label {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.rewrite-demo__input {
  width: 100%;
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-mono);
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink);
  resize: vertical;
}
.rewrite-demo__input:focus {
  outline: 2px solid var(--cat-current);
  outline-offset: 1px;
}
.rewrite-demo__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.rewrite-demo__error {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--cat-storage);
}
.rewrite-demo__results {
  display: grid;
  /* Two panes: source + visual preview. Stack on narrow screens. */
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  margin-top: var(--space-3);
}
@media (max-width: 720px) {
  .rewrite-demo__results {
    grid-template-columns: 1fr;
  }
}
.rewrite-demo__pane {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}
.rewrite-demo__pane-label {
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}
.rewrite-demo__source {
  background: #1f1c16;
  color: #f0ebe0;
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.55;
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  min-height: 10em;
  overflow-x: auto;
}
.rewrite-demo__preview {
  width: 100%;
  min-height: 10em;
  background: var(--paper);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
}

/* ==========================================================================
   14c. Durable Objects demos (shared room + counter race)
   --------------------------------------------------------------------------
   Two components on /durable-objects:
     .room  — fixed-aspect surface that hosts live cursors from peers
     .race  — two-column side-by-side output of an atomic vs. racy counter
   Visual language matches the rest of the site: paper-deep card surface,
   mono labels, accent only where it earns it.
   ========================================================================== */

/* ─── Shared-room surface ─── */
.room {
  margin: var(--space-4) 0 var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.room__surface {
  /* Fixed aspect ratio so cursor coordinates (sent as 0..1) map
     consistently across all clients regardless of viewport. */
  position: relative;
  aspect-ratio: 16 / 7;
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: crosshair;
  /* Subtle dot grid hints at the workshop visual language without
     overwhelming the cursor markers. */
  background-image: radial-gradient(
    circle,
    var(--graph) 0.8px,
    transparent 1.2px
  );
  background-size: 16px 16px;
}

.room__hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--ink-faint);
  pointer-events: none;
  text-align: center;
  padding: var(--space-4);
}

/* A peer's cursor: small dot + name chip. Positioned absolutely from
   JS via top/left percentages. The JS sets --cursor-color per element
   so each peer keeps a stable identity color. */
.cursor {
  position: absolute;
  pointer-events: none;
  width: 14px;
  height: 14px;
  margin-left: -7px;
  margin-top: -7px;
  border-radius: 50%;
  background: var(--cursor-color, var(--ink));
  box-shadow: 0 0 0 2px var(--paper-deep),
    0 1px 4px rgba(0, 0, 0, 0.18);
  /* Smooth motion between server pings without lying about latency. */
  transition: top 80ms linear, left 80ms linear;
  z-index: 2;
}
.cursor__name {
  position: absolute;
  top: 14px;
  left: 14px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  color: #fff;
  background: var(--cursor-color, var(--ink));
  padding: 1px 6px;
  border-radius: 4px;
  white-space: nowrap;
}

/* The "you" cursor renders inside the surface too, but in a calmer
   ring style so it's easy to tell yours from peers'. */
.cursor--self {
  background: transparent;
  border: 2px solid var(--cursor-color, var(--cat-current));
  box-shadow: none;
}
.cursor--self .cursor__name {
  background: transparent;
  color: var(--cursor-color, var(--cat-current));
  border: 1px solid currentColor;
  font-weight: 600;
}

/* Click ripple — fades out via animation. */
.ripple {
  position: absolute;
  width: 8px;
  height: 8px;
  margin-left: -4px;
  margin-top: -4px;
  border-radius: 50%;
  background: var(--cursor-color, var(--cat-current));
  pointer-events: none;
  animation: ripple-pulse 700ms var(--ease) forwards;
  z-index: 1;
}
@keyframes ripple-pulse {
  0%   { transform: scale(1);  opacity: 0.7; }
  100% { transform: scale(8);  opacity: 0;   }
}
@media (prefers-reduced-motion: reduce) {
  .ripple { animation-duration: 1ms; }
  .cursor { transition: none; }
}

.room__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--ink-faint);
  flex-wrap: wrap;
}
.room__identity {
  color: var(--ink-muted);
}
.room__count {
  color: var(--ink-muted);
}

.room__log {
  list-style: none;
  margin: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-muted);
  /* Cap so the log doesn't grow without bound. */
  max-height: 9em;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.room__log:empty::before {
  content: "// recent events appear here";
  color: var(--ink-faint);
}
.room__log-event {
  margin: 0;
  line-height: 1.4;
}
.room__log-event--join { color: var(--cat-ai); }
.room__log-event--leave { color: var(--ink-faint); }
.room__log-event--click { color: var(--ink); }

/* ─── Counter race (DO vs KV) ─── */
.race {
  margin: var(--space-4) 0 var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.race__controls {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.race__error {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--cat-storage);
}

.race__cols {
  display: grid;
  /* Two equal columns side by side; stack on narrow screens so the
     race still reads top-to-bottom on mobile. */
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}
@media (max-width: 720px) {
  .race__cols {
    grid-template-columns: 1fr;
  }
}

.race__col {
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 14em;
  /* The "good" / "bad" modifiers paint a thin top accent so the
     two columns are recognizable at a glance. The accents live OFF
     the page's --cat-current so the race visually transcends the
     page accent (this is the lesson, not the chrome). */
}
.race__col--good {
  border-top: 3px solid var(--cat-ai);
}
.race__col--bad {
  border-top: 3px solid var(--cat-storage);
}

.race__col-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.race__col-title {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0;
  color: var(--ink);
}
.race__col-sub {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-faint);
  margin: 0;
}

.race__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Wraps so 25 numbers don't make the column tall. */
  grid-template-columns: repeat(auto-fill, minmax(2.4em, 1fr));
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 0.85rem;
}

.race__cell {
  background: var(--paper);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-sm);
  padding: 4px 6px;
  text-align: center;
  color: var(--ink);
  /* Faint pop-in for newly-arrived numbers. */
  animation: race-cell-in 180ms var(--ease);
}
@keyframes race-cell-in {
  from { transform: translateY(2px); opacity: 0; }
  to   { transform: translateY(0);   opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .race__cell { animation: none; }
}
.race__cell--dup {
  /* The visceral payoff: duplicate numbers lit up in storage magenta. */
  background: color-mix(in srgb, var(--cat-storage) 18%, var(--paper));
  border-color: var(--cat-storage);
  color: var(--cat-storage);
  font-weight: 600;
}

.race__summary {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-muted);
  margin: 0;
  /* Multi-line summary — unique count, duplicates, gaps. */
  white-space: pre-wrap;
  line-height: 1.5;
}
.race__summary--good { color: var(--cat-ai); }
.race__summary--bad  { color: var(--cat-storage); }

/* ==========================================================================
   15. Article + prose (long-form text on product pages)
   ========================================================================== */

.article {
  max-width: var(--container-article);
}

.prose {
  color: var(--ink);
}
.demo-kicker {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  margin: var(--space-12) 0 var(--space-2);
  padding: 0.2rem 0.55rem;
  border: 1.5px solid var(--cat-current);
  border-radius: 999px;
  background: var(--cat-current-soft);
  color: var(--cat-current);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1;
  text-transform: uppercase;
}
.demo-kicker + h2 {
  margin-top: 0;
}
.prose h2 {
  margin-top: var(--space-12);
}
.prose h3 {
  margin-top: var(--space-8);
}
.prose p {
  color: var(--ink-muted);
  font-size: 1.05rem;
  line-height: 1.7;
}
.prose ul {
  color: var(--ink-muted);
  margin: 0 0 var(--space-4);
  padding-left: var(--space-6);
}
.prose li {
  margin-bottom: var(--space-2);
}
.prose strong {
  color: var(--ink);
  font-weight: 600;
}

/* ==========================================================================
   16. Utility — visually hidden (a11y)
   ========================================================================== */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   16b. R2 demo — uploader, gallery, hero asset
   --------------------------------------------------------------------------
   The /r2 page has two demos. This section styles both:

     - .r2-uploader / .r2-dropzone / .r2-progress  (demo A: drag/drop upload)
     - .r2-gallery                                  (demo A: per-session list)
     - .r2-hero-asset                               (demo B: image from R2)

   Visual language matches the rest of the site — paper-deep card surface,
   accent only where it earns it. The dropzone has explicit visual states
   (data-state="idle|dragover|uploading|error|success") that the client
   JS toggles; CSS does the rest.
   ========================================================================== */

/* ─── Uploader root + error line ─── */
.r2-uploader {
  margin: var(--space-4) 0 var(--space-6);
}
.r2-uploader__error {
  margin: var(--space-2) 0 0;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  /* Storage magenta is the page accent, but errors should always read
     as errors — borrow the cat-storage hue without the soft fill. */
  color: var(--cat-storage);
  background: color-mix(in srgb, var(--cat-storage) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--cat-storage) 35%, transparent);
  border-radius: var(--radius-sm);
}

/* ─── Dropzone ─── */
.r2-dropzone {
  /* It's a <label> so clicking anywhere triggers the hidden <input>. */
  display: block;
  position: relative;
  padding: var(--space-12) var(--space-6);
  background: var(--paper-deep);
  border: 2px dashed var(--paper-edge);
  border-radius: var(--radius);
  text-align: center;
  cursor: pointer;
  transition:
    border-color var(--dur-hover) var(--ease),
    background var(--dur-hover) var(--ease),
    transform var(--dur-hover) var(--ease);
}
.r2-dropzone:hover,
.r2-dropzone:focus-within {
  border-color: var(--cat-current);
}
.r2-dropzone:focus-visible {
  outline: 2px solid var(--cat-current);
  outline-offset: 2px;
}

/* The native input is visually hidden but still reachable by SR users
   via the label (and triggered by click/keyboard). */
.r2-dropzone__input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

.r2-dropzone__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: center;
}
.r2-dropzone__title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--ink);
}
.r2-dropzone__hint {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--ink-faint);
}

/* State: dragover — visitor is hovering a file over the box. */
.r2-dropzone[data-state="dragover"] {
  border-color: var(--cat-current);
  background: var(--cat-current-soft);
  transform: translateY(-1px);
}

/* State: uploading — show progress, dim the idle copy. */
.r2-dropzone[data-state="uploading"] {
  cursor: progress;
  border-style: solid;
  border-color: var(--cat-current);
}
.r2-dropzone[data-state="uploading"] .r2-dropzone__inner {
  opacity: 0.55;
}

/* State: success — quick green flash before we revert to idle. */
.r2-dropzone[data-state="success"] {
  border-color: #2f9e44;
  background: color-mix(in srgb, #2f9e44 10%, var(--paper-deep));
}

/* State: error — red border, idle copy stays visible. */
.r2-dropzone[data-state="error"] {
  border-color: var(--cat-storage);
  background: color-mix(in srgb, var(--cat-storage) 6%, var(--paper-deep));
}

/* ─── Progress bar (overlaid on the dropzone) ─── */
.r2-progress {
  position: absolute;
  left: var(--space-6);
  right: var(--space-6);
  bottom: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.r2-progress__bar {
  height: 6px;
  width: 0%;
  background: var(--cat-current);
  border-radius: 3px;
  transition: width 120ms linear;
}
.r2-progress__label {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--ink-muted);
  text-align: left;
}

/* ─── Gallery (per-session uploads) ─── */
.r2-gallery {
  margin: var(--space-6) 0 var(--space-8);
  min-height: 1rem;
}
.r2-gallery__empty {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--ink-faint);
}
.r2-gallery__list {
  list-style: none;
  margin: 0;
  padding: 0;
  /* Three-up grid that collapses on narrow screens. */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}
@media (max-width: 640px) {
  .r2-gallery__list {
    grid-template-columns: 1fr;
  }
}
.r2-gallery__item {
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.r2-gallery__img {
  width: 100%;
  /* Square-ish thumbnails — `cover` keeps them visually consistent
     even when source aspect ratios vary. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  background: var(--paper);
}
.r2-gallery__meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--ink-muted);
  border-top: 1px solid var(--paper-edge);
}
.r2-gallery__size {
  flex: 0 0 auto;
}
.r2-gallery__expires {
  flex: 1 1 auto;
  color: var(--ink-faint);
}
.r2-gallery__link {
  flex: 0 0 auto;
  color: var(--cat-current);
  text-decoration: none;
  font-weight: 600;
}
.r2-gallery__link:hover {
  text-decoration: underline;
}
/* ─── Object proof panel ─── */
.r2-proof {
  margin: calc(var(--space-6) * -1) 0 var(--space-8);
  padding: var(--space-4);
  background: color-mix(in srgb, var(--cat-current) 8%, var(--paper-deep));
  border: 1px solid color-mix(in srgb, var(--cat-current) 35%, var(--paper-edge));
  border-radius: var(--radius);
}
.r2-proof__label {
  display: block;
  margin: 0 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cat-current);
}
.r2-proof__grid {
  display: grid;
  gap: var(--space-2);
  margin: 0;
}
.r2-proof__row {
  display: grid;
  grid-template-columns: minmax(7rem, 0.34fr) 1fr;
  gap: var(--space-3);
  align-items: baseline;
}
.r2-proof dt,
.r2-proof dd {
  margin: 0;
}
.r2-proof dt {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--ink-faint);
}
.r2-proof dd {
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--ink-muted);
  overflow-wrap: anywhere;
}
.r2-proof a {
  color: var(--cat-current);
  text-decoration: none;
}
.r2-proof a:hover {
  text-decoration: underline;
}
@media (max-width: 640px) {
  .r2-proof__row {
    grid-template-columns: 1fr;
    gap: var(--space-1);
  }
}

/* ─── Hero asset (demo B) ─── */
.r2-hero-asset {
  margin: var(--space-4) 0 var(--space-8);
}
.r2-hero-asset__img {
  display: block;
  width: 100%;
  /* Source is 4248x960. Setting `height: auto` honors the aspect ratio
     while the explicit width/height attrs in HTML reserve the right
     box during load (no CLS). */
  height: auto;
  border-radius: var(--radius);
  background: var(--paper-deep);
  border: 1px solid var(--paper-edge);
}
.r2-hero-asset__caption {
  margin: var(--space-2) 0 0;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--ink-faint);
}
.r2-hero-asset__link {
  color: var(--cat-current);
  text-decoration: none;
}
.r2-hero-asset__link:hover {
  text-decoration: underline;
}

/* ==========================================================================
   17. Roadmap dialog (soon / idea items)
   --------------------------------------------------------------------------
   A single shared <dialog> populated by JS when the user clicks a
   non-live menu item. Native <dialog> handles focus trap, Escape-to-
   close, and inert-ing the rest of the page; we just paint it.
   ========================================================================== */

.roadmap-dialog {
  /* Native <dialog> ships with chrome borders; replace with paper. */
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  padding: var(--space-8);
  max-width: 480px;
  width: calc(100% - var(--space-6) * 2);
  box-shadow: var(--shadow-menu);
  font-family: var(--font-sans);
  /* --cat-current is set inline by JS at open time to match the
     clicked item's *category* (compute=blue, storage=magenta, etc.) —
     this overrides the inherited value from <main> so the dialog
     reflects the item, not the page. */
}

/* Warm translucent backdrop instead of the browser default near-black. */
.roadmap-dialog::backdrop {
  background: rgba(40, 30, 20, 0.45);
  backdrop-filter: blur(2px);
}

.roadmap-dialog__title {
  font-family: var(--font-sans);
  font-size: 1.4rem;
  font-weight: 600;
  margin: 0 0 var(--space-3);
  /* Use the inherited page accent for the title — small, deliberate
     touch that ties the modal to the page it opened from. */
  color: var(--cat-current);
  /* Reserve room on the right so the close × never collides */
  padding-right: var(--space-8);
}

.roadmap-dialog__body {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink-muted);
  margin: 0 0 var(--space-5);
}

/* The product's blurb, set apart by typography rather than chrome.
   Larger display-weight text in full ink (vs the muted body above),
   with curly quotation marks bracketing it for visual rhythm. The
   left edge sits flush with the body — no card, no border — so the
   dialog stays calm and the blurb earns its weight from type alone. */
.roadmap-dialog__gist {
  font-family: var(--font-sans);
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1.45;
  color: var(--ink);
  margin: 0 0 var(--space-6);
  /* A subtle hanging indent so the opening quote sits in the margin
     and the first letter of actual text aligns with the body above. */
  padding-left: var(--space-4);
  text-indent: calc(-1 * var(--space-4));
}
/* Curly quotes: ::before owns the opening, ::after the closing.
   Color them in the page accent so the gist still ties to the
   category visually, just more lightly than a full card would. */
.roadmap-dialog__gist::before {
  content: "\201C"; /* left double quotation mark */
  color: var(--cat-current);
  font-size: 1.4em;
  font-weight: 600;
  margin-right: 0.15em;
  vertical-align: -0.1em;
}
.roadmap-dialog__gist::after {
  content: "\201D"; /* right double quotation mark */
  color: var(--cat-current);
  font-size: 1.4em;
  font-weight: 600;
  margin-left: 0.1em;
  vertical-align: -0.1em;
}

.roadmap-dialog__close {
  /* Floating × in the top-right corner */
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  background: none;
  border: 0;
  font-family: var(--font-sans);
  font-size: 1.5rem;
  line-height: 1;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  color: var(--ink-faint);
  cursor: pointer;
  transition: background var(--dur-hover) var(--ease),
    color var(--dur-hover) var(--ease);
}
.roadmap-dialog__close:hover {
  background: var(--paper-edge);
  color: var(--ink);
}
.roadmap-dialog__close:focus-visible {
  outline: 2px solid var(--cat-current);
  outline-offset: 2px;
}

.roadmap-dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
}
