/*
 * Base styles for the Addable Labs website — Direction A, Signal (plan D-01).
 *
 * Ported from the Signal preview (previews/assets/signal.css): a product-led,
 * crisp layout, mono headings with a readable system-sans body, elevated
 * surfaces with 3:1 borders, one vivid green accent, orange only for status,
 * restrained staggered motion. Every colour comes from tokens.css — no colour
 * literal may appear in this file (enforced by `pnpm check:contrast`). The
 * components and their states are documented in docs/identity.md.
 *
 * Sections: base · type helpers · buttons, links, chips · header and toggle ·
 * hero and console · sections and cards (the D-14 balance rule) · trust,
 * writing, contact · footer · page hero, about, article, blog, 404 ·
 * motion, reduced motion and print.
 */

/* Base ------------------------------------------------------------------ */

/* color-scheme is owned by tokens.css (dark on :root, light only under the
   toggle's data-theme switch, D-13); nothing here may re-enable the OS scheme. */
html {
  box-sizing: border-box;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

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

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1 0 auto;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-mono);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 var(--space-3);
  text-wrap: balance;
}

h1 {
  font-size: var(--text-display);
  font-weight: 300;
  letter-spacing: -0.035em;
  line-height: 1.05;
}

h2 {
  font-size: var(--text-2xl);
}

h3 {
  font-size: var(--text-xl);
}

h4 {
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-muted);
}

p,
ul,
ol,
dl {
  margin: 0 0 var(--space-3);
}

a {
  color: var(--color-accent-text);
  text-decoration: none;
}

a:hover {
  color: var(--color-accent-strong);
}

svg {
  display: block;
}

img {
  max-width: 100%;
  height: auto;
}

/* Focus (REQ-022): one visible ring on every focusable element, drawn 3 px
   outside the element so it stays visible on the green button too (plan
   Risks: on dark the ring colour equals the button surface). No rule in this
   file removes the outline. */
:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Container (REQ-019): stops growing at 76 rem so 4K does not stretch lines;
   fluid gutters down to 1 rem at 360 px. */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.page-head {
  padding-top: var(--space-5);
}

/* Skip link: first focusable element, visible only on focus (REQ-022). */
.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 20;
  padding: var(--space-2) var(--space-3);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-mono);
  font-weight: 500;
  border-radius: var(--radius-sm);
}

.skip-link:not(:focus) {
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* Type helpers ---------------------------------------------------------- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent-text);
  margin-bottom: var(--space-3);
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-accent);
  box-shadow: 0 0 0 4px var(--color-glow);
}

.lead {
  font-size: var(--text-lg);
  color: var(--color-text-muted);
  max-width: 40rem;
}

.section-lead {
  color: var(--color-text-muted);
  max-width: 44rem;
  font-size: var(--text-lg);
}

/* Wordmark (D-05): `addable` + dimmed `labs` in Mono Medium, live text. */
.wordmark {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: -0.02em;
  font-size: 1.1rem;
  color: var(--color-text);
}

.wordmark-dim {
  color: var(--color-text-muted);
  font-weight: 300;
}

.arrow {
  display: inline-block;
  margin-inline-start: 0.4em;
  transition: transform var(--dur) var(--ease);
}

a:hover .arrow,
.button:hover .arrow {
  transform: translateX(3px);
}

.plus {
  color: var(--color-accent-text);
  font-weight: 500;
  margin-inline-end: 0.5em;
}

/* Buttons, links, chips (REQ-017, REQ-019: 44 px targets) ----------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: -0.01em;
  border-radius: 999px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* Primary: accent surface, dark text, a 1 px accent-strong edge that gives
   the button its boundary on light (plan Risks: 1.32:1 against the page
   otherwise) and equals the surface on dark. */
.button-primary {
  background: var(--color-accent);
  color: var(--color-on-accent);
  border-color: var(--color-accent-strong);
  box-shadow: 0 0 0 0 var(--color-glow);
}

.button-primary:hover {
  color: var(--color-on-accent);
  background: var(--color-accent-strong);
  box-shadow: 0 0 0 6px var(--color-glow);
  transform: translateY(-2px);
}

.button-primary:active {
  transform: translateY(0);
  filter: saturate(0.85) brightness(0.94);
  box-shadow: 0 0 0 2px var(--color-glow);
}

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

.button-secondary:hover {
  color: var(--color-text);
  border-color: var(--color-accent-text);
  background: var(--color-surface);
  transform: translateY(-1px);
}

.button-secondary:active {
  transform: translateY(0);
  background: var(--color-surface-2);
}

/* The header's small CTA keeps the 44 px target (REQ-019); only the
   horizontal padding shrinks. */
.button-sm {
  padding-inline: var(--space-3);
}

.text-link {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.text-link:hover {
  text-decoration: underline;
  text-underline-offset: 0.3em;
}

/* Chips: pill, mono, status dot. Kinds follow the data's status keys
   (chip-<status>) plus the category and draft chips. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  min-height: 28px;
  padding: 0 0.7em;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.02em;
  line-height: 1.2;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  color: var(--color-text-muted);
  background: transparent;
  white-space: nowrap;
}

.chip-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.chip-open-source-mit {
  color: var(--color-accent-text);
  border-color: var(--color-accent-text);
}

.chip-in-development,
.chip-draft {
  color: var(--color-secondary-text);
  border-color: var(--color-secondary-text);
}

.chip-experiment,
.chip-private {
  color: var(--color-text-muted);
  border-color: var(--color-border);
}

/* Category and RSS chips are links: 44 px tall targets with the pill drawn
   inside; a category page marks its own chip with aria-current. */
.chip-cat,
.chip-rss {
  min-height: 44px;
  padding-block: 0;
  color: var(--color-accent-text);
  border-color: transparent;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--color-border);
  padding-inline: 0.9em;
}

a.chip:hover {
  border-color: var(--color-accent-text);
  box-shadow: inset 0 0 0 1px var(--color-accent-text);
  color: var(--color-accent-strong);
}

a.chip:active {
  background: var(--color-surface-2);
}

.chip-cat[aria-current="page"] {
  color: var(--color-text);
  box-shadow: inset 0 0 0 1px var(--color-accent-text);
  background: var(--color-surface-2);
}

/* Header and toggle (REQ-014, REQ-019, REQ-022) --------------------------- */

/* Sticky and blurred: the translucent page colour is a ::before layer at
   82 % opacity (no colour literal, no mixing) over the backdrop blur. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  isolation: isolate;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--color-hairline);
}

.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-bg);
  opacity: 0.82;
  pointer-events: none;
}

/* The row wraps instead of hiding the navigation: below 40 rem the brand and
   the controls share the first line and the nav takes a full second line, so
   Home / About / Blog stay reachable by keyboard on every width. */
.header-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  min-height: 64px;
  padding-block: var(--space-1);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-text);
  min-height: 44px;
}

.brand .mark {
  color: var(--color-accent-text);
  width: 28px;
  height: 28px;
  transition: color var(--dur) var(--ease);
}

.brand:hover .mark {
  color: var(--color-accent-strong);
}

.site-nav {
  margin-inline-start: auto;
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  border-radius: 999px;
  transition: color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

/* Accent underline on hover and on the current page (the current page keeps
   it without hover and reads in the full text colour). */
.site-nav a:hover {
  color: var(--color-text);
  box-shadow: inset 0 -2px 0 var(--color-accent);
  border-radius: 0;
}

.site-nav a[aria-current="page"] {
  color: var(--color-text);
  box-shadow: inset 0 -2px 0 var(--color-accent);
  border-radius: 0;
}

.header-controls {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.header-controls .button-sm {
  display: none;
}

@media (max-width: 40rem) {
  .site-nav {
    order: 3;
    width: 100%;
    margin-inline-start: 0;
  }

  .site-nav a {
    padding-inline: var(--space-2);
  }

  .header-controls {
    margin-inline-start: auto;
  }
}

@media (min-width: 64rem) {
  .header-controls .button-sm {
    display: inline-flex;
  }
}

.lang-switch,
.lang-switch-footer {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.lang-switch:hover,
.lang-switch-footer:hover {
  color: var(--color-text);
}

/* Appearance toggle (REQ-007 as amended by A-03/A-04): a 44 px button; the
   moon shows on dark (the default), the sun on light. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  min-width: 44px;
  padding: 0 var(--space-2);
  background: transparent;
  color: var(--color-text-muted);
  border: 1px solid transparent;
  border-radius: 999px;
  cursor: pointer;
  font: inherit;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.theme-toggle:hover {
  color: var(--color-text);
  border-color: var(--color-border);
}

.theme-toggle:active {
  background: var(--color-surface-2);
}

/* Without JavaScript the site is dark and no inert control is shown (D-13). */
html:not(.js) .theme-toggle {
  display: none;
}

.theme-toggle-sun {
  display: none;
}

:root[data-theme="light"] .theme-toggle-sun {
  display: block;
}

:root[data-theme="light"] .theme-toggle-moon {
  display: none;
}

.theme-toggle-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* Hero and console (REQ-009, REQ-017) ------------------------------------- */

.hero {
  position: relative;
  overflow: hidden;
  padding-block: var(--space-6) var(--space-7);
  isolation: isolate;
}

.hero-glow {
  position: absolute;
  inset: -20% -10% auto auto;
  width: 60vw;
  max-width: 56rem;
  aspect-ratio: 1;
  z-index: -1;
  background: radial-gradient(circle at 60% 40%, var(--color-glow), transparent 60%);
  pointer-events: none;
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.35;
  background-image: linear-gradient(var(--color-hairline) 1px, transparent 1px), linear-gradient(90deg, var(--color-hairline) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse at 50% 0%, currentcolor 30%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse at 50% 0%, currentcolor 30%, transparent 75%);
}

.hero-grid {
  display: grid;
  gap: var(--space-6);
  align-items: center;
}

/* Copy beside the console from 70 rem: below that the console spans the
   full width, so its four columns and one-line log texts stay readable
   (at 64 rem the 5fr column left the text 88 px). */
@media (min-width: 70rem) {
  .hero-grid {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    gap: var(--space-7);
  }
}

.hero-copy h1 {
  max-width: 20ch;
  margin-bottom: var(--space-4);
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  margin-block: var(--space-4);
}

.proof-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  padding: 0;
  list-style: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.console {
  margin: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 30px 80px -30px var(--color-glow), 0 0 0 1px var(--color-hairline) inset;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  overflow: hidden;
}

.console-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-hairline);
  color: var(--color-text-muted);
}

.console-dots {
  display: inline-flex;
  gap: 6px;
}

.console-dots i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-hairline);
  border: 1px solid var(--color-border);
}

/* The list owns the four columns (time | stage | text | state) and every line
   is a subgrid row, so the columns line up down the whole console (the
   founder's note, ce5f6cc). Without subgrid a line falls back to its own
   grid with the same tracks. */
.console-log {
  margin: 0;
  padding: var(--space-3);
  list-style: none;
  display: grid;
  grid-template-columns: max-content max-content minmax(0, 1fr) max-content;
  gap: var(--space-2) var(--space-3);
}

.log-line {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: max-content max-content minmax(0, 1fr) max-content;
  gap: var(--space-3);
  align-items: baseline;
}

@supports (grid-template-columns: subgrid) {
  .log-line {
    grid-template-columns: subgrid;
  }
}

.log-time {
  color: var(--color-text-muted);
}

.log-stage {
  color: var(--color-text);
  font-weight: 500;
}

.log-text {
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

.log-ok {
  color: var(--color-accent-text);
  font-weight: 500;
}

.log-wait {
  color: var(--color-secondary-text);
  font-weight: 500;
}

/* The block sits on the baseline at cap height (JetBrains Mono ~0.73em), so it
   lines up with the state word instead of hanging into the descender space
   (founder feedback, 2026-09-21). An empty inline-block's baseline is its
   bottom margin edge. */
.caret {
  display: inline-block;
  width: 0.55em;
  height: 0.75em;
  margin-inline-start: 0.3em;
  background: currentColor;
  vertical-align: baseline;
  animation: blink 1.1s steps(2, start) infinite;
}

@media (max-width: 30rem) {
  .console-log,
  .log-line {
    grid-template-columns: max-content minmax(0, 1fr) max-content;
  }

  @supports (grid-template-columns: subgrid) {
    .log-line {
      grid-template-columns: subgrid;
    }
  }

  .log-time {
    display: none;
  }
}

/* Sections and cards (REQ-010, REQ-011, REQ-017 as amended by A-02) -------- */

.section {
  padding-block: var(--space-7);
  border-top: 1px solid var(--color-hairline);
}

.section-alt {
  background: var(--color-surface-2);
}

.section-head {
  max-width: 50rem;
  margin-bottom: var(--space-5);
}

.section-head h2 {
  margin-bottom: var(--space-2);
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-4);
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

/* The balance rule (D-14, A-02, AC-30). The grids declare only their column
   tracks and leave rows implicit; every card is a grid that spans N rows and
   takes its row tracks from the parent (subgrid), so the cards in one row
   share each track: equal heights, aligned titles, texts and headings, and
   the action row in the last track, pinned to the bottom. The services grid
   gains its columns at 40 and 64 rem, where a mono title fits on one line in
   both languages; the apps grid at 46 and 70 rem, where a card is 20.5 rem
   wide — room for the longest status chip, the founder-confirmed
   "privat · API-nycklar på förfrågan" (33 characters, 275 px), on one line
   (decomposition decision 4). */
.service-grid {
  display: grid;
  gap: var(--space-4);
}

.app-grid {
  display: grid;
  gap: var(--space-3);
  margin: 0 0 var(--space-3);
}

@media (min-width: 40rem) {
  .service-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 46rem) {
  .app-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 64rem) {
  .service-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 70rem) {
  .app-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
  }
}

/* number | title | text | "What you get" heading | list | action */
.service {
  display: grid;
  grid-row: span 6;
  grid-template-rows: subgrid;
  row-gap: 0;
  align-content: start;
}

/* theme caption | chip row | name | summary | action */
.app {
  display: grid;
  grid-row: span 5;
  grid-template-rows: subgrid;
  row-gap: 0;
  align-content: start;
}

.service > *,
.app > * {
  min-width: 0;
  overflow-wrap: anywhere;
}

.service > :last-child,
.app > :last-child {
  align-self: end;
}

@supports not (grid-template-rows: subgrid) {
  .service,
  .app {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .service-grid,
  .app-grid {
    align-items: stretch;
  }

  .service > :last-child,
  .app > :last-child {
    margin-top: auto;
  }
}

.service .num {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-accent-text);
  letter-spacing: 0.1em;
  margin-bottom: var(--space-3);
}

/* One-line titles: 1.25rem mono, no wrapping inside words (the copy bands in
   tests/apps.test.mjs keep the longest title within the narrowest track). */
.service h3,
.app-name {
  font-size: 1.25rem;
  margin: 0 0 var(--space-2);
  overflow-wrap: normal;
  text-wrap: nowrap;
}

.service p {
  color: var(--color-text-muted);
}

.gets-heading {
  margin-top: var(--space-3);
  margin-bottom: var(--space-2);
}

.gets {
  padding: 0;
  list-style: none;
  margin-bottom: var(--space-3);
}

.gets li {
  padding-inline-start: 1.4em;
  position: relative;
  font-size: var(--text-sm);
  margin-bottom: var(--space-1);
}

.gets li::before {
  content: "+";
  position: absolute;
  left: 0;
  color: var(--color-accent-text);
  font-family: var(--font-mono);
  font-weight: 500;
}

/* App card: the theme caption is the first track — a muted uppercase mono
   line like the services' number — and the chip row below it holds only the
   status chip, so the row is one line at every width with the chip's text
   intact: the widest chip fits the narrowest card the grid allows (the
   breakpoints above), and nothing shares its line. */
.app-theme {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-top {
  display: flex;
  align-items: center;
  margin-bottom: var(--space-3);
  white-space: nowrap;
}

.app-text {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  margin: 0 0 var(--space-3);
}

/* The action row is the same element in every card: a 44 px repository link
   or the mono private note, above a dashed rule. */
.app-action,
.app-private {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin: 0;
  padding-top: var(--space-2);
  border-top: 1px dashed var(--color-border);
  width: 100%;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.app-action {
  font-weight: 500;
  color: var(--color-accent-text);
}

.app-action:hover {
  color: var(--color-accent-strong);
  text-decoration: underline;
  text-underline-offset: 0.3em;
}

.app-private {
  color: var(--color-text-muted);
}

.app-linked:hover {
  transform: translateY(-3px);
  border-color: var(--color-accent-text);
  box-shadow: 0 16px 40px -24px var(--color-glow);
}

.app:not(.app-linked) {
  background: transparent;
  border-style: solid;
}

/* Trust, writing, contact (REQ-012 … REQ-014) ------------------------------ */

.trust-grid {
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 64rem) {
  .trust-grid {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: var(--space-7);
  }
}

.trust-copy h2 {
  max-width: 18ch;
}

.trust-points {
  display: grid;
  gap: var(--space-4);
  margin: 0;
}

@media (min-width: 40rem) {
  .trust-points {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.trust-point {
  padding-top: var(--space-3);
  border-top: 2px solid var(--color-accent);
}

.trust-point dt {
  font-family: var(--font-mono);
  font-weight: 500;
  margin-bottom: var(--space-1);
}

.trust-point dd {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* The "Proof, not claims" text is the p.factory-note the content gate hooks
   (REQ-012): a paragraph inside its dd, without the paragraph margin. */
.trust-point .factory-note {
  margin: 0;
}

.post-list {
  padding: 0;
  margin: 0 0 var(--space-3);
  list-style: none;
  display: grid;
  gap: var(--space-3);
}

@media (min-width: 48rem) {
  .post-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
  }
}

.post {
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.post:hover {
  border-color: var(--color-accent-text);
  transform: translateY(-2px);
}

.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

.post-title {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}

.post-title a {
  color: var(--color-text);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.post-title a:hover {
  color: var(--color-accent-text);
}

.post-text {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  margin: 0;
}

/* "All articles" under the landing's post grid (REQ-013). */
.writing-all {
  margin: var(--space-3) 0 0;
}

.contact-band {
  display: grid;
  gap: var(--space-4);
  align-items: center;
  padding: var(--space-5) var(--gutter);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--color-glow), transparent 60%), var(--color-surface);
}

@media (min-width: 64rem) {
  .contact-band {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  }

  .contact-band .cta-row {
    justify-content: flex-end;
  }
}

.contact-band h2 {
  margin-bottom: var(--space-2);
}

.contact-band .section-lead {
  margin: 0;
  font-size: var(--text-md);
}

/* Footer (REQ-014, AC-15): brand + factory line, Contact, Site ------------- */

.site-footer {
  border-top: 1px solid var(--color-hairline);
  padding-block: var(--space-6);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.footer-grid {
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 48rem) {
  .footer-grid {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  }
}

.footer-brand .mark {
  width: 24px;
  height: 24px;
}

.footer-brand p {
  max-width: 32ch;
  margin-top: var(--space-2);
}

/* Aktiebolagslagen 28 kap. 5 §: the company name, its organisation number and
   its registered seat, published on the website. No 32 ch measure here — the
   three facts read as one line wherever the column is wide enough, and only a
   narrow column wraps them. */
.footer-brand .company-line {
  max-width: none;
}

.company-line span {
  white-space: nowrap;
}

.footer-heading {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

.footer-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-1);
}

.footer-list a {
  display: inline-flex;
  align-items: center;
  min-width: 44px;
  min-height: 44px;
  color: var(--color-text);
}

.footer-list a:hover {
  color: var(--color-accent-text);
}

.footer-list .lang-switch-footer {
  padding: 0;
}

.footer-controls .theme-toggle {
  margin-inline-start: calc(-1 * var(--space-2));
  color: var(--color-text);
}

/* Page hero, about, article, blog, 404 (REQ-016) --------------------------- */

/* Inner-page title band (partials/page-hero.njk): the landing hero's glow and
   grid behind an eyebrow, the h1 and a lead; the 404 page centres it. */
.page-hero {
  padding-block: var(--space-6) var(--space-5);
  border-bottom: 1px solid var(--color-hairline);
}

.page-hero-copy h1 {
  max-width: 22ch;
  margin-bottom: var(--space-3);
}

.page-hero-copy .lead {
  margin-bottom: var(--space-3);
}

.page-hero-copy .cta-row {
  margin-block: var(--space-3) 0;
}

.page-hero-centered {
  padding-block: var(--space-7);
  text-align: center;
}

.page-hero-centered .page-hero-copy > *,
.page-hero-centered .not-found-sv > * {
  margin-inline: auto;
}

.page-hero-centered .cta-row {
  justify-content: center;
}

.page-hero-centered .hero-glow {
  inset: -30% auto auto 50%;
  translate: -50% 0;
}

.not-found-sv {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-hairline);
}

.not-found-sv h2 {
  font-size: var(--text-2xl);
}

/* About page: the facts line (founding month, country) in mono under the
   mission heading; the approach section places its note under the heading
   on wide screens while the four points span both rows (DOM order: heading,
   points, note). */
.facts-line {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin: 0;
  text-wrap: balance;
}

.about-mission .section-lead {
  margin: 0;
}

.about-approach .trust-points {
  margin: 0;
}

@media (min-width: 64rem) {
  .about-approach .trust-grid {
    grid-template-rows: auto 1fr;
    row-gap: var(--space-5);
  }

  .about-approach .trust-points {
    grid-column: 2;
    grid-row: 1 / span 2;
  }

  .about-approach .note {
    grid-column: 1;
    grid-row: 2;
    align-self: start;
  }
}

/* Labelled note card (the about page's article card; A-04's GRC-fork note
   until the founder replaced it): eyebrow label, a mono title, the text and
   the link as the cards' action row. */
.note {
  max-width: 34rem;
}

.note .eyebrow {
  margin-bottom: var(--space-2);
}

.note h3 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}

.note-text {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.note .app-action {
  margin-top: var(--space-2);
}

.article {
  --measure: 44rem;
  padding-block: var(--space-5) var(--space-6);
}

/* The article is a centred composition (founder feedback 2026-09-22,
   replacing the left-edge measure and figure lane of si-55iu): header,
   footer and every block of the body sit on one 44rem measure (about 80
   characters at 1.0625rem/1.7) centred in the container, and the page gets
   its width from the elements that deserve it — a wide figure spans the
   whole container, an inline figure sits at the measure with its caption
   beside the panel, and the "More from the blog" band closes the page.
   Figures are direct children of the body (the `figure` shortcode,
   eleventy.config.js), so every other child is a text block. */
.article-header,
.article-footer,
.article-body > :not(.figure) {
  max-width: var(--measure);
  margin-inline: auto;
}

.article-header {
  margin-bottom: var(--space-5);
}

.article-header h1 {
  font-size: clamp(2rem, 1.3rem + 2.4vw, 3.25rem);
  max-width: 24ch;
}

.article-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-bottom: var(--space-3);
}

.article-summary {
  font-size: var(--text-lg);
  color: var(--color-text-muted);
}

.article-body {
  font-size: 1.0625rem;
  line-height: 1.7;
}

/* Section headings a clear step below the title (the title is up to
   3.25rem): the accent dash above them carries the rhythm, no rule. */
.article-body h2,
.article-body h3 {
  margin-top: var(--space-6);
}

.article-body h2 {
  font-size: 1.75rem;
}

.article-body h3 {
  font-size: 1.25rem;
  margin-top: var(--space-5);
}

.article-body h2::before,
.article-body h3::before {
  content: "";
  display: block;
  width: 2rem;
  height: 2px;
  margin-bottom: var(--space-3);
  background: var(--color-accent);
}

.article-body a {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.article-body pre,
.article-body code {
  font-family: var(--font-mono);
  font-size: 0.9em;
}

.article-body code {
  padding: 0.1em 0.35em;
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-sm);
}

.article-body pre {
  padding: var(--space-3);
  overflow-x: auto;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

.article-body pre code {
  padding: 0;
  border: 0;
  background: transparent;
}

.article-body blockquote {
  margin: 0 0 var(--space-3);
  padding-inline-start: var(--space-3);
  border-inline-start: 2px solid var(--color-accent);
  color: var(--color-text-muted);
}

.article-body table {
  max-width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.article-body th,
.article-body td {
  padding: var(--space-1) var(--space-2);
  border-bottom: 1px solid var(--color-hairline);
  text-align: left;
}

.article-footer {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-hairline);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* Article figures (si-55iu; REQ-008): a <figure> holding a row of inline
   SVG panels and a mono caption. Every panel is drawn on a 320-unit-wide
   viewBox and rendered as a card — the console's surface, border and radius
   — between 20rem (a 360 px phone, where a panel renders 1:1) and 24.5rem,
   so a 13-unit label is 13–16 px. A wide figure spans the whole container
   with its panels in one row from 48rem (stacked below it) and its caption
   centred underneath on the measure. An inline figure (one panel) sits in
   the flow on the measure: from 48rem the panel on the left and the caption
   beside it, top-aligned; below 48rem the caption under the panel. The
   layout gate measures all of this on every article page at five widths. */
.figure {
  margin: var(--space-5) auto;
}

.figure-panels {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}

/* One panel is 24.5rem at most; two share a row only up to that width each
   (auto-fit counts tracks by the definite maximum), so neither stretches
   its type across a 71rem row the way 1fr would. */
.figure-panels-1 {
  max-width: 24.5rem;
}

.figure-panels-2 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 24.5rem));
  justify-content: center;
}

.figure-panel {
  width: 100%;
  height: auto;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: 0 0 0 1px var(--color-hairline) inset;
}

.figure figcaption {
  max-width: var(--measure);
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.6;
  color: var(--color-text-muted);
}

.figure-wide {
  width: 100%;
}

.figure-wide .figure-panels-1 {
  margin-inline: auto;
}

.figure-wide figcaption {
  margin-inline: auto;
  text-align: center;
  text-wrap: balance;
}

.figure-inline {
  max-width: var(--measure);
}

@media (min-width: 48rem) {
  .figure-inline {
    display: grid;
    grid-template-columns: minmax(20rem, 24.5rem) minmax(0, 1fr);
    gap: var(--space-4);
    align-items: start;
  }

  .figure-inline figcaption {
    margin-top: 0;
    padding-top: var(--space-3);
  }
}

/* The ending band (founder feedback 2026-09-22): the other articles of the
   page's language as .post cards under the "More from the blog" heading,
   the landing's writing band closing the article page — one card spans the
   row, two or three share it — then the "All articles" link. */
.article-more .post-list {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}

/* The drawing vocabulary of the figures (src/_includes/figures/figures.mjs):
   text in the text tokens, the accent as a surface and as line art for
   "passed"/highlight, the orange text variant for the founder's gates and
   "waiting", hairlines for scaffolding. Sizes are SVG user units (the
   320-unit panel), so 13px here is 13 units. */
.fig-grid {
  fill: none;
  stroke: var(--color-hairline);
  opacity: 0.6;
}

.fig-head {
  font: 500 11px var(--font-mono);
  letter-spacing: 0.04em;
  fill: var(--color-text-muted);
}

.fig-label {
  font: 500 13px var(--font-mono);
  fill: var(--color-text);
}

.fig-small {
  font-size: 12px;
}

.fig-note {
  font: 400 10.5px var(--font-mono);
  fill: var(--color-text-muted);
}

.fig-strong {
  font-weight: 500;
}

.fig-muted {
  fill: var(--color-text-muted);
}

.fig-ok {
  fill: var(--color-accent-text);
}

.fig-wait {
  fill: var(--color-secondary-text);
}

.fig-hair {
  fill: none;
  stroke: var(--color-hairline);
}

.fig-line {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1.25;
}

.fig-dashed {
  stroke-dasharray: 3 4;
  stroke-linecap: round;
}

.fig-arrowhead {
  fill: var(--color-border);
}

.fig-check {
  fill: none;
  stroke: var(--color-accent-text);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.fig-gate {
  fill: var(--color-secondary);
}

.fig-dot {
  fill: var(--color-accent);
}

.fig-glow {
  fill: var(--color-glow);
}

.fig-node {
  fill: var(--color-surface-2);
  stroke: var(--color-border);
}

.fig-row {
  fill: var(--color-surface-2);
}

.fig-bar {
  fill: var(--color-text);
}

.fig-bar-muted {
  fill: var(--color-border);
}

.fig-area {
  fill: var(--color-glow);
  stroke: var(--color-accent-text);
  stroke-width: 1.5;
  stroke-linejoin: round;
}

.fig-member {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
  stroke-linejoin: round;
}

.fig-accent-stroke {
  fill: none;
  stroke: var(--color-accent-text);
  stroke-width: 1.5;
}

.fig-cell {
  fill: var(--color-accent);
}

.fig-tick {
  fill: var(--color-text);
}

.fig-gap {
  fill: none;
  stroke: var(--color-secondary);
  stroke-width: 2;
  stroke-dasharray: 2 3;
  stroke-linecap: round;
}

/* The founder's caret in the loop figure blinks like the console's. */
.fig-caret {
  fill: var(--color-secondary-text);
  animation: blink 1.1s steps(2, start) infinite;
}

/* Blog index and category pages: the RSS chip in the hero, the post grid
   (.post-list, above), the categories heading and the chip row. In the
   metadata rows every chip takes the category chip's 44 px height so the
   draft chip sits on one line with it. */
.blog-rss {
  margin: 0;
}

.post-meta .chip,
.article-meta .chip {
  min-height: 44px;
}

.page-body .post-list {
  margin-top: var(--space-5);
}

.categories-heading {
  margin-top: var(--space-5);
  font-size: var(--text-xl);
}

.category-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  padding: 0;
  list-style: none;
}

.post-empty,
.back-link {
  margin-top: var(--space-5);
}

.page-body {
  padding-bottom: var(--space-7);
}

/* Motion (REQ-018) ---------------------------------------------------------- */

@keyframes blink {
  50% {
    opacity: 0;
  }
}

@keyframes rise {
  from {
    opacity: 0;
    translate: 0 14px;
  }

  to {
    opacity: 1;
    translate: none;
  }
}

/* Entrance: .reveal elements start hidden only when JavaScript runs (html.js)
   and reveal.js adds is-visible on intersection; the hero and the console
   stagger in pure CSS so the first screen never waits for the script. Only
   opacity and translate move, never layout. The stagger index comes from the
   element's position among its siblings (html-validate forbids inline
   styles), capped at the sixth sibling; the console's log lines share the
   rules. */
.reveal:nth-child(2),
.log-line:nth-child(2) {
  --i: 1;
}

.reveal:nth-child(3),
.log-line:nth-child(3) {
  --i: 2;
}

.reveal:nth-child(4),
.log-line:nth-child(4) {
  --i: 3;
}

.reveal:nth-child(5),
.log-line:nth-child(5) {
  --i: 4;
}

.reveal:nth-child(n + 6),
.log-line:nth-child(n + 6) {
  --i: 5;
}

@media (prefers-reduced-motion: no-preference) {
  html.js .reveal {
    opacity: 0;
    translate: 0 14px;
    transition: opacity 600ms var(--ease), translate 600ms var(--ease);
    transition-delay: calc(var(--i, 0) * 90ms);
  }

  html.js .reveal.is-visible {
    opacity: 1;
    translate: none;
  }

  html.js .hero .reveal {
    animation: rise 700ms var(--ease) both;
    animation-delay: calc(var(--i, 0) * 90ms);
    opacity: 1;
    translate: none;
    transition: none;
  }

  html.js .console .log-line {
    animation: rise 500ms var(--ease) both;
    animation-delay: calc(400ms + var(--i, 0) * 120ms);
  }
}

/* Cross-document view transitions (REQ-018): navigation and the language
   switch fade in supporting browsers; the inline theme script has already
   set data-theme before the new document paints, so the transition never
   shows the wrong theme. Off under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: 0.01ms !important;
  }

  .reveal {
    opacity: 1;
    translate: none;
  }

  .caret,
  .fig-caret {
    animation: none;
  }

  html {
    scroll-behavior: auto;
  }
}

/* Print (REQ-018, REQ-022): the light set — legible on paper, where
   backgrounds are usually not printed — and every section in place, because
   nothing scrolls while printing and the entrance keeps unrevealed sections
   at opacity 0. The attribute selector outranks the :root[data-theme="dark"]
   switch rule in tokens.css that the inline script has already set. */
@media print {
  :root,
  :root[data-theme] {
    color-scheme: light;
  }

  html.js .reveal,
  html.js .hero .reveal,
  html.js .console .log-line {
    opacity: 1;
    translate: none;
    animation: none;
    transition: none;
  }
}
