/* ==========================================================================
   base.css: layout and components shared by ALL themes.

   Rule of thumb: this file decides WHERE things are; theme files decide how
   they LOOK. Everything visual below reads a token (custom property). Themes
   override those tokens in css/themes/<id>.css under html[data-theme="<id>"].

   The values in :root are the neutral "wireframe" look, which is also the
   fallback for any token a theme forgets to set.
   ========================================================================== */

:root {
  /* ---- Shared layout tokens (NOT themeable: keep layout identical) ---- */
  --container: 70rem;
  --measure: 44rem;
  --gutter: clamp(1rem, 4vw, 2rem);
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2.5rem;
  --space-xl: 4rem;
  --space-2xl: clamp(4rem, 8vw, 6.5rem);

  /* ---- Theme tokens: colors ---- */
  --color-bg: #ffffff;
  --color-bg-alt: #f2f2f2;
  --color-surface: #ffffff;
  --color-surface-alt: #f6f6f6;
  --color-text: #1a1a1a;
  --color-text-muted: #555555;
  --color-heading: #111111;
  --color-accent: #1a1a1a;
  --color-on-accent: #ffffff;
  --color-link: #0b57d0;
  --color-link-hover-bg: transparent;
  --color-link-hover-text: #0b57d0;
  --color-highlight: #e8f0fe;
  --color-highlight-text: #1a1a1a;
  --color-border: #8a8a8a;
  --color-focus: #0b57d0;
  --color-header-bg: #ffffff;
  --color-header-text: #1a1a1a;
  --color-footer-bg: #1a1a1a;
  --color-footer-text: #ffffff;
  --color-footer-link: #ffffff;
  --color-bar-bg: #e9e9e9;
  --color-bar-text: #1a1a1a;
  --color-placeholder-bg: #eeeeee;
  --color-placeholder-text: #555555;

  /* ---- Theme tokens: type ---- */
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-heading: var(--font-body);
  --font-display: var(--font-body);
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
  --font-size-base: 1.0625rem;
  --line-height: 1.6;
  --heading-weight: 700;
  --display-weight: 800;
  --display-color: var(--color-heading);
  --display-shadow: none;
  --display-shadow-hero: var(--display-shadow);
  --display-size: clamp(2.25rem, 5vw, 3.25rem);
  --display-size-hero: clamp(2.75rem, 7vw, 4.5rem);
  --display-line-height: 1.1;
  --display-letter-spacing: -0.01em;

  /* ---- Theme tokens: shape and motion ---- */
  --radius: 6px;
  --border-width: 1px;
  --border-style: solid;
  --shadow: 0 1px 3px rgb(0 0 0 / 0.12);
  --shadow-hover: 0 6px 16px rgb(0 0 0 / 0.14);
  --duration: 160ms;
  --easing: ease;
  --page-pattern: none;
}

/* ---------- Reset ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
[hidden] {
  display: none !important;
}
img,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}
input,
button,
select,
textarea {
  font: inherit;
}

/* ---------- Base ---------- */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow-x: clip;
  background-color: var(--color-bg);
  background-image: var(--page-pattern);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height);
  text-rendering: optimizeLegibility;
}
main {
  flex: 1;
}
main:focus {
  outline: none;
}

h1,
h2,
h3,
h4 {
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  line-height: 1.2;
  text-wrap: balance;
}
h2 {
  font-size: clamp(1.6rem, 3vw, 2.1rem);
  margin-bottom: var(--space-m);
}
h3 {
  font-size: 1.2rem;
  margin-bottom: var(--space-xs);
}
p,
ul,
ol,
dl,
address {
  margin-bottom: var(--space-s);
}
p:last-child,
ul:last-child,
dl:last-child {
  margin-bottom: 0;
}
address {
  font-style: normal;
}

a {
  color: var(--color-link);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.18em;
}
a:hover {
  background-color: var(--color-link-hover-bg);
  color: var(--color-link-hover-text);
}
:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 3px;
}
::selection {
  background: var(--color-highlight);
  color: var(--color-highlight-text);
}

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: var(--space-xs);
  left: var(--space-xs);
  z-index: 100;
  padding: var(--space-xs) var(--space-s);
  background: var(--color-accent);
  color: var(--color-on-accent);
  transform: translateY(-200%);
}
.skip-link:focus {
  transform: none;
}

/* ---------- Layout ---------- */
.container {
  width: min(100% - 2 * var(--gutter), var(--container));
  margin-inline: auto;
}
.narrow {
  max-width: var(--measure);
}
.section,
.hero,
.page-header {
  position: relative;
}
.section {
  padding-block: var(--space-2xl);
}
.section--alt {
  background-color: var(--color-bg-alt);
}

.split {
  display: grid;
  gap: var(--space-l);
  align-items: center;
}
.split__media {
  width: 100%;
  max-width: 24rem;
}
.split .split__media--portrait {
  max-width: 24rem;
  justify-self: center;
}
/* Reversed split (the pyramid): the picture goes after the text on every
   screen size, so on phones the heading comes first and the pyramid
   follows it, centred */
.split--reverse .split__media {
  order: 2;
  justify-self: center;
}
@media (min-width: 48rem) {
  .split {
    grid-template-columns: 5fr 7fr;
    gap: var(--space-xl);
  }
  .split__media {
    max-width: none;
  }
  .split--reverse {
    grid-template-columns: 7fr 5fr;
  }
}

.card-grid {
  display: grid;
  gap: var(--space-m);
  padding: 0;
  list-style: none;
}
.card-grid--2 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
}
.card-grid--4 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
}
.card-grid--3 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
.card-grid--work {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}

/* ---------- Header ---------- */
.site-header {
  background: var(--color-header-bg);
  color: var(--color-header-text);
  border-bottom: var(--border-width) var(--border-style) var(--color-border);
}
.site-header__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "name"
    "nav"
    "switch";
  align-items: center;
  gap: var(--space-xs) var(--space-m);
  padding-block: var(--space-s);
}
@media (min-width: 36rem) {
  .site-header__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name switch"
      "nav  nav";
  }
}
@media (min-width: 56rem) {
  .site-header__inner {
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "name nav switch";
  }
}
.site-name {
  grid-area: name;
  justify-self: start;
  color: inherit;
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  font-size: 1.15rem;
  text-decoration: none;
}
.site-nav {
  grid-area: nav;
}
.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 56rem) {
  .site-nav ul {
    justify-content: flex-end;
  }
}
.site-nav a {
  display: inline-block;
  padding: var(--space-2xs) var(--space-xs);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius);
  color: inherit;
  text-decoration: none;
}
.site-nav a:hover,
.site-nav a[aria-current] {
  background: var(--color-highlight);
  border-color: var(--color-border);
  color: var(--color-highlight-text);
}
.site-nav__icon {
  display: inline-flex !important;
  align-items: center;
  height: 100%;
}

/* "Case studies" menu (<details>): inline on small screens, a dropdown panel
   from 56rem up. Summary looks like the other nav links. */
.nav-menu {
  position: relative;
}
.nav-menu summary {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: var(--space-2xs) var(--space-xs);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;
}
.nav-menu summary::-webkit-details-marker {
  display: none;
}
.nav-menu summary::after {
  content: "";
  width: 0.45em;
  height: 0.45em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-25%) rotate(45deg);
}
.nav-menu[open] summary::after {
  transform: translateY(15%) rotate(-135deg);
}
.nav-menu summary:hover,
.nav-menu summary.is-current {
  background: var(--color-highlight);
  border-color: var(--color-border);
  color: var(--color-highlight-text);
}
.site-nav .nav-menu__list {
  display: grid;
  gap: 0;
  margin-top: var(--space-2xs);
  padding: var(--space-2xs) 0 var(--space-2xs) var(--space-s);
}
.site-nav .nav-menu__list a {
  display: block;
}
@media (min-width: 56rem) {
  .site-nav .nav-menu__list {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    min-width: 15rem;
    padding: var(--space-xs);
    background: var(--color-surface);
    border: var(--border-width) var(--border-style) var(--color-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    color: var(--color-text);
  }
}

/* ---------- Theme switcher ---------- */
.theme-switcher {
  grid-area: switch;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: 0.9rem;
}
.theme-switcher label {
  font-weight: 700;
}
.theme-switcher select {
  min-width: 0;
  max-width: 100%;
  padding: var(--space-2xs) var(--space-xs);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  color: var(--color-text);
}

/* Tablets (48–56rem): the theme dropdown moves down beside the menu links,
   on one row under the name. Links a little smaller (14px; the html prefix
   outranks the themes' own link sizes), the dropdown and its label smaller
   still (12.8px), so they fit. Below 48rem there isn't room, so the
   dropdown stays beside the name. (Lina, 2026-11-07; tried in
   NeuroSpiceGirl first.) */
@media (min-width: 48rem) and (max-width: 55.99rem) {
  .site-header__inner {
    grid-template-areas:
      "name name"
      "nav  switch";
  }
  html[data-theme] .site-header .site-nav a,
  html[data-theme] .site-header .nav-menu summary {
    font-size: 0.875rem;
  }
  .theme-switcher {
    font-size: 0.8rem;
  }
  /* The row starts the width of a link's padding and border to the left,
     so the word "Home" lines up with "Lina Bodestad" above it (its
     highlight box sticks out a little, Lina 2026-11-07) */
  .site-nav > ul {
    margin-left: calc(-1 * (var(--space-xs) + var(--border-width)));
  }
}

/* ---------- Phone menu button ----------
   Below 36rem the links and the theme dropdown fold away behind a "Menu"
   button beside the name (header.njk, nav.js). The button only shows once
   nav.js has set data-menu on the header, so without JavaScript the full
   menu shows as before. On wider screens the wrapper (.site-menu) is
   "display: contents" and the header looks as it always has. Themes style
   the button along with their nav links. */
.site-menu {
  display: contents;
}
.menu-toggle {
  display: none;
  align-items: center;
  gap: 0.5em;
  min-height: 2.75rem; /* 44px tap target */
  padding: var(--space-2xs) var(--space-xs);
  border: var(--border-width) solid currentColor;
  border-radius: var(--radius);
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.menu-toggle:hover,
.menu-toggle[aria-expanded="true"] {
  background: var(--color-highlight);
  border-color: var(--color-border);
  color: var(--color-highlight-text);
}
/* Three bars, turning into an X when open */
.menu-toggle__icon,
.menu-toggle__icon::before,
.menu-toggle__icon::after {
  display: block;
  width: 1.1em;
  height: 2px;
  background: currentColor;
}
.menu-toggle__icon {
  position: relative;
}
.menu-toggle__icon::before,
.menu-toggle__icon::after {
  content: "";
  position: absolute;
  left: 0;
}
.menu-toggle__icon::before {
  top: -0.35em;
}
.menu-toggle__icon::after {
  top: 0.35em;
}
.menu-toggle[aria-expanded="true"] .menu-toggle__icon {
  background: transparent;
}
.menu-toggle[aria-expanded="true"] .menu-toggle__icon::before {
  top: 0;
  transform: rotate(45deg);
}
.menu-toggle[aria-expanded="true"] .menu-toggle__icon::after {
  top: 0;
  transform: rotate(-45deg);
}
@media (forced-colors: active) {
  .menu-toggle__icon,
  .menu-toggle__icon::before,
  .menu-toggle__icon::after {
    background: CanvasText;
  }
  .menu-toggle[aria-expanded="true"] .menu-toggle__icon {
    background: transparent;
  }
}
@media (max-width: 35.99rem) {
  .site-header[data-menu] .site-header__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name toggle"
      "menu menu";
  }
  .site-header[data-menu] .menu-toggle {
    display: inline-flex;
    grid-area: toggle;
  }
  .site-header[data-menu] .site-menu {
    grid-area: menu;
    display: grid;
    gap: var(--space-s);
    padding-block: var(--space-xs) var(--space-2xs);
  }
  .site-header[data-menu] .site-menu > * {
    grid-area: auto;
  }
  .site-header[data-menu="closed"] .site-menu {
    display: none;
  }
  /* The open menu: links stacked and right-aligned under the button (the
     case studies indented from the right), the theme dropdown right-aligned
     below a dashed line (Lina, 2026-11-07, all themes) */
  .site-header[data-menu] .site-nav > ul {
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-2xs);
  }
  .site-header[data-menu] .nav-menu {
    text-align: right;
  }
  .site-header[data-menu] .site-nav .nav-menu__list {
    justify-items: end;
    padding: var(--space-2xs) var(--space-s) var(--space-2xs) 0;
  }
  .site-header[data-menu] .theme-switcher {
    justify-content: flex-end;
    padding-top: var(--space-xs);
    border-top: 2px dashed color-mix(in srgb, currentColor 30%, transparent);
  }
}
/* ...rolling down like a curtain when it opens (no movement under reduced
   motion or with motion paused) */
@media (max-width: 35.99rem) and (prefers-reduced-motion: no-preference) {
  html:not([data-motion="paused"]) .site-header[data-menu="open"] .site-menu {
    animation: menu-curtain 280ms ease-out;
  }
}
@keyframes menu-curtain {
  from {
    clip-path: inset(0 0 100% 0);
    transform: translateY(-0.5rem);
  }
  to {
    clip-path: inset(0 0 0 0);
    transform: none;
  }
}

/* ---------- Display titles (hero + page h1) ---------- */
.display-title {
  color: var(--display-color);
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--display-size);
  line-height: var(--display-line-height);
  letter-spacing: var(--display-letter-spacing);
  text-shadow: var(--display-shadow);
  margin-bottom: var(--space-s);
}
.display-title--hero {
  font-size: var(--display-size-hero);
  text-shadow: var(--display-shadow-hero);
}

/* ---------- Hero ---------- */
.hero {
  padding-block: var(--space-xl);
}
.hero + .section {
  padding-top: var(--space-l);
}
.hero__grid {
  display: grid;
  gap: var(--space-l);
  align-items: center;
}
@media (min-width: 52rem) {
  .hero__grid {
    grid-template-columns: 7fr 5fr;
    gap: var(--space-xl);
  }
}
.hero__text {
  max-width: var(--measure);
}
/* The hero h1 holds the big themed greeting */
.hero h1 .display-title {
  display: block;
}
.hero .button-row {
  margin-block: 36px;
}

/* Large text for short key statements */
.statement {
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  font-size: clamp(1.3rem, 2.6vw, 1.7rem);
  line-height: 1.3;
  color: var(--color-heading);
}

/* ---------- Proof strip (key numbers) ---------- */
.stats {
  display: grid;
  gap: var(--space-l);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 48rem) {
  .stats {
    grid-template-columns: repeat(3, 1fr);
  }
}
.stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}
.stat__value {
  color: var(--display-color);
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: clamp(2.75rem, 6vw, 4rem);
  line-height: 1;
  text-shadow: var(--display-shadow);
}
.stat__label {
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  font-size: 1.15rem;
}
.stat__detail {
  font-size: 0.95rem;
}

/* Theme art: decorative slots each theme fills with its own artwork
   (backgrounds on the <span>s). Same size in every theme. */
.theme-art {
  position: relative;
  width: 100%;
  margin-inline: auto;
}
.theme-art span {
  position: absolute;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  pointer-events: auto;
}
.theme-art--hero {
  max-width: 26rem;
  aspect-ratio: 1 / 1;
}
@media (max-width: 51.99rem) {
  .theme-art--hero {
    max-width: 18rem;
  }
}

/* AI tool logos beside "How I work with AI" (partials/ai-logos.njk): a
   sticker collage like NeuroSpiceGirl's hero art, beside the text (below it
   on narrow screens). Each logo is tilted and snaps straight on hover, in
   two jerky steps like the hero stickers; nothing moves under reduced
   motion. Themes set the colours: the logo (color), an optional outline
   (stroke on .ai-logos__mark) and the die-cut edge (--ai-logo-edge). */
.ai-layout {
  display: grid;
  gap: var(--space-l);
  align-items: center;
}
@media (min-width: 48rem) {
  .ai-layout {
    grid-template-columns: 7fr 5fr;
    gap: var(--space-xl);
  }
}
.ai-logos {
  position: relative;
  width: 100%;
  max-width: 20rem;
  aspect-ratio: 1 / 1;
  margin-inline: auto;
}
/* Smaller on phones, where it sits below the text and would otherwise take
   a whole screen of scrolling */
@media (max-width: 47.99rem) {
  .ai-logos {
    max-width: 14rem;
  }
}
.ai-logos__sticker {
  position: absolute;
  transition: transform 200ms steps(2, end);
}
.ai-logos__sticker--claude {
  top: 6%;
  left: 2%;
  width: 52%;
  height: 52%;
  transform: rotate(-6deg);
}
.ai-logos__sticker--figma {
  top: 0;
  right: 8%;
  width: 38%;
  height: 38%;
  transform: rotate(9deg);
}
.ai-logos__sticker--obsidian {
  bottom: 0;
  left: 14%;
  width: 42%;
  height: 42%;
  transform: rotate(7deg);
}
.ai-logos__sticker--copilot {
  bottom: 12%;
  right: 0;
  width: 48%;
  height: 48%;
  transform: rotate(-8deg);
}
@media (prefers-reduced-motion: no-preference) {
  .ai-logos__sticker:hover {
    transform: rotate(0deg) scale(1.06);
  }
}
.ai-logos__sticker svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.ai-logos__edge {
  fill: var(--ai-logo-edge, #fff);
  stroke: var(--ai-logo-edge, #fff);
  stroke-width: 9;
  stroke-linejoin: round;
}
.ai-logos__mark {
  fill: currentColor;
}

/* ---------- Page header (sub pages) ---------- */
.page-header {
  padding-block: var(--space-xl);
  margin-bottom: var(--space-xl);
  border-bottom: var(--border-width) var(--border-style) var(--color-border);
}
.page-header__grid {
  display: grid;
  gap: var(--space-l);
  align-items: center;
}
@media (min-width: 48rem) {
  .page-header__grid {
    grid-template-columns: 1fr auto;
  }
}
.page-header__text {
  max-width: var(--measure);
}
.page-header__lead {
  font-size: 1.2rem;
  color: var(--color-text);
}
.eyebrow {
  margin-bottom: var(--space-xs);
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* No eyebrow above the Home title on phones, so the hero starts with
   "Hi! I'm Lina." (Lina, 2026-11-07; Berliner hides it on every screen) */
@media (max-width: 47.99rem) {
  .hero .eyebrow {
    display: none;
  }
}

.facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s) var(--space-l);
  margin-top: var(--space-m);
}
.facts dt,
.card__facts dt,
.contact-list__label {
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.facts dd,
.card__facts dd {
  margin: 0;
}

/* ---------- Buttons ---------- */
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  margin-top: var(--space-m);
}
.button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0.7rem 1.25rem;
  border: var(--border-width) var(--border-style) var(--color-accent);
  border-radius: var(--radius);
  background: var(--color-accent);
  color: var(--color-on-accent);
  box-shadow: var(--shadow);
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  text-decoration: none;
  transition: transform var(--duration) var(--easing), box-shadow var(--duration) var(--easing);
}
.button:hover {
  background: var(--color-accent);
  color: var(--color-on-accent);
  box-shadow: var(--shadow-hover);
  transform: translate(-1px, -1px);
}
.button--secondary,
.button--secondary:hover {
  background: var(--color-surface);
  color: var(--color-accent);
}

/* ---------- Cards ("windows") ---------- */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: var(--border-width) var(--border-style) var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.card__bar {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  min-height: 0.6rem;
  padding: var(--space-2xs) var(--space-s);
  background: var(--color-bar-bg);
  color: var(--color-bar-text);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.4;
}
.card__bar-title {
  flex: 1;
}
.card__bar-buttons {
  display: none;
}
.card__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: var(--space-m);
}
.card__facts {
  display: grid;
  gap: var(--space-xs);
  margin-block: var(--space-s);
}
.card__more {
  margin-top: auto;
  padding-top: var(--space-s);
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  color: var(--color-link);
}

/* Whole card is clickable; the real link is the heading */
.card--link {
  transition: transform var(--duration) var(--easing), box-shadow var(--duration) var(--easing);
}
.card--link .card__title a {
  color: inherit;
  text-decoration: none;
}
.card--link .card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}
.card--link .card__title a:hover {
  background: none;
}
.card--link:hover {
  box-shadow: var(--shadow-hover);
  transform: translate(-2px, -2px);
}
.card--link:focus-within {
  outline: 3px solid var(--color-focus);
  outline-offset: 3px;
}
.card--link .card__title a:focus-visible {
  outline: none;
}
.card__title {
  font-size: 1.35rem;
  margin-bottom: var(--space-2xs);
}
.card--lead .card__title {
  font-size: clamp(1.5rem, 2.6vw, 1.9rem);
}
.card__role {
  margin-bottom: var(--space-xs);
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.card--lead .card__body {
  padding: var(--space-l);
}
.subsection-title {
  margin-block: var(--space-l) var(--space-m);
  font-size: 1.25rem;
}
h2 + .subsection-title {
  margin-top: 0;
}

/* ---------- Prose (Markdown content) ---------- */
.prose {
  padding-bottom: var(--space-xl);
}
.prose > * {
  max-width: var(--measure);
}
.prose > .placeholder,
.prose > table {
  max-width: none;
}
.prose h2 {
  margin-top: var(--space-xl);
}
.prose h3 {
  margin-top: var(--space-l);
}
.prose h2 + h3,
.prose h2:first-child {
  margin-top: 0;
}
.prose ul,
.prose ol {
  padding-left: 1.25em;
}
.prose li + li {
  margin-top: var(--space-2xs);
}
.prose .placeholder {
  margin-block: var(--space-m) var(--space-l);
}
/* Photo of Lina at the top of About (4:3), and a case study's photo at the
   top of the case (16:9): as wide as the page, with the theme's border and
   corners, or its portrait frame where it has one (NeuroSpiceGirl).
   Berliner shows the black-and-white version instead where there is one
   (Lina's photo, and a case photo with a `berliner` version). */
.prose > :is(.about-photo, .case-photo) {
  display: block;
  max-width: none;
  width: 100%;
  height: auto;
  margin-block: var(--space-m) var(--space-l);
  border: var(--portrait-border, var(--border-width) var(--border-style) var(--color-border));
  border-radius: var(--radius);
  box-shadow: var(--portrait-shadow, none);
}
/* A small case image with a transparent background (Game of Chores' app
   screens): at no more than its own size, so it stays sharp, lined up
   with the text on the left (Lina, 2026-11-03), and without a frame, which
   would box in empty space */
/* Design screens inside a case's text (Carmo, Cetasol) get as much space
   above them as the next heading has, so they sit midway between the
   paragraph before and the heading after (Lina, 2026-11-03) */
.prose > .case-photo--in-text {
  margin-top: var(--space-xl);
}
.prose > .case-photo--natural {
  width: auto;
  max-width: 100%;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.prose > :is(.about-photo--berliner, .case-photo--berliner),
html[data-theme="berliner"] .prose > :is(.about-photo, .case-photo--colour) {
  display: none;
}
html[data-theme="berliner"] .prose > :is(.about-photo--berliner, .case-photo--berliner) {
  display: block;
}
/* A case can also have its own Nordic forest photo (case-study.njk) */
.prose > .case-photo--nordic,
html[data-theme="nordic-forest"] .prose > .case-photo--default {
  display: none;
}
html[data-theme="nordic-forest"] .prose > .case-photo--nordic {
  display: block;
}

/* About page sections, laid out more like Home. A band (Books, More
   projects) runs its background across the whole window: the shadow paints
   the colour out to the window edges without making the page scroll
   sideways, and the clip keeps it from spilling above and below. Themes
   set the colour with --about-band-bg (the alternate section colour by
   default) and the text colours to go with it. A split (Teaching) puts the
   text and the school logos side by side on wider screens, like "How I
   work with AI" on Home; on phones the logos sit below the text. Bands and
   splits open with their heading, so they take over the space above it. */
.prose > :is(.about-band, .about-split) {
  margin-top: var(--space-xl);
}
.about-band {
  --about-band-bg: var(--color-bg-alt);
  padding-block: var(--space-2xl);
  background: var(--about-band-bg);
  box-shadow: 0 0 0 100vmax var(--about-band-bg);
  clip-path: inset(0 -100vmax);
}
.prose > .about-split {
  max-width: none;
}
.about-split__text > * {
  max-width: var(--measure);
}
@media (min-width: 48rem) {
  .about-split {
    display: grid;
    grid-template-columns: 7fr 5fr;
    gap: var(--space-xl);
    align-items: center;
  }
}

/* School logos in the teaching section of About: all the same height
   (22px, 16px on phones), wrapping into centred rows. Logos have very
   different shapes, so the wide ones (Hyper Island) take far more room
   than the compact ones (Yrgo). */
.prose .schools {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-m) var(--space-l);
  margin-block: var(--space-l) 0;
  padding: 0;
  list-style: none;
}
.prose .schools li {
  margin: 0;
}
.schools img {
  display: block;
  width: auto;
  height: 1.375rem;
  max-width: 100%;
  object-fit: contain;
}
@media (min-width: 48rem) {
  .prose .about-split .schools {
    margin-top: 0;
    column-gap: var(--space-m);
  }
}
@media (max-width: 30rem) {
  .prose .schools {
    column-gap: var(--space-m);
  }
  .schools img {
    height: 1rem;
  }
}

/* Book covers (About), each a link to where the book is sold: the newest
   book wide across the text column, the three older ones side by side below
   it. Their shapes differ a little, so each takes a share of the row in
   proportion to its --ratio (width / height), which gives them all the same
   height. Each cover gets the theme's border and corners; themes add their
   own hover. */
.books {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  margin: var(--space-m) 0 var(--space-l);
}
.books__row {
  display: flex;
  gap: inherit;
}
.books__row .books__link {
  flex: var(--ratio) 1 0;
  min-width: 0;
}
.books__link {
  display: block;
}
.books__cover {
  display: block;
  width: 100%;
  height: auto;
  border: var(--border-width) var(--border-style) var(--color-border);
  border-radius: var(--radius);
}
/* Quotes (Markdown "> ") in blog posts and pages */
.prose blockquote {
  margin-block: var(--space-m);
  padding-left: var(--space-m);
  border-left: 3px solid var(--color-accent);
  font-style: italic;
}
.prose blockquote p:last-child {
  margin-bottom: 0;
}

/* ---------- Tables (case study Snapshot and Results) ---------- */
.prose table {
  width: 100%;
  margin-block: var(--space-m) var(--space-l);
  border-collapse: collapse;
  background: var(--color-surface-alt);
  border: var(--border-width) var(--border-style) var(--color-border);
  box-shadow: var(--shadow);
}
.prose th,
.prose td {
  padding: var(--space-xs) var(--space-s);
  border-bottom: 1px solid var(--color-border);
  text-align: left;
  vertical-align: top;
}
.prose tr:last-child > * {
  border-bottom: 0;
}
.prose thead th {
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.prose tbody th,
.prose tbody td:first-child {
  width: 30%;
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
}
/* Small screens: stack each row (label above value) instead of squeezing */
@media (max-width: 40rem) {
  .prose table,
  .prose tbody,
  .prose tr,
  .prose th,
  .prose td {
    display: block;
    width: auto;
  }
  .prose thead {
    display: none;
  }
  .prose tbody th,
  .prose tbody td:first-child {
    width: auto;
    padding-bottom: 0;
    border-bottom: 0;
  }
}

/* ---------- Image placeholders ---------- */
.placeholder {
  display: grid;
  place-items: center;
  width: 100%;
  padding: var(--space-m);
  background: var(--color-placeholder-bg);
  border: 2px dashed var(--color-border);
  border-radius: var(--radius);
  color: var(--color-placeholder-text);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  text-align: center;
}
.placeholder--round {
  border-radius: 50%;
}

/* Round portrait photo; themes can add a frame via --portrait-border/-shadow */
.portrait {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 50%;
  border: var(--portrait-border, 0);
  box-shadow: var(--portrait-shadow, none);
}
/* A theme's own portrait photo: off unless that theme swaps it in */
.portrait--theme {
  display: none;
}

/* ---------- Pager (prev / next case) ---------- */
.pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-s);
  padding-block: var(--space-l) var(--space-2xl);
  border-top: var(--border-width) var(--border-style) var(--color-border);
}
.pager__link {
  display: flex;
  flex-direction: column;
  padding: var(--space-s) var(--space-m);
  border: var(--border-width) var(--border-style) var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  box-shadow: var(--shadow);
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  text-decoration: none;
}
.pager__link--next {
  margin-left: auto;
  text-align: right;
}
.pager__label {
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ---------- Contact page ---------- */
.contact-list {
  margin-block: var(--space-m);
  padding: 0 !important;
  list-style: none;
}
.contact-list li {
  display: grid;
  gap: var(--space-2xs);
  margin-bottom: var(--space-m);
  font-size: 1.15rem;
}

/* ---------- Blog (cards, post meta, tags) ----------
   Post cards are window cards (.card--link): large on the Blog page's
   Featured row, compact in "All posts" and on Home. */
.post-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.5em;
  margin-bottom: var(--space-xs);
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: 0.8rem;
}
.page-header .post-meta {
  margin-top: var(--space-s);
  font-size: 0.9rem;
}
/* Specific enough to beat themes' general ".card__body h3" sizes */
.post-card .card__body .card__title {
  font-size: 1.35rem;
}
.post-card--featured .card__body .card__title {
  font-size: clamp(1.5rem, 2.6vw, 1.9rem);
}
/* Like the other card grids, but empty columns are kept, so one or two
   posts keep their normal card width instead of stretching. At most three
   columns (two for Featured); cards are never narrower than 16rem (22rem). */
.card-grid--posts {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, max(16rem, (100% - 2 * var(--space-m)) / 3)), 1fr));
}
.card-grid--posts-featured {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, max(22rem, (100% - var(--space-m)) / 2)), 1fr));
}
/* Compact cards show at most three lines of the excerpt */
.post-card.card--compact .post-card__excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-xs);
  margin-top: var(--space-s);
  padding: 0;
  list-style: none;
  font-family: var(--font-mono);
  font-size: 0.8rem;
}
.tag-list li {
  padding: 0.1rem 0.5rem;
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius);
}
.page-header .tag-list {
  margin-top: var(--space-xs);
}

/* ---------- Talent stack (Home) ----------
   Overlapping skill circles drawn in SVG with live text
   (partials/talent-stack.njk). Themes restyle it with these properties:
   --talent-circle, --talent-circle-opacity   circle fill
   --talent-outline, --talent-outline-width   circle outline (none by default)
   --talent-text, --talent-text-halo          label colour, and an outline
                                              behind the letters that keeps
                                              them clear of crossing circles
   --talent-font, --talent-font-small         label fonts (large/small labels)
   --talent-size-xl, -l, -s                   label sizes, in drawing units
   Each circle and its label share a .talent-stack__bubble group, so a theme
   can animate a bubble as one piece, or just its circle (both scale from
   the circle's centre, and --i is the bubble's position in the list, for
   staggering). Blend modes don't work
   here: an animating group is drawn on its own and can't blend with the
   bubbles behind it, so overlaps come from the fill opacity alone.
   Below 36rem the labels would be too small to read, so the list of skills
   is shown instead of the drawing. */
.talent-stack {
  margin-top: var(--space-l);
}
.talent-stack__graphic {
  display: none;
  width: 100%;
  max-width: 50rem; /* 48rem for the bubbles, plus the drawing's margin */
  margin-inline: auto;
}
.talent-stack__bubble,
.talent-stack__bubble :is(circle, rect) {
  transform-box: fill-box;
  transform-origin: center;
}
.talent-stack__bubble :is(circle, rect) {
  fill: var(--talent-circle, var(--color-accent));
  fill-opacity: var(--talent-circle-opacity, 0.12);
  stroke: var(--talent-outline, none);
  stroke-width: var(--talent-outline-width, 2);
}
/* The rectangle version of each bubble: off unless a theme swaps it in
   (hide the circle and show .talent-stack__block) */
.talent-stack__block {
  display: none;
}
.talent-stack__label {
  fill: var(--talent-text, var(--color-text));
  stroke: var(--talent-text-halo, none);
  stroke-width: 6px;
  stroke-linejoin: round;
  paint-order: stroke;
  font-family: var(--talent-font, var(--font-heading));
  text-anchor: middle;
  dominant-baseline: central;
  /* Lets the text scale continuously; otherwise browsers snap it to whole
     pixel sizes and hinting, so a small scale-up (e.g. a hover pulse) jumps
     in a few visible steps. */
  text-rendering: geometricPrecision;
}
/* Sizes in drawing units (the drawing is 1007 wide) */
.talent-stack__label--xl {
  font-size: var(--talent-size-xl, 44px);
}
.talent-stack__label--l {
  font-size: var(--talent-size-l, 32px);
}
.talent-stack__label--s {
  font-family: var(--talent-font-small, var(--talent-font, var(--font-heading)));
  font-size: var(--talent-size-s, 22px);
}
.talent-stack__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  padding: 0;
  list-style: none;
}
.talent-stack__list li {
  padding: 0.3rem 0.75rem;
  border: var(--border-width) solid var(--color-border);
  border-radius: 999px;
  font-size: 0.9rem;
}
@media (min-width: 36rem) {
  .talent-stack__graphic {
    display: block;
  }
  /* Still read by screen readers; the drawing itself is aria-hidden */
  .talent-stack__list {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}
@media (forced-colors: active) {
  .talent-stack__bubble :is(circle, rect) {
    fill: none;
    stroke: CanvasText;
  }
  .talent-stack__label {
    fill: CanvasText;
    stroke: none;
  }
}
/* Bubble animations, shared so themes can opt in (see a theme's talent
   stack rules for how and when they run). From Animate.css bounceIn and
   pulse (https://animate.style); our pulse goes to 110% instead of 105%,
   and is meant for the circle only, so the label stays legible. */
@keyframes talent-bounce-in {
  from,
  20%,
  40%,
  60%,
  80%,
  to {
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }
  0% {
    opacity: 0;
    transform: scale(0.3);
  }
  20% {
    transform: scale(1.1);
  }
  40% {
    transform: scale(0.9);
  }
  60% {
    opacity: 1;
    transform: scale(1.03);
  }
  80% {
    transform: scale(0.97);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}
@keyframes talent-pulse {
  50% {
    transform: scale(1.1);
  }
}

/* ---------- Pyramid of Design (Home) ----------
   Four stacked levels drawn in SVG with live text (partials/pyramid.njk).
   Themes restyle it with:
   --pyramid-fill, --pyramid-fill-opacity   level fill
   --pyramid-text, --pyramid-font           label colour and font
   Labels are 22 drawing units (the drawing is 600 wide), so they stay at
   about 12px or more on a 375px-wide phone.
   A theme can pulse a level's shape on hover (keyframes pyramid-pulse
   below); the drawing may then reach a little past its box, so it isn't
   clipped. */
.pyramid__graphic {
  width: 100%;
  overflow: visible;
}
.pyramid__graphic polygon {
  fill: var(--pyramid-fill, var(--color-accent));
  fill-opacity: var(--pyramid-fill-opacity, 0.12);
  transform-box: fill-box;
  transform-origin: center;
}
.pyramid__graphic text {
  fill: var(--pyramid-text, var(--color-text));
  font-family: var(--pyramid-font, var(--font-heading));
  font-size: 22px;
  text-anchor: middle;
  dominant-baseline: central;
}
@media (forced-colors: active) {
  .pyramid__graphic polygon {
    fill: none;
    stroke: CanvasText;
  }
  .pyramid__graphic text {
    fill: CanvasText;
  }
}
/* Like talent-pulse, but to 105%: the levels are wide and only 8 units
   apart, so a bigger pulse would cover the levels above and below */
@keyframes pyramid-pulse {
  50% {
    transform: scale(1.05);
  }
}

/* ---------- Footer ---------- */
.site-footer {
  background: var(--color-footer-bg);
  color: var(--color-footer-text);
  padding-block: var(--space-xl) var(--space-l);
}
.site-footer a {
  color: var(--color-footer-link);
}
.site-footer a:hover {
  background: none;
  color: var(--color-footer-link);
  text-decoration-thickness: 3px;
}
.site-footer__inner {
  display: grid;
  gap: var(--space-m);
}
.site-footer__heading {
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  font-size: 1.3rem;
  margin-bottom: var(--space-xs);
}
.site-footer ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-l);
  padding: 0;
  list-style: none;
}
.site-footer__tagline,
.site-footer__meta {
  font-size: 0.85rem;
}

/* "Pause animations" button. Hidden until motion.js runs, and only shown by
   themes with looping motion. */
.motion-toggle {
  display: none;
  justify-self: start;
  padding: 0.35rem 0.75rem;
  border: var(--border-width) solid currentColor;
  border-radius: var(--radius);
  background: transparent;
  color: var(--color-footer-link);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  cursor: pointer;
}
.motion-toggle:hover {
  background: var(--color-footer-link);
  color: var(--color-footer-bg);
}

/* ---------- Motion ---------- */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 280ms;
}
/* Set by the "Pause animations" button (motion.js) */
[data-motion="paused"] *,
[data-motion="paused"] *::before,
[data-motion="paused"] *::after {
  animation-play-state: paused !important;
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
