/* The site's own brand sheet. It is declared in site/config/assets.ncl too, but that
   file is consumed by build.rs at COMPILE time: a thin site runs the shared image and
   compiles nothing, so its declaration there never reaches the served <head>. custom.css
   IS in the baked list, which makes this line the hook that actually applies your brand.
   COMPLETE: edit styles/website-brand.css, not this file. */
@import url('/styles/website-brand.css');

/* Brand typography, per the brand manual (opus-ontoref/assets/branding/index.html, generated
   from visual/style.ncl `typography`):
     · Literata 600       — the wordmark (already outlined into the logo SVG)
     · Literata 400–600   — headings, body and the tagline: a serif drawn for long reading,
                            because the book is the Work's archetype. --font-sans carries it
                            on purpose: it is the token the framework's body text reads.
     · JetBrains Mono     — code, ids, NCL
   The faces are SELF-HOSTED: the framework serves `font-src 'self' data:`, so a
   fonts.googleapis.com stylesheet is blocked outright. fonts-brand.css and
   /styles/fonts/*.woff2 are generated by `just sync-brand-fonts` from the justfile's
   font_src_url. @import must precede every style rule, so it stays at the top. */
@import url('/styles/fonts-brand.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: var(--font-sans);
  line-height: 1.6;
  color: var(--color-text);
  background-color: var(--color-surface);
}

/* 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 brand's ink and accent are not heading colours by assumption: --site-heading is
 * the ink of each theme (both >11:1), and the accent survives where it CAN be read — as
 * the h2 rule below. Headings take the brand's heading face, --site-heading-face, from
 * assets/branding/visual/style.ncl `typography.headings`. See --site-* 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(--site-heading, #64748b);
  font-family: var(--site-heading-face, inherit);
}

/* 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(--site-heading, #64748b);
  font-family: var(--site-heading-face, inherit);
}

/* 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(--site-rule, #2563eb);
}

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;
}

/* ── website brand tokens (mirror of website-brand.css) ───────────────────────
 * Kept here so the brand palette applies in source asset mode without requiring
 * a framework rebuild to pick up the new website-brand.css <link>. The canonical
 * copy is /styles/website-brand.css, wired in config/assets.ncl. */
:root {
}
/* ADR reference links injected by linkify-adrs.nu */
.adr-ref {
  color: var(--color-brand-primary);
  text-decoration: none;
  border-bottom: 1px solid rgba(var(--site-amber-rgb), 0.35);
  font-family: 'JetBrains 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 #60a5fa — 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 {
}

/* ══════════════════════════════════════════════════════════════════════════
   website document chrome (.doc-*) — port of outreach/web's hand-built page
   layout so the SSR site About matches it. Self-contained: defines the
   --site-* 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 {
  /* Opus's palette (assets/branding/visual/style.ncl), taken from the cover illustration:
   * earth grounds at night, parchment ink, the path's ochre as the accent. The token names
   * are the template's (`amber` is the seam's role, not a hue). */
  --site-amber: #D4A853;
  --site-amber-dark: #C8934A;
  --site-amber-light: #DDB870;
  --site-silver: #F0E3C0;
  --site-dark: #21130C;
  --site-gray: #C8A870;
  --site-gray2: #D9C9A6;
  --site-surface: #2C1810;
  --site-border: #4A3226;
  --site-amber-rgb: 212, 168, 83;
  --site-silver-rgb: 240, 227, 192;

  /* Headings take the parchment ink (14.15:1 on #21130C); the seam takes the ochre. */
  --site-heading: #F0E3C0;
  --site-rule: #D4A853;
  --site-heading-face: 'Literata', Georgia, serif;

  /* 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: 'Literata', Georgia, 'Times New Roman', serif;
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Consolas, monospace;
  --font-family-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', monospace;
}
html.light,
[data-theme="light"] {
  --site-dark: #F5EDD8;
  --site-surface: #FAF5E8;
  --site-border: #D9C9A6;
  --site-silver: #2C1810;
  --site-gray: #6E4C1D;
  --site-gray2: #4A3226;

  /* By day the accent is the forest's sage, #4A6741 (5.44:1 on parchment); the ochre
   * measures 1.89 there and stays a night colour. */
  --site-amber: #4A6741;
  --site-amber-dark: #3A5233;
  --site-amber-light: #5E7F54;
  --site-amber-rgb: 74, 103, 65;
  --site-heading: #2C1810;
  --site-rule: #4A6741;
}

/* Full-bleed dark canvas for the About body; the column centres inside it. */
.about-doc {
  background: var(--site-dark);
  color: var(--site-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(--site-border);
  padding-bottom: 1.25rem;
  margin-bottom: 1.5rem;
}
.doc-eyebrow {
  font-family: 'JetBrains Mono', Consolas, monospace;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--site-amber);
  text-transform: uppercase;
  margin: 0 0 0.5rem;
}
.about-doc h1 {
  color: var(--site-silver);
  font-size: 2.25rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0;
}
.doc-lead {
  color: var(--site-gray2);
  font-size: 1.05rem;
  line-height: 1.7;
  margin: 0.85rem 0 0;
  max-width: 40rem;
}

.doc-toc {
  border: 1px solid var(--site-border);
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
  margin: 1.5rem 0 2rem;
  background: var(--site-surface);
}
.doc-toc p {
  font: 600 0.7rem/1 'JetBrains 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(--site-silver);
  text-decoration: none;
}
.doc-toc a:hover {
  color: var(--site-amber);
}

.doc-section {
  margin: 2.25rem 0;
}
.doc-section > h2 {
  color: var(--site-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(--site-silver);
  font-family: 'JetBrains Mono', Consolas, monospace;
  font-size: 0.95rem;
  font-weight: 600;
  margin: 1.5rem 0 0.6rem;
}
.doc-idx-h .doc-count {
  color: var(--site-gray);
  font-weight: 400;
}

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

.doc-mono {
  font-family: 'JetBrains Mono', Consolas, monospace;
  color: var(--site-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(--site-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(--site-border);
  border-radius: 0.5rem;
  padding: 0.7rem 0.9rem;
  background: var(--site-surface);
}
.doc-cards .doc-card-title {
  font-weight: 600;
  color: var(--site-silver);
  margin: 0;
}
.doc-cards .doc-card-meta {
  font: 400 0.78rem/1.4 'JetBrains Mono', monospace;
  color: var(--site-gray);
}
.doc-cards .doc-card-body {
  color: var(--site-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(--site-silver);
  text-decoration: none;
  font-size: 0.9rem;
  line-height: 1.5;
}
.doc-idx-list a:hover {
  color: var(--site-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(--site-border);
  border-radius: 0.45rem;
  color: var(--site-silver);
  font-size: 0.88rem;
  text-decoration: none;
  background: var(--site-surface);
  transition: border-color 0.15s, color 0.15s;
}
.doc-btn:hover {
  border-color: var(--site-amber);
  color: var(--site-amber);
}

.doc-graph {
  margin: 1rem auto;
  max-width: 24rem;
  border: 1px solid var(--site-border);
  border-radius: 0.5rem;
  background: var(--site-surface);
  padding: 0.5rem;
}
.doc-graph svg {
  width: 100%;
  height: auto;
  display: block;
}
/* The projected architecture diagram (gen-diagram.nu → /r/website-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(--site-border);
  vertical-align: top;
}
.doc-kv td:first-child {
  font-family: 'JetBrains Mono', monospace;
  color: var(--site-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 tagline takes the wordmark's face, Literata 500: the hero rotator draws the
   definition-phrases in the Work's own voice. */
[data-tagline-rotator] {
  display: inline;
  font-family: 'Literata', -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(--site-amber, #2563eb);
  animation: tagline-blink 1s infinite;
}
@keyframes tagline-blink {
  50% {
    opacity: 0;
  }
}
.tagline-focus {
  display: inline-block;
  margin-top: 0.4em;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.72em;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--site-gray, #64748b);
}
.tagline-focus:not(:empty)::before {
  content: '· ';
  color: var(--site-amber, #2563eb);
}
@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(--site-silver-rgb), 0.28);
  background: transparent;
  color: var(--site-gray, #64748b);
  cursor: pointer;
  transition:
    color 0.2s ease,
    border-color 0.2s ease,
    background 0.2s ease;
}
.tagline-door:hover {
  color: var(--site-silver, #64748b);
  border-color: rgba(var(--site-silver-rgb), 0.55);
}
.tagline-door.is-active {
  color: var(--site-dark, #0f172a);
  background: var(--site-amber, #2563eb);
  border-color: var(--site-amber, #2563eb);
}
.tagline-panels {
  margin-top: 1rem;
}
.tagline-panel {
  display: none;
  border: 1px solid rgba(var(--site-silver-rgb), 0.22);
  border-radius: 12px;
  padding: 1rem 1.25rem;
  background: rgba(var(--site-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(--site-silver, #64748b);
}
.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(--site-amber, #2563eb);
  border-bottom: 1px solid rgba(var(--site-amber-rgb), 0.4);
}
.tagline-panel-graph:hover {
  color: var(--site-amber-light, #60a5fa);
}
@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 "website" links to /about in brand amber.
   Emitted by scripts/build/linkify-site.nu over blog markdown.
   ────────────────────────────────────────────────────────────────────────── */
.brand-site {
  color: var(--site-amber, #2563eb);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid rgba(var(--site-amber-rgb, 37, 99, 235), 0.35);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.brand-site:hover,
.brand-site:focus-visible {
  color: var(--site-amber-light, #60a5fa);
  border-bottom-color: var(--site-amber, #2563eb);
}

/* ── 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(--site-border, #334155);
  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(--site-silver, #64748b); /* 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(--site-gray2, #94a3b8);
  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(--site-amber, #2563eb);
  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(--site-amber, #2563eb); }
.pe-star-on { color: var(--site-amber, #2563eb); }        /* part of the average */
.pe-star-mine { color: var(--site-amber-light, #60a5fa); } /* the caller's own vote */
.pe-rating-meta { font-variant-numeric: tabular-nums; color: var(--site-gray, #64748b); font-size: 0.85rem; }
.pe-rating-hint { font-size: 0.8rem; color: var(--site-gray, #64748b); font-style: italic; }

/* Views line (subtle) */
.pe-views-line { font-size: 0.78rem; color: var(--site-gray, #64748b); }
.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(--site-amber, #2563eb);
  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(--site-surface, #1e293b); /* white in light, dark in dark */
  color: var(--site-silver, #64748b);
  border: 1px solid var(--site-border, #334155);
  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(--site-gray, #64748b);
  font-size: 1.1rem; line-height: 1; padding: 0.3rem;
}
.pe-modal-close:hover { color: var(--site-silver, #64748b); }
.pe-modal-title { font-size: 1.15rem; font-weight: 650; margin-bottom: 0.15rem; color: var(--site-silver, #64748b); }
.pe-modal-sub { font-size: 0.8rem; color: var(--site-gray, #64748b); margin-bottom: 0.75rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pe-modal-invite { font-size: 0.88rem; color: var(--site-gray2, #94a3b8); 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(--site-gray2, #94a3b8); }
.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(--site-border, #334155);
  background: rgba(128, 128, 128, 0.1); /* subtle fill for contrast against the card */
  color: var(--site-silver, #64748b);
}
.pe-field input::placeholder,
.pe-field textarea::placeholder { color: var(--site-gray, #64748b); }
.pe-field input:focus,
.pe-field textarea:focus { outline: 2px solid var(--site-amber, #2563eb); outline-offset: 0; }
.pe-consent {
  display: flex; align-items: flex-start; gap: 0.5rem;
  font-size: 0.82rem; color: var(--site-gray2, #94a3b8); 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(--site-silver, #64748b); }

/* 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 --site-* tokens (flip under [data-theme="light"]). ───────── */
.nl-cta {
  margin: 2.5rem 0 0;
  padding: 2rem 1.5rem;
  border: 1px solid var(--site-border);
  border-radius: 0.9rem;
  background: var(--site-surface);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  text-align: center;
}
.nl-cta__title {
  color: var(--site-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(--site-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(--site-amber, #2563eb);
  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(--site-silver);
  border: 1px solid var(--site-border);
}
.nl-cta__btn--ghost:hover { background: var(--site-dark); border-color: var(--site-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(--site-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(--site-surface);
  border: 1px solid var(--site-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(--site-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(--site-gray);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
.nl-modal__close:hover { color: var(--site-silver); background: var(--site-dark); }
.nl-field { display: flex; flex-direction: column; gap: 0.3rem; }
.nl-field__lbl { font-size: 0.8rem; font-weight: 600; color: var(--site-gray); }
.nl-field input {
  width: 100%;
  padding: 0.6rem 0.75rem;
  background: var(--site-dark);
  color: var(--site-silver);
  border: 1px solid var(--site-border);
  border-radius: 0.5rem;
  font-size: 0.95rem;
}
.nl-field input::placeholder { color: var(--site-gray); opacity: 0.7; }
.nl-field input:focus {
  outline: 2px solid var(--site-amber);
  outline-offset: 1px;
  border-color: var(--site-amber);
}
.nl-consent {
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--site-gray2);
}
.nl-consent input { margin-top: 0.2rem; flex: 0 0 auto; accent-color: var(--site-amber); }
.nl-consent a { color: var(--site-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: #f8fafc; }
.dark .ds-prose h3 { color: #64748b; }

.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: #f8fafc; }

.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(var(--site-amber-rgb), 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: #f8fafc; }
/* 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 #2563eb — 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: #111827;
}

.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: #111827;
}

/* 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 var(--site-amber); }
.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; }
