/* Brand typography, per the brand manual (assets/branding/index.html) — which is the
   canonical source, and this file did not follow it. It loaded Inter + JetBrains Mono
   and called them "brand", justified as mirroring outreach/web: it aligned with a SIBLING
   SURFACE instead of with the manual, and a copy of a copy is how a brand drifts.
   The manual says, and now so does this file:
     · IBM Plex Mono 600 — the wordmark (already baked into the logo SVG)
     · IBM Plex Mono     — code, ids, NCL
     · IBM Plex Sans 500 — the tagline
     · system font stack — every other UI element and all body text (so NO webfont for
                           body: the site's --font-sans already resolves to the system
                           stack, which is what the manual asks for — Inter was the drift)
   The faces are SELF-HOSTED. The framework serves `style-src 'self' 'unsafe-inline';
   font-src 'self' data:`, so a fonts.googleapis.com stylesheet is blocked outright — the
   site loaded unstyled type in every browser that enforces it, and Brave never even sent
   the request. fonts-ibm-plex.css and /fonts/*.woff2 are generated by
   `just sync-brand-fonts`; the family list lives in scripts/build/sync-brand-fonts.nu.
   @import must precede every style rule (comments excepted), so it stays at the top. */
@import url('/styles/fonts-ibm-plex.css');

/* Custom CSS file for static file serving example */

/* Reset and base styles */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* The manual's system stack, verbatim. It was "Segoe UI" first — a Windows face leading
   the stack on a brand whose manual prints -apple-system first. */
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  line-height: 1.6;
  color: #333;
  background-color: #f8f9fa;
}

/* Container */
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
}

/* Typography — headings carry the brand, in the only way the brand allows.
 *
 * This was `color: #2c3e50` — a literal from the starter theme, with NO dark
 * variant: on the dark canvas the headings were slate-on-slate, all but invisible.
 *
 * The wordmark is `Onto` in SILVER #C0CCD8 + `ref` in AMBER #E8A838, and neither is
 * a heading colour on white: silver is 1.6:1 and amber 2.1:1 — amber fails even the
 * 3:1 floor for large text. So the brand is DERIVED, never copied: --onto-heading is
 * the wordmark silver on the dark canvas and its deep sibling on the light one (both
 * >11:1), and the amber survives where it CAN be read — as the h2 rule below, which
 * is the typographic echo of the `Onto|ref` seam. See --onto-* tokens further down;
 * they already flip on `html.light` / `[data-theme="light"]`, which is what <html>
 * actually carries. */
h1,
h2,
h3,
h4,
h5,
h6 {
  margin-bottom: 1rem;
  color: var(--onto-heading, #c0ccd8);
}

/* The element rule above cannot reach the page TITLE: `.ds-heading-xl` carries its own
 * `--color-neutral-900` in htmx-components.css, a class selector that loads AFTER this
 * file — so the h1 stayed framework-neutral while every h2 below it went brand, which
 * is the worst of both. `html .ds-page ...` (0,2,1) outranks both `.ds-heading-xl` and
 * its `.dark` twin, so ONE rule brands the title in BOTH themes — the token already
 * knows which theme it is in. */
html .ds-page .ds-heading-xl,
html .ds-page .ds-heading-lg {
  color: var(--onto-heading, #c0ccd8);
}

/* The amber seam. Scoped to page/prose bodies so it marks SECTIONS, never the
 * headings inside nav, cards or the hero — a rule everywhere is a rule nowhere. */
.ds-page-body h2,
.ds-prose h2 {
  padding-left: 0.75rem;
  border-left: 3px solid var(--onto-rule, #e8a838);
}

h1 {
  font-size: 2.5rem;
  font-weight: 700;
}

h2 {
  font-size: 2rem;
  font-weight: 600;
}

h3 {
  font-size: 1.5rem;
  font-weight: 500;
}

p {
  margin-bottom: 1rem;
}

/* Buttons */
.btn-icon {
  padding: 2px 4px !important;
}

.btn {
  display: inline-block;
  /* padding: 12px 24px; */
  font-size: 1rem;
  font-weight: 500;
  text-decoration: none;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.btn-primary {
  background-color: #3498db;
  color: white;
}

.btn-primary:hover {
  background-color: #2980b9;
  transform: translateY(-2px);
}

.btn-secondary {
  background-color: #6c757d;
  color: white;
}

.btn-secondary:hover {
  background-color: #5a6268;
}

.btn-success {
  background-color: #28a745;
  color: white;
}

.btn-success:hover {
  background-color: #218838;
}

/* Cards */
.card {
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  padding: 20px;
  margin-bottom: 20px;
  transition:
    transform 0.3s ease,
    box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-5px);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}

.card-title {
  color: #2c3e50;
  margin-bottom: 10px;
}

.card-text {
  color: #666;
  line-height: 1.5;
}

/* Navigation */
.navbar {
  background-color: #2c3e50;
  padding: 1rem 0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.navbar-brand {
  color: white;
  font-size: 1.5rem;
  font-weight: 700;
  text-decoration: none;
}

.navbar-nav {
  display: flex;
  list-style: none;
  gap: 2rem;
  margin-left: auto;
}

.nav-link {
  color: #ecf0f1;
  text-decoration: none;
  transition: color 0.3s ease;
}

.nav-link:hover {
  color: #3498db;
}

/* Grid system */
.row {
  display: flex;
  flex-wrap: wrap;
  margin: 0 -15px;
}

.col {
  flex: 1;
  padding: 0 15px;
}

.col-1 {
  flex: 0 0 8.333333%;
}
.col-2 {
  flex: 0 0 16.666667%;
}
.col-3 {
  flex: 0 0 25%;
}
.col-4 {
  flex: 0 0 33.333333%;
}
.col-6 {
  flex: 0 0 50%;
}
.col-8 {
  flex: 0 0 66.666667%;
}
.col-12 {
  flex: 0 0 100%;
}

/* Utilities */
.text-center {
  text-align: center;
}

.text-left {
  text-align: left;
}

.text-right {
  text-align: right;
}

.mt-1 {
  margin-top: 0.25rem;
}
.mt-2 {
  margin-top: 0.5rem;
}
.mt-3 {
  margin-top: 1rem;
}
.mt-4 {
  margin-top: 1.5rem;
}
.mt-5 {
  margin-top: 3rem;
}

.mb-1 {
  margin-bottom: 0.25rem;
}
.mb-2 {
  margin-bottom: 0.5rem;
}
.mb-3 {
  margin-bottom: 1rem;
}
.mb-4 {
  margin-bottom: 1.5rem;
}
.mb-5 {
  margin-bottom: 3rem;
}

.p-1 {
  padding: 0.25rem;
}
.p-2 {
  padding: 0.5rem;
}
.p-3 {
  padding: 1rem;
}
.p-4 {
  padding: 1.5rem;
}
.p-5 {
  padding: 3rem;
}

/* Alerts */
.alert {
  padding: 15px;
  margin-bottom: 20px;
  border: 1px solid transparent;
  border-radius: 6px;
}

.alert-info {
  color: #0c5460;
  background-color: #d1ecf1;
  border-color: #bee5eb;
}

.alert-success {
  color: #155724;
  background-color: #d4edda;
  border-color: #c3e6cb;
}

.alert-warning {
  color: #856404;
  background-color: #fff3cd;
  border-color: #ffeaa7;
}

.alert-danger {
  color: #721c24;
  background-color: #f8d7da;
  border-color: #f5c6cb;
}

/* Forms */
.form-group {
  margin-bottom: 1rem;
}

.form-label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 500;
  color: #333;
}

.form-control {
  display: block;
  width: 100%;
  padding: 0.75rem;
  font-size: 1rem;
  border: 1px solid #ced4da;
  border-radius: 6px;
  transition:
    border-color 0.3s ease,
    box-shadow 0.3s ease;
}

.form-control:focus {
  outline: none;
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}

/* Responsive design */
@media (max-width: 768px) {
  .container {
    padding: 0 15px;
  }

  .row {
    flex-direction: column;
  }

  .col {
    flex: 1;
    margin-bottom: 1rem;
  }

  .navbar-nav {
    flex-direction: column;
    gap: 1rem;
  }

  h1 {
    font-size: 2rem;
  }

  h2 {
    font-size: 1.5rem;
  }
}

/* Animation utilities */
.fade-in {
  animation: fadeIn 0.5s ease-in;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.slide-up {
  animation: slideUp 0.6s ease-out;
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Static file serving indicator */
.static-file-badge {
  position: fixed;
  bottom: 20px;
  right: 20px;
  background-color: #28a745;
  color: white;
  padding: 8px 12px;
  border-radius: 20px;
  font-size: 0.8rem;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  z-index: 1000;
}

.static-file-badge:before {
  content: "📁 ";
}

/* All images inside post body: never overflow the column. */
.post-content-body img {
  max-width: 100%;
  height: auto;
}

/* Post body: lead thumbnail image rendered from markdown (first paragraph, lone img) */
.post-content-body > p:first-child > img:only-child {
  width: 100%;
  max-height: 24rem;
  object-fit: contain;
  border-radius: 0.5rem;
  margin-bottom: 1rem;
}

/* Hide duplicate H1 title from markdown body — already shown in the header section.
   Covers two layouts:
   - thumbnail-first: <p><img></p> <h1>  → adjacent sibling after first p
   - no thumbnail:    <h1> as first child */
.post-content-body > p:first-child + h1,
.post-content-body > h1:first-child {
  display: none;
}

/* Content grid card thumbnail */
.grid-post-figure {
  max-height: 11rem;
}

.grid-post-img {
  height: 100%;
  max-height: 11rem;
  object-fit: contain;
  margin: 0 auto;
}

/* Theme-aware thumbnails: show light img on light theme, dark img on dark theme */
.grid-post-img-dark {
  display: none;
}

[data-theme="dark"] .grid-post-img-light {
  display: none;
}

[data-theme="dark"] .grid-post-img-dark {
  display: block;
}

/* ── Content graph mini ────────────────────────────────────────────────────── */

/* Constrain graph to sidebar width; prevent overflow on mobile */
.content-graph-mini-wrapper {
  max-width: 280px;
}

/* Preview: scales SVG to wrapper width, zoom-in cursor */
.content-graph-mini-preview {
  cursor: zoom-in;
  border-radius: 0.5rem;
  overflow: hidden;
  transition: opacity 0.15s;
}

.content-graph-mini-preview:hover {
  opacity: 0.85;
}

.content-graph-mini-preview svg {
  width: 100%;
  height: auto;
  display: block;
}

/* Dark-mode overrides for SVG CSS custom properties.
   The SVG embeds <style> using var(--cg-label) etc with light-mode fallbacks.
   Setting these on the wrapper propagates into the SVG through the cascade. */
[data-theme="dark"] .content-graph-mini-wrapper {
  --cg-label: #d1d5db;
  --cg-edge:  #4b5563;
  --cg-node:  #9ca3af;
}

/* Related content link styling — <A> from leptos_router renders a plain <a>.
   No underline; subtle hover; text matches surrounding content. */
.content-graph-related a {
  font-size: 0.875rem;
  line-height: 1.375;
  text-decoration: none;
  transition: opacity 0.15s;
}

.content-graph-related a:hover {
  opacity: 0.75;
}

/* ── ontoref brand tokens (mirror of ontoref-brand.css) ───────────────────────
 * Kept here so the brand palette applies in source asset mode without requiring
 * a framework rebuild to pick up the new ontoref-brand.css <link>. The canonical
 * copy is /styles/ontoref-brand.css, wired in config/assets.ncl. */
:root {
  --color-brand-primary: #E8A838;
  --color-brand-secondary: #C0CCD8;
  --color-brand-accent: #E8A838;
  --color-primary: #E8A838;
  --color-primary-hover: #C88A20;
  --color-secondary: #C0CCD8;
  --color-secondary-hover: #a8b4c8;
  --color-accent: #E8A838;
  --color-info: #C0CCD8;
  --color-surface-dark: #0F1319;
  --color-surface-hover-dark: #1a212b;
}
/* ADR reference links injected by linkify-adrs.nu */
.adr-ref {
  color: var(--color-brand-primary);
  text-decoration: none;
  border-bottom: 1px solid rgba(232, 168, 56, 0.35);
  font-family: 'IBM Plex Mono', ui-monospace, Consolas, monospace;
  font-size: 0.875em;
}
.adr-ref:hover {
  color: var(--color-primary-hover);
  border-bottom-color: var(--color-brand-primary);
}

/* PRIMARY IS ONE COLOUR IN BOTH THEMES. The dark theme used to lighten it to #F0C265 — a
 * reasonable instinct (lift the hue off a dark canvas) that ended up giving the brand's own
 * button a different colour depending on the reader's OS setting. The amber sits on its own
 * filled surface, never on the page, so it needs no lift: the surface it must contrast with
 * travels with it. One primary, one brand, either theme. */
.dark {
  --color-brand-primary: #E8A838;
  --color-brand-secondary: #C0CCD8;
  --color-brand-accent: #E8A838;
  --color-primary: #E8A838;
  --color-primary-hover: #C88A20;
  --color-secondary: #C0CCD8;
  --color-secondary-hover: #a8b4c8;
  --color-accent: #E8A838;
  --color-info: #C0CCD8;
  --color-surface: #0F1319;
  --color-surface-hover: #1a212b;
  --color-text: #C0CCD8;
  --color-text-secondary: #8090A4;
}

/* ══════════════════════════════════════════════════════════════════════════
   ontoref document chrome (.doc-*) — port of outreach/web's hand-built page
   layout so the SSR site About matches it. Self-contained: defines the
   --onto-* token set and the .doc-* classes used by pages/about_project.j2.
   Lives here (custom.css is already in config/assets.ncl source_css) so the
   brand look ships with ZERO framework rebuild.
   ══════════════════════════════════════════════════════════════════════════ */
:root {
  --onto-amber: #e8a838;
  --onto-amber-dark: #c88a20;
  --onto-amber-light: #f0c265;
  --onto-silver: #c0ccd8;
  --onto-dark: #0f1319;
  --onto-gray: #8090a4;
  --onto-gray2: #a8b4c8;
  --onto-surface: #141c24;
  --onto-border: #1e2a38;
  --onto-amber-rgb: 232, 168, 56;
  --onto-silver-rgb: 192, 204, 216;

  /* Headings + the amber seam. Dark canvas: the wordmark silver itself (11.3:1 on
   * #0F1319), and the light amber the brand already reserves for dark surfaces. */
  --onto-heading: #c0ccd8;
  --onto-rule: #f0c265;

  /* The FRAMEWORK's font tokens, rebound to the manual. Restyling classes was not enough:
   * design-system.css and theme-default.css hand every token-driven component a font of
   * their own (--font-mono → ui-monospace…, --font-family-mono → JetBrains Mono). Leaving
   * them meant half the site asked for a face this file no longer loads and silently fell
   * back — the drift would have been invisible, which is the only kind that survives. */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Consolas, monospace;
  --font-family-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Code', monospace;
}
html.light,
[data-theme="light"] {
  --onto-dark: #f0f2f5;
  --onto-surface: #ffffff;
  --onto-border: #d1d9e0;
  --onto-silver: #1a2030;
  --onto-gray: #5a6a7c;
  --onto-gray2: #374151;

  /* Light canvas: the silver's deep sibling (13.2:1 on white) — same hue family as the
   * logo, legible where the logo's own value is not.
   * The rule takes the brand's DARK AMBER (#C88A20, declared in ontoref-brand.css for
   * hover/pressed), not the base #E8A838: on white the base amber sits at 2.1:1 and
   * reads as a washed-out smudge. Same brand, the member of it that survives this canvas. */
  --onto-heading: #26313f;
  --onto-rule: #c88a20;
}

/* Full-bleed dark canvas for the About body; the column centres inside it. */
.about-doc {
  background: var(--onto-dark);
  color: var(--onto-silver);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  border-radius: 0.75rem;
}
.doc-wrap {
  max-width: 52rem;
  margin: 0 auto;
  padding: 2.5rem 1.25rem 3.5rem;
}

.doc-head {
  border-bottom: 1px solid var(--onto-border);
  padding-bottom: 1.25rem;
  margin-bottom: 1.5rem;
}
.doc-eyebrow {
  font-family: 'IBM Plex Mono', Consolas, monospace;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--onto-amber);
  text-transform: uppercase;
  margin: 0 0 0.5rem;
}
.about-doc h1 {
  color: var(--onto-silver);
  font-size: 2.25rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0;
}
.doc-lead {
  color: var(--onto-gray2);
  font-size: 1.05rem;
  line-height: 1.7;
  margin: 0.85rem 0 0;
  max-width: 40rem;
}

.doc-toc {
  border: 1px solid var(--onto-border);
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
  margin: 1.5rem 0 2rem;
  background: var(--onto-surface);
}
.doc-toc p {
  font: 600 0.7rem/1 'IBM Plex Mono', monospace;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.55;
  margin: 0 0 0.5rem;
}
.doc-toc ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
}
.doc-toc a {
  font-size: 0.85rem;
  color: var(--onto-silver);
  text-decoration: none;
}
.doc-toc a:hover {
  color: var(--onto-amber);
}

.doc-section {
  margin: 2.25rem 0;
}
.doc-section > h2 {
  color: var(--onto-amber);
  font-size: 1.2rem;
  font-weight: 600;
  margin: 0 0 0.85rem;
  scroll-margin-top: 5rem;
}
.doc-section h3,
.doc-idx-h {
  color: var(--onto-silver);
  font-family: 'IBM Plex Mono', Consolas, monospace;
  font-size: 0.95rem;
  font-weight: 600;
  margin: 1.5rem 0 0.6rem;
}
.doc-idx-h .doc-count {
  color: var(--onto-gray);
  font-weight: 400;
}

.doc-prose {
  line-height: 1.75;
  color: var(--onto-gray2);
}
.doc-prose p {
  margin: 0 0 0.85rem;
}

.doc-mono {
  font-family: 'IBM Plex Mono', Consolas, monospace;
  color: var(--onto-gray);
}

/* Generic flat list (timeline, cards) */
.doc-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.doc-list > li {
  display: flex;
  gap: 0.75rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--onto-border);
  line-height: 1.6;
}
.doc-list > li:last-child {
  border-bottom: none;
}
.doc-list .doc-mono {
  flex-shrink: 0;
  white-space: nowrap;
}

/* Definition cards (axioms/tensions/practices) — block, not a wall. */
.doc-cards {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.75rem;
}
.doc-cards > li {
  border: 1px solid var(--onto-border);
  border-radius: 0.5rem;
  padding: 0.7rem 0.9rem;
  background: var(--onto-surface);
}
.doc-cards .doc-card-title {
  font-weight: 600;
  color: var(--onto-silver);
  margin: 0;
}
.doc-cards .doc-card-meta {
  font: 400 0.78rem/1.4 'IBM Plex Mono', monospace;
  color: var(--onto-gray);
}
.doc-cards .doc-card-body {
  color: var(--onto-gray2);
  font-size: 0.92rem;
  line-height: 1.55;
  margin: 0.35rem 0 0;
}

/* Index list (ADRs, catalog) — dense rows with mono code prefix. */
.doc-idx-list {
  list-style: none;
  padding: 0;
  margin: 0;
  columns: 2;
  column-gap: 1.5rem;
}
.doc-idx-list > li {
  break-inside: avoid;
  padding: 0.2rem 0;
}
.doc-idx-list a {
  color: var(--onto-silver);
  text-decoration: none;
  font-size: 0.9rem;
  line-height: 1.5;
}
.doc-idx-list a:hover {
  color: var(--onto-amber);
}
.doc-idx-list a .doc-mono {
  font-size: 0.82em;
}
@media (max-width: 640px) {
  .doc-idx-list {
    columns: 1;
  }
}

.doc-btn {
  display: inline-block;
  margin-top: 1rem;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--onto-border);
  border-radius: 0.45rem;
  color: var(--onto-silver);
  font-size: 0.88rem;
  text-decoration: none;
  background: var(--onto-surface);
  transition: border-color 0.15s, color 0.15s;
}
.doc-btn:hover {
  border-color: var(--onto-amber);
  color: var(--onto-amber);
}

.doc-graph {
  margin: 1rem auto;
  max-width: 24rem;
  border: 1px solid var(--onto-border);
  border-radius: 0.5rem;
  background: var(--onto-surface);
  padding: 0.5rem;
}
.doc-graph svg {
  width: 100%;
  height: auto;
  display: block;
}
/* The projected architecture diagram (gen-diagram.nu → /r/ontoref-diagram.svg) is
   wide; widen the graph card when it holds the diagram image rather than a mini-svg. */
.doc-graph:has(.about-diagram) {
  max-width: 62rem;
}
.about-diagram {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 0.375rem;
}

.doc-kv {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.doc-kv td {
  padding: 0.4rem 0.6rem;
  border-bottom: 1px solid var(--onto-border);
  vertical-align: top;
}
.doc-kv td:first-child {
  font-family: 'IBM Plex Mono', monospace;
  color: var(--onto-gray2);
  white-space: nowrap;
  width: 1%;
}

/* ── Tagline rotator (hero definition-phrases) ──────────────────────────────
   Mechanism only — phrases come from /r/taglines.json. Fade is the no-typed.js
   fallback; typed.js draws its own .typed-cursor. */
/* The manual assigns IBM Plex Sans 500 to exactly one thing — the tagline — and this is
   the tagline: the hero rotator draws the definition-phrases in the wordmark's own voice. */
[data-tagline-rotator] {
  display: inline;
  font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  font-weight: 500;
}
.tagline-rot {
  transition: opacity 0.35s ease;
  opacity: 0;
}
.tagline-rot.is-in {
  opacity: 1;
}
.typed-cursor {
  color: var(--onto-amber, #e8a838);
  animation: tagline-blink 1s infinite;
}
@keyframes tagline-blink {
  50% {
    opacity: 0;
  }
}
.tagline-focus {
  display: inline-block;
  margin-top: 0.4em;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.72em;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--onto-gray, #8090a4);
}
.tagline-focus:not(:empty)::before {
  content: '· ';
  color: var(--onto-amber, #e8a838);
}
@media (prefers-reduced-motion: reduce) {
  .tagline-rot {
    transition: none;
    opacity: 1;
  }
  .typed-cursor {
    animation: none;
  }
}
.tagline-doors {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  justify-content: center;
  margin-top: 1.2em;
}
.tagline-door {
  font: inherit;
  font-size: 0.8em;
  padding: 0.35em 0.9em;
  border-radius: 999px;
  border: 1px solid rgba(var(--onto-silver-rgb), 0.28);
  background: transparent;
  color: var(--onto-gray, #8090a4);
  cursor: pointer;
  transition:
    color 0.2s ease,
    border-color 0.2s ease,
    background 0.2s ease;
}
.tagline-door:hover {
  color: var(--onto-silver, #c0ccd8);
  border-color: rgba(var(--onto-silver-rgb), 0.55);
}
.tagline-door.is-active {
  color: var(--onto-dark, #0f1319);
  background: var(--onto-amber, #e8a838);
  border-color: var(--onto-amber, #e8a838);
}
.tagline-panels {
  margin-top: 1rem;
}
.tagline-panel {
  display: none;
  border: 1px solid rgba(var(--onto-silver-rgb), 0.22);
  border-radius: 12px;
  padding: 1rem 1.25rem;
  background: rgba(var(--onto-silver-rgb), 0.05);
}
.tagline-panel.is-open {
  display: block;
  animation: tagline-panel-in 0.3s ease;
}
@keyframes tagline-panel-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.tagline-panel-line {
  margin: 0 0 0.75rem;
  font-size: 1.05em;
  color: var(--onto-silver, #c0ccd8);
}
.tagline-panel-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
  align-items: center;
}
.tagline-panel-graph {
  font-size: 0.85em;
  color: var(--onto-amber, #e8a838);
  border-bottom: 1px solid rgba(var(--onto-amber-rgb), 0.4);
}
.tagline-panel-graph:hover {
  color: var(--onto-amber-light, #f0c265);
}
@media (prefers-reduced-motion: reduce) {
  .tagline-panel {
    animation: none;
  }
}
/* Header + footer logo — horizontal, 3rem (overrides framework h-8), nudged down. */
nav a.flex-shrink-0 img,
footer a.inline-block img {
  height: 3rem;
  width: auto;
  margin-top: 10px;
}

/* ──────────────────────────────────────────────────────────────────────────
   Brand mention: inline "ontoref" links to /about in brand amber.
   Emitted by scripts/build/linkify-site.nu over blog markdown.
   ────────────────────────────────────────────────────────────────────────── */
.brand-site {
  color: var(--onto-amber, #e8a838);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid rgba(var(--onto-amber-rgb, 232, 168, 56), 0.35);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.brand-site:hover,
.brand-site:focus-visible {
  color: var(--onto-amber-light, #f0c265);
  border-bottom-color: var(--onto-amber, #e8a838);
}

/* ── Per-post engagement (rating · comment · views) ───────────────────────── */
/* Layout lives here (not Tailwind utilities) so spacing is reliable regardless
   of the purged utility set. */
.pe-engagement {
  margin-top: 3rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--onto-border, #1e2a38);
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.pe-block {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
}
.pe-msg-block {
  justify-content: space-between;
}
.pe-cta-label {
  font-weight: 650;
  font-size: 0.95rem;
  color: var(--onto-silver, #c0ccd8); /* theme-aware: dark text in light, light in dark */
}
.pe-msg-copy {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  max-width: 34rem;
}
.pe-cta-sub {
  font-size: 0.85rem;
  color: var(--onto-gray2, #a8b4c8);
  line-height: 1.35;
}

/* Primary CTA button (comment, modal submit) */
.pe-cta-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.55rem 1.1rem;
  border: 0;
  border-radius: 0.6rem;
  background: var(--onto-amber, #e8a838);
  color: #1a1206; /* dark ink on amber — legible in both themes */
  font-weight: 650;
  font-size: 0.9rem;
  cursor: pointer;
  white-space: nowrap;
  transition: filter 0.15s ease, transform 0.1s ease;
}
.pe-cta-btn:hover { filter: brightness(1.08); }
.pe-cta-btn:active { transform: translateY(1px); }
.pe-cta-btn-block { width: 100%; justify-content: center; margin-top: 0.25rem; }

/* Stars */
.pe-rating { display: inline-flex; align-items: center; gap: 0.6rem; }
.pe-stars { display: inline-flex; gap: 0.1rem; }
.pe-star {
  background: none;
  border: 0;
  padding: 0 0.05rem;
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
  color: rgba(128, 128, 128, 0.42); /* empty — neutral in both themes */
  transition: color 0.12s ease, transform 0.12s ease;
}
.pe-star:hover { transform: scale(1.15); color: var(--onto-amber, #e8a838); }
.pe-star-on { color: var(--onto-amber, #e8a838); }        /* part of the average */
.pe-star-mine { color: var(--onto-amber-light, #f0c265); } /* the caller's own vote */
.pe-rating-meta { font-variant-numeric: tabular-nums; color: var(--onto-gray, #8090a4); font-size: 0.85rem; }
.pe-rating-hint { font-size: 0.8rem; color: var(--onto-gray, #8090a4); font-style: italic; }

/* Views line (subtle) */
.pe-views-line { font-size: 0.78rem; color: var(--onto-gray, #8090a4); }
.pe-views { font-variant-numeric: tabular-nums; font-weight: 600; }

/* Grid card rating badge (only when the post has votes) */
.pe-card-rating {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  color: var(--onto-amber, #e8a838);
  opacity: 0.9;
}

/* Comment modal */
.pe-modal-backdrop {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(0, 0, 0, 0.55);
  cursor: pointer;
}
.pe-modal-wrap {
  position: fixed; inset: 0; z-index: 85;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; pointer-events: none;
}
.pe-modal-card {
  position: relative;
  width: 100%; max-width: 30rem;
  background: var(--onto-surface, #141c24); /* white in light, dark in dark */
  color: var(--onto-silver, #c0ccd8);
  border: 1px solid var(--onto-border, #1e2a38);
  border-radius: 1rem;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
  padding: 1.5rem;
  pointer-events: auto;
}
.pe-modal-close {
  position: absolute; top: 0.6rem; right: 0.6rem;
  background: none; border: 0; cursor: pointer;
  color: var(--onto-gray, #8090a4);
  font-size: 1.1rem; line-height: 1; padding: 0.3rem;
}
.pe-modal-close:hover { color: var(--onto-silver, #c0ccd8); }
.pe-modal-title { font-size: 1.15rem; font-weight: 650; margin-bottom: 0.15rem; color: var(--onto-silver, #c0ccd8); }
.pe-modal-sub { font-size: 0.8rem; color: var(--onto-gray, #8090a4); margin-bottom: 0.75rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pe-modal-invite { font-size: 0.88rem; color: var(--onto-gray2, #a8b4c8); margin-bottom: 1rem; line-height: 1.4; }
.pe-msg-form { display: flex; flex-direction: column; gap: 0.75rem; }
.pe-field { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.85rem; color: var(--onto-gray2, #a8b4c8); }
.pe-field .ds-input,
.pe-field input,
.pe-field textarea {
  width: 100%;
  padding: 0.5rem 0.65rem;
  border-radius: 0.5rem;
  border: 1px solid var(--onto-border, #1e2a38);
  background: rgba(128, 128, 128, 0.1); /* subtle fill for contrast against the card */
  color: var(--onto-silver, #c0ccd8);
}
.pe-field input::placeholder,
.pe-field textarea::placeholder { color: var(--onto-gray, #8090a4); }
.pe-field input:focus,
.pe-field textarea:focus { outline: 2px solid var(--onto-amber, #e8a838); outline-offset: 0; }
.pe-consent {
  display: flex; align-items: flex-start; gap: 0.5rem;
  font-size: 0.82rem; color: var(--onto-gray2, #a8b4c8); line-height: 1.3; cursor: pointer;
}
.pe-consent input { margin-top: 0.15rem; }
.pe-msg-thanks { text-align: center; padding: 0.5rem 0; color: var(--onto-silver, #c0ccd8); }

/* Honeypot — kept in the DOM but invisible and out of the tab order. */
.pe-hp {
  position: absolute !important;
  left: -9999px !important;
  width: 1px; height: 1px; opacity: 0;
}

@media (max-width: 640px) {
  .pe-msg-block { flex-direction: column; align-items: flex-start; }
  .pe-cta-btn { width: 100%; justify-content: center; }
}
/* ── Newsletter subscribe CTA (nl-cta) — foot of content grids and posts.
   Config-driven (site.ncl [newsletter] → `newsletter` global → subscribe.j2);
   theme-aware via --onto-* tokens (flip under [data-theme="light"]). ───────── */
.nl-cta {
  margin: 2.5rem 0 0;
  padding: 2rem 1.5rem;
  border: 1px solid var(--onto-border);
  border-radius: 0.9rem;
  background: var(--onto-surface);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  text-align: center;
}
.nl-cta__title {
  color: var(--onto-silver);
  font-size: clamp(1.35rem, 3vw, 1.9rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.15;
  margin: 0;
}
.nl-cta__sub {
  color: var(--onto-gray);
  max-width: 46ch;
  margin: 0 auto;
  font-size: 0.98rem;
  line-height: 1.5;
}
.nl-cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
}
.nl-cta__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.62rem 1.25rem;
  border-radius: 0.6rem;
  font-weight: 650;
  font-size: 0.95rem;
  text-decoration: none;
  white-space: nowrap;
  transition: filter 0.15s ease, transform 0.1s ease, background 0.15s ease;
}
.nl-cta__btn--primary {
  background: var(--onto-amber, #e8a838);
  color: #1a1206;
  border: 1px solid transparent;
}
.nl-cta__btn--primary:hover { filter: brightness(1.06); transform: translateY(-1px); }
.nl-cta__btn--ghost {
  background: transparent;
  color: var(--onto-silver);
  border: 1px solid var(--onto-border);
}
.nl-cta__btn--ghost:hover { background: var(--onto-dark); border-color: var(--onto-gray); }
@media (max-width: 640px) {
  .nl-cta__actions { flex-direction: column; width: 100%; }
  .nl-cta__btn { width: 100%; justify-content: center; }
}

/* ── Newsletter subscribe lightbox (nl-modal) — listmonk form ─────────────── */
dialog.nl-modal {
  position: fixed;
  inset: 0;
  margin: auto;
  border: 0;
  padding: 0;
  background: transparent;
  max-width: 30rem;
  width: calc(100% - 2rem);
  height: fit-content;
  max-height: calc(100vh - 2rem);
  overflow: auto;
  color: var(--onto-silver);
}
.nl-modal__submit:disabled { opacity: 0.45; cursor: not-allowed; filter: none; transform: none; }
dialog.nl-modal::backdrop {
  background: rgba(4, 8, 12, 0.62);
  backdrop-filter: blur(3px);
}
.nl-modal__card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  background: var(--onto-surface);
  border: 1px solid var(--onto-border);
  border-radius: 0.9rem;
  padding: 1.75rem 1.5rem;
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.7);
  text-align: left;
}
.nl-modal__title {
  color: var(--onto-silver);
  font-size: 1.25rem;
  font-weight: 700;
  margin: 0 1.6rem 0 0;
  line-height: 1.2;
}
.nl-modal__close {
  position: absolute;
  top: 0.55rem;
  right: 0.7rem;
  padding: 0.1rem 0.4rem;
  background: transparent;
  border: 0;
  border-radius: 0.4rem;
  color: var(--onto-gray);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
.nl-modal__close:hover { color: var(--onto-silver); background: var(--onto-dark); }
.nl-field { display: flex; flex-direction: column; gap: 0.3rem; }
.nl-field__lbl { font-size: 0.8rem; font-weight: 600; color: var(--onto-gray); }
.nl-field input {
  width: 100%;
  padding: 0.6rem 0.75rem;
  background: var(--onto-dark);
  color: var(--onto-silver);
  border: 1px solid var(--onto-border);
  border-radius: 0.5rem;
  font-size: 0.95rem;
}
.nl-field input::placeholder { color: var(--onto-gray); opacity: 0.7; }
.nl-field input:focus {
  outline: 2px solid var(--onto-amber);
  outline-offset: 1px;
  border-color: var(--onto-amber);
}
.nl-consent {
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--onto-gray2);
}
.nl-consent input { margin-top: 0.2rem; flex: 0 0 auto; accent-color: var(--onto-amber); }
.nl-consent a { color: var(--onto-amber); }
.nl-modal__submit { margin-top: 0.4rem; width: 100%; justify-content: center; }

/* ══════════════════════════════════════════════════════════════════════════
   Long-form legal / language pages — privacy.j2, legal.j2, lenguaje.j2

   `ds-prose`, `ds-callout` and `ds-table` were WRITTEN INTO THOSE TEMPLATES AND NEVER
   DEFINED: no rule for any of the three existed in any stylesheet the site loads. A class
   that does not exist raises no error and leaves no gap — the markup simply renders
   unstyled, inheriting whatever colour the page hands down. That is why these three pages
   read badly in the dark and only there: unstyled text stops being legible exactly when the
   canvas stops being white.

   Every colour below resolves through a theme token, so both themes are answered at once
   and neither can drift away from the other.
   ══════════════════════════════════════════════════════════════════════════ */
.ds-prose {
  color: var(--color-neutral-700, #374151);
  line-height: 1.7;
  max-width: 68ch;
}
.dark .ds-prose { color: var(--color-neutral-300, #d1d5db); }

.ds-prose h2 {
  color: var(--color-neutral-900, #111827);
  font-weight: 600;
  font-size: 1.35rem;
  margin: 2.2rem 0 0.7rem;
}
.ds-prose h3 {
  color: var(--color-neutral-800, #1f2937);
  font-weight: 600;
  font-size: 1.08rem;
  margin: 1.5rem 0 0.5rem;
}
.dark .ds-prose h2 { color: #F2F5F8; }
.dark .ds-prose h3 { color: #C0CCD8; }

.ds-prose p  { margin: 0.7rem 0; }
.ds-prose ul { margin: 0.7rem 0; padding-left: 1.3rem; list-style: disc; }
.ds-prose li { margin: 0.35rem 0; }
.ds-prose strong { color: var(--color-neutral-900, #111827); font-weight: 600; }
.dark .ds-prose strong { color: #F2F5F8; }

.ds-prose a { color: var(--color-brand-primary); text-underline-offset: 2px; }
.ds-prose a:hover { color: var(--color-primary-hover); }

/* A set-apart aside (contact block, obligation, caveat). Tinted with the brand amber at low
 * alpha so it reads as "quoted" on either canvas without inventing a second surface colour. */
.ds-callout {
  border-left: 3px solid var(--color-brand-primary);
  background: rgba(232, 168, 56, 0.07);
  border-radius: 0.35rem;
  padding: 0.9rem 1.1rem;
  margin: 1.4rem 0;
}
.ds-callout p { margin: 0.3rem 0; }

.ds-table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.3rem 0;
  font-size: 0.94rem;
  color: var(--color-neutral-700, #374151);
}
.dark .ds-table { color: var(--color-neutral-300, #d1d5db); }
.ds-table th,
.ds-table td {
  border-bottom: 1px solid rgba(128, 144, 164, 0.28);
  padding: 0.55rem 0.7rem;
  text-align: left;
  vertical-align: top;
}
.ds-table th { color: var(--color-neutral-900, #111827); font-weight: 600; }
.dark .ds-table th { color: #F2F5F8; }
/* The table scrolls inside itself: a legal page must never scroll the page sideways. */
.ds-table-wrap { overflow-x: auto; }

/* ══════════════════════════════════════════════════════════════════════════
   Primary button / badge — one filled amber, one ink, both themes

   TWO defects met here:

   1. The base rule for the primary button exists only under the ATTRIBUTE selector
      (`[ds-btn-primary=""]`) — the CLASS `.ds-btn-primary`, which is what every template
      actually writes, has rules for :hover, :focus and :disabled but NO base. So the button
      never got its own background or text colour; it inherited them, and inheritance changes
      with the theme. That is the whole bug: not a colour chosen badly, a colour never chosen.

   2. Where a colour WAS chosen (the badge) it was white on #E8A838 — a contrast of 2.3:1,
      below the 4.5:1 floor. Amber is a LIGHT fill: it takes dark ink, not white. Black on this
      amber is 8.6:1. The dark theme's "black text" that looked like the bug was, in contrast
      terms, the only correct half of the pair.

   Ink is fixed, not theme-dependent, because the surface under it is fixed: the button carries
   its own amber wherever it lands. A filled control should not change its text colour with the
   page — it is not sitting on the page.
   ══════════════════════════════════════════════════════════════════════════ */
.ds-btn-primary,
.dark .ds-btn-primary,
.ds-badge-primary,
.dark .ds-badge-primary {
  background-color: var(--color-brand-primary);
  color: #15191F;
}

.ds-btn-primary:hover,
.dark .ds-btn-primary:hover,
.ds-badge-primary:hover,
.dark .ds-badge-primary:hover {
  background-color: var(--color-primary-hover);
  color: #15191F;
}

/* The class had no base rule at all, so it also lacked the box the attribute selector gives:
 * restate the shape, or a `class="ds-btn-primary"` renders as bare inline text with a fill. */
.ds-btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 0.375rem;
  padding: 0.5rem 1rem;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

/* Subscribe modal — result fragment (swapped in by /api/htmx/newsletter). It replaces the form, so
 * it carries its own close button: swap the form away without one and the reader is trapped in a
 * dialog whose only exit is the Escape key. */
.nl-modal__result {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1rem;
  text-align: left;
}
.nl-modal__result-msg { margin: 0; line-height: 1.6; }
.nl-modal__result--ok  { border-left: 3px solid #E8A838; }
.nl-modal__result--err { border-left: 3px solid #C0504D; }

/* ── Figuras pareadas por tema, para prosa ───────────────────────────────────
   Un cuerpo markdown no alcanza la lógica de tema de la plantilla: el nav usa
   `dark:hidden` / `hidden dark:block`, pero esas son utilidades que UnoCSS
   compila escaneando las PLANTILLAS, y el markdown del contenido no se escanea.
   Un post que las usara enviaría las dos imágenes visibles a la vez. Así que
   aquí van en CSS plano, colgando de la raíz de tema (`html.dark`), que es la
   misma que ya gobierna `.dark .card` y compañía. */
.only-light { display: block; }
.only-dark  { display: none; }
.dark .only-light { display: none; }
.dark .only-dark  { display: block; }
