/* ==========================================================================
   dyttyr.com
   One stylesheet for the whole site.

   Design rules this file holds itself to:
   - One accent colour (amber), used identically in both themes. Deliberately
     off the default SaaS-blue: warm enough to sit with the site's warm
     greys. --warn below is shifted toward yellow, a good 18-20 degrees of
     hue off the accent, so the two are never mistaken for each other in a
     status line.
   - Two corner radii and nothing else: 6px for controls and tags, 10px for
     cards and panels. Documented here so it stays consistent.
   - Both themes clear WCAG AA for body text. Muted text is 6.4:1 on light
     and 7.4:1 on dark; link colour is 5.4:1 and 9.9:1.
   - Motion is hover, focus and one short page fade. Nothing loops, nothing
     moves on scroll, and everything collapses under prefers-reduced-motion.
   ========================================================================== */

/* ------------------------------------------------------------------ fonts */
/* Self-hosted so there are no third-party requests and no layout shift.
   Drop the .woff2 files into src/fonts and the build copies them across.
   If they are absent the fallback stack below takes over cleanly. */

@font-face {
  font-family: 'Geist';
  src: url('/assets/fonts/Geist-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Geist';
  src: url('/assets/fonts/Geist-Medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Geist';
  src: url('/assets/fonts/Geist-SemiBold.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('/assets/fonts/GeistMono-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ----------------------------------------------------------------- tokens */

:root {
  color-scheme: light;

  --bg: #fbfbfa;
  --bg-subtle: #f4f4f2;
  --bg-inset: #ecece9;
  --border: #dddcd8;
  --border-strong: #c3c2bd;

  --text: #1b1b19;
  --text-muted: #5c5c57;

  --accent: #9a5b0a;
  --accent-hover: #7c4708;
  --accent-wash: #fbeed9;
  --accent-contrast: #ffffff;

  --ok: #146c43;
  --warn: #766c00;
  --error: #b42318;

  --font-sans: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo,
    Consolas, 'Liberation Mono', monospace;

  /* Two radii, no more. Controls and tags use sm, cards and panels use md. */
  --r-sm: 6px;
  --r-md: 10px;

  --shell: 68rem;
  --measure: 68ch;

  /* Shadows are tinted toward the page ground, never pure black. */
  --shadow-card: 0 1px 2px rgb(27 27 25 / 0.04), 0 6px 20px rgb(27 27 25 / 0.05);

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

[data-theme='dark'] {
  color-scheme: dark;

  --bg: #131315;
  --bg-subtle: #1a1a1d;
  --bg-inset: #222226;
  --border: #2e2e33;
  --border-strong: #43434a;

  --text: #ebebe9;
  --text-muted: #a3a3a0;

  --accent: #f2b155;
  --accent-hover: #f7c67e;
  --accent-wash: #3a2a12;
  --accent-contrast: #1a1206;

  --ok: #5fd39a;
  --warn: #e8d03c;
  --error: #f27a72;

  --shadow-card: 0 1px 2px rgb(0 0 0 / 0.3), 0 6px 20px rgb(0 0 0 / 0.28);
}

/* ------------------------------------------------------------------ reset */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 5.5rem; /* clears the sticky header on anchor jumps */
}

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.6;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.2;
}

p {
  margin: 0;
}

ul,
ol {
  margin: 0;
  padding: 0;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: color 0.15s var(--ease);
}

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

img,
svg {
  max-width: 100%;
  height: auto;
  display: block;
}

code,
kbd,
samp,
pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

::selection {
  background: var(--accent-wash);
  color: var(--text);
}

/* --------------------------------------------------------------- skeleton */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: 1.5rem;
}

main {
  flex: 1 0 auto;
  padding-block: 3.5rem 5rem;
}

/* One short fade as a page settles. This is the whole "page transition":
   deliberately quiet, and skipped entirely under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .page-enter {
    animation: page-in 0.26s var(--ease) both;
  }
  @keyframes page-in {
    from {
      opacity: 0;
      transform: translateY(4px);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.65rem 1rem;
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: 0 0 var(--r-sm) 0;
  font-weight: 500;
  text-decoration: none;
}

.skip-link:focus {
  left: 0;
}

/* ----------------------------------------------------------------- header */

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

@supports not (backdrop-filter: blur(1px)) {
  .site-header {
    background: var(--bg);
  }
}

.header-inner {
  height: 4rem; /* 64px, comfortably inside the 80px cap */
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  /* Reads as roughly 7.5px between the mark and the d, once the glyph's own
     left bearing is counted. Half the mark height is the usual lockup gap. */
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 1.0625rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
}

/* The supplied logo, shown as is. Never restyle the artwork itself.

   logo.svg has a squared viewBox tightened to the artwork, so this box size is
   effectively the mark size and no negative-margin correction is needed.

   At 1.25rem the mark renders about 18.8px wide and 16px tall. The wordmark
   beside it has an 11px cap height and a 15px full extent from the top of the
   d to the bottom of the y, which puts the mark at 1.45 times cap height and
   just above the word's own height. That is the balanced range for a mark set
   next to a wordmark; much larger and it starts to dominate the name. */
.wordmark-mark {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
  display: block;
  object-fit: contain;
}

/* An inlined SVG logo fills with currentColor, so it follows the theme through
   ordinary inheritance and needs nothing here.

   A bitmap logo cannot do that, so dark-ink artwork gets inverted instead.
   Inverting flips lightness only: shape, proportion and spacing are untouched.
   Scoped to img so it never double-applies to the inline SVG.

   A bitmap logo must have a TRANSPARENT background. With white baked in, this
   rule turns that white into a black block. */
[data-theme='dark'] img.wordmark-mark {
  filter: invert(1);
}

.wordmark:hover {
  color: var(--accent);
}

.nav {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  margin-inline-start: auto;
}

.nav-link {
  color: var(--text-muted);
  text-decoration: none;
  font-size: 0.9375rem;
  white-space: nowrap;
}

.nav-link:hover {
  color: var(--accent);
}

.nav-link.is-current {
  color: var(--text);
  font-weight: 500;
}

.theme-toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.125rem;
  height: 2.125rem;
  padding: 0;
  font: inherit;
  color: var(--text-muted);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease),
    background-color 0.15s var(--ease);
}

.theme-toggle svg {
  width: 1.0625rem;
  height: 1.0625rem;
  display: block;
}

/* The button shows the theme it will switch you TO, which is why light mode
   shows a moon. Both icons ship in the markup and css picks one, so the right
   icon is on screen before any script runs rather than flashing after it.

   These selectors are deliberately scoped to .theme-toggle. A bare .icon-sun
   would lose to the .theme-toggle svg rule above on specificity and both icons
   would show at once. */
.theme-toggle .icon-sun {
  display: none;
}

[data-theme='dark'] .theme-toggle .icon-moon {
  display: none;
}

[data-theme='dark'] .theme-toggle .icon-sun {
  display: block;
}

.theme-toggle:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-wash);
}

.theme-toggle:active {
  transform: translateY(1px);
}

/* ----------------------------------------------------------------- footer */

.site-footer {
  border-top: 1px solid var(--border);
  padding-block: 2rem;
  margin-top: auto;
}

.footer-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  align-items: baseline;
  justify-content: space-between;
}

.footer-note,
.footer-nav {
  font-size: 0.875rem;
  color: var(--text-muted);
}

.footer-nav {
  display: flex;
  gap: 1.25rem;
}

/* ------------------------------------------------------------------- home */

.intro {
  padding-top: 1.5rem; /* hero sits high, well under the pt-24 cap */
  padding-bottom: 4.5rem;
}

/* The header mark is a terminal prompt glyph; this line says so instead of
   leaving it as a 20px icon nobody reads. */
.intro-prompt {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 1.1rem;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.intro-cursor {
  display: inline-block;
  width: 0.5em;
  height: 1em;
  background: var(--accent);
  vertical-align: text-bottom;
}

@media (prefers-reduced-motion: no-preference) {
  .intro-cursor {
    animation: cursor-blink 1.05s step-end infinite;
  }
}

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

.intro-title {
  font-size: clamp(2.125rem, 5vw, 3.25rem);
  letter-spacing: -0.03em;
  line-height: 1.08;
  max-width: 26ch;
}

.intro-lede {
  margin-top: 1.25rem;
  max-width: 46ch;
  font-size: 1.0625rem;
  color: var(--text-muted);
}

.intro-actions {
  margin-top: 2rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.home-terminal {
  padding-block: 3rem;
  border-top: 1px solid var(--border);
}

/* A decorative macOS-style window chrome around the filterable tools/notes
   list. The traffic-light dots are fixed brand colours, not theme tokens:
   that's what makes them read as "real" window controls in both themes. */
.term-window {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-subtle);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.term-titlebar {
  display: flex;
  align-items: center;
  padding: 0.65rem 1rem;
  background: var(--bg-inset);
  border-bottom: 1px solid var(--border);
}

.term-dots {
  display: flex;
  flex: none;
  gap: 0.4rem;
}

.term-dot {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
}

.term-dot-red {
  background: #ff5f57;
}

.term-dot-yellow {
  background: #febc2e;
}

.term-dot-green {
  background: #28c840;
}

.term-titlebar-name {
  flex: 1;
  margin-inline-end: 2.7rem; /* balances the dot cluster so the name reads centred */
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-muted);
}

.term-body {
  padding: 1.25rem 1.5rem 1.5rem;
}

.term-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.875rem;
}

.term-line .prompt-cmd {
  color: var(--text);
}

.term-results {
  list-style: none;
  margin-top: 1.25rem;
  border-top: 1px solid var(--border);
}

.term-result {
  border-bottom: 1px solid var(--border);
}

.term-result a {
  display: block;
  padding-block: 0.75rem;
  text-decoration: none;
  transition: color 0.15s var(--ease);
}

.term-result a:hover .term-result-title {
  color: var(--accent);
}

.term-result-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  font-size: 0.875rem;
}

.term-result-type {
  flex: none;
  width: 3.25rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.term-result-title {
  flex: 1;
  min-width: 0;
  color: var(--text);
  font-weight: 500;
  transition: color 0.15s var(--ease);
}

.term-result-date {
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-muted);
}

.term-result-desc {
  margin-top: 0.3rem;
  margin-inline-start: calc(3.25rem + 0.75rem);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.term-more {
  margin-top: 1rem;
  display: flex;
  gap: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
}

.term-more-sep {
  color: var(--border-strong);
}

.colophon {
  padding-top: 3rem;
  border-top: 1px solid var(--border);
}

.colophon-title {
  font-size: 1.25rem;
  margin-bottom: 1rem;
}

/* --------------------------------------------------------- page furniture */

.page-head {
  max-width: 58ch;
  margin-bottom: 3rem;
}

.page-title {
  font-size: clamp(1.875rem, 3.6vw, 2.5rem);
  letter-spacing: -0.025em;
}

.page-lede {
  margin-top: 1rem;
  color: var(--text-muted);
  font-size: 1.0625rem;
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
  font-size: 0.875rem;
  color: var(--text-muted);
}

.breadcrumb a {
  text-decoration: none;
}

.breadcrumb a:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* Muted rather than border-coloured: as a visible glyph it has to clear AA
   like any other text, even though it is aria-hidden. */
.breadcrumb-sep {
  color: var(--text-muted);
}

.group {
  margin-bottom: 3.5rem;
}

.group:last-child {
  margin-bottom: 0;
}

.group-title {
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--text-muted);
  padding-bottom: 0.75rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
}

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  white-space: nowrap; /* a CTA never wraps to a second line */
  padding: 0.6rem 1.1rem;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s var(--ease), border-color 0.15s var(--ease),
    color 0.15s var(--ease), transform 0.1s var(--ease);
}

.btn:active {
  transform: translateY(1px);
}

.btn[hidden] {
  display: none;
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
}

.btn-primary:hover {
  background: var(--accent-hover);
  color: var(--accent-contrast);
}

.btn-quiet {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text);
}

.btn-quiet:hover {
  background: var(--accent-wash);
  border-color: var(--accent);
  color: var(--accent);
}

.btn-small {
  padding: 0.4rem 0.7rem;
  font-size: 0.8125rem;
}

.btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn[disabled]:active {
  transform: none;
}

/* ------------------------------------------------------------ tools index

   A flat, filterable index rather than a card grid: cards were the one boxy,
   templated-looking pattern on the site, and stopped scaling once there was
   more than a screenful of tools. The toolbar stays reachable via position:
   sticky as the list grows, so filtering replaces scrolling. */

.tools-toolbar {
  position: sticky;
  top: 4rem; /* sits right under .site-header, which is also 4rem tall */
  z-index: 10;
  /* No margin-top: it sits right after .page-head, whose own margin-bottom
     already provides the gap. */
  padding-block: 0.85rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 0.75rem;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--border);
}

@supports not (backdrop-filter: blur(1px)) {
  .tools-toolbar {
    background: var(--bg);
  }
}

.tools-search {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  flex: 1 1 14rem;
  max-width: 20rem;
  transition: border-color 0.15s var(--ease);
}

.tools-search:focus-within {
  border-color: var(--accent);
}

.tools-search-icon {
  flex: none;
  width: 0.9rem;
  height: 0.9rem;
  color: var(--text-muted);
}

.tools-search input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 0.875rem;
  color: var(--text);
  background: none;
  border: none;
}

.tools-search input:focus-visible {
  outline: none;
}

.tools-search input::placeholder {
  color: var(--text-muted);
}

.tools-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  /* Without a grow factor this shrinks to its own content width, so once it
     wraps the pills bunch up under the search box instead of spreading
     across the row that is actually available to them. */
  flex: 1 1 auto;
  min-width: 0;
}

.tools-pill {
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.32rem 0.75rem;
  color: var(--text-muted);
  background: none;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease),
    background-color 0.15s var(--ease);
}

.tools-pill:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.tools-pill[aria-pressed='true'] {
  color: var(--accent);
  background: var(--accent-wash);
  border-color: var(--accent);
}

.tools-count {
  margin-inline-start: auto;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.tool-group {
  margin-top: 2.5rem;
  margin-bottom: 0;
}

.tool-group-title {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.25rem;
}

.tool-rows {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 2rem;
}

.tool-row {
  position: relative;
  display: block;
  padding: 0.8rem 0.25rem 0.8rem 0.9rem;
  margin-inline: -0.25rem;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  transition: background-color 0.15s var(--ease);
}

.tool-row::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.5rem;
  bottom: 0.5rem;
  width: 2px;
  background: transparent;
  transition: background-color 0.15s var(--ease);
}

.tool-row:hover {
  background: var(--bg-subtle);
}

.tool-row:hover::before {
  background: var(--accent);
}

.tool-row-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.tool-row-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.tool-row-title {
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--text);
  transition: color 0.15s var(--ease);
}

.tool-row:hover .tool-row-title {
  color: var(--accent);
}

.tool-row-desc {
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  color: var(--text-muted);
  line-height: 1.5;
}

.tools-empty {
  margin-top: 2.5rem;
  padding: 2.5rem 0;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9375rem;
}

/* ------------------------------------------------------------ notes index */

.note-list {
  list-style: none;
}

.note-row {
  border-bottom: 1px solid var(--border);
}

.note-row:last-child {
  border-bottom: 0;
}

.note-row-link {
  display: block;
  padding-block: 1.1rem;
  text-decoration: none;
}

.note-row-title {
  font-size: 1.0625rem;
  color: var(--text);
  transition: color 0.15s var(--ease);
}

.note-row-link:hover .note-row-title {
  color: var(--accent);
}

.note-row-desc {
  margin-top: 0.35rem;
  font-size: 0.9375rem;
  color: var(--text-muted);
  max-width: 62ch;
}

.note-row-meta,
.note-meta {
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  color: var(--text-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.meta-sep::before {
  content: '';
  display: inline-block;
  width: 1px;
  height: 0.8em;
  margin-right: 0.75rem;
  background: var(--border-strong);
  vertical-align: -0.05em;
}

.row-tags,
.note-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding-bottom: 1.1rem;
}

.note-tags {
  margin-top: 1rem;
  padding-bottom: 0;
}

.tag {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  padding: 0.15rem 0.45rem;
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  color: var(--text-muted);
}

/* ------------------------------------------------------------- note pages */

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

.note-head {
  padding-bottom: 1.75rem;
  margin-bottom: 2.25rem;
  border-bottom: 1px solid var(--border);
}

.contents {
  padding: 1.25rem 1.5rem;
  margin-bottom: 2.5rem;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.contents-title {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-muted);
  margin-bottom: 0.65rem;
}

.contents-list {
  list-style: none;
  display: grid;
  gap: 0.4rem;
  font-size: 0.9375rem;
}

.contents-list a {
  text-decoration: none;
}

.contents-list a:hover {
  color: var(--accent);
  text-decoration: underline;
}

.nearby {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-top: 4rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border);
  max-width: var(--measure);
}

.nearby-link {
  display: block;
  padding: 1rem 1.1rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  text-decoration: none;
  color: var(--text);
  font-size: 0.9375rem;
  transition: border-color 0.15s var(--ease), background-color 0.15s var(--ease),
    color 0.15s var(--ease);
}

.nearby-link:hover {
  border-color: var(--accent);
  background: var(--accent-wash);
  color: var(--accent);
}

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

.nearby-dir {
  display: block;
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-bottom: 0.25rem;
}

/* ------------------------------------------------------------------ prose */

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

.prose-narrow {
  max-width: var(--measure);
}

.prose > * + * {
  margin-top: 1.25rem;
}

.prose h2 {
  margin-top: 2.75rem;
  font-size: 1.375rem;
}

.prose h3 {
  margin-top: 2rem;
  font-size: 1.125rem;
}

.prose h2 + *,
.prose h3 + * {
  margin-top: 0.75rem;
}

.prose ul,
.prose ol {
  padding-inline-start: 1.35rem;
}

.prose li + li {
  margin-top: 0.4rem;
}

.prose li::marker {
  color: var(--text-muted);
}

.prose strong {
  font-weight: 600;
}

.prose code {
  padding: 0.12em 0.35em;
  border-radius: 4px;
  background: var(--bg-inset);
  color: var(--text);
}

.prose pre {
  margin-top: 1.25rem;
  padding: 1rem 1.1rem;
  overflow-x: auto;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-size: 0.875rem;
  line-height: 1.6;
}

.prose pre code {
  padding: 0;
  background: none;
  font-size: inherit;
}

.prose blockquote {
  margin: 1.75rem 0;
  padding-left: 1.25rem;
  border-left: 2px solid var(--accent);
  color: var(--text-muted);
}

.prose blockquote p + p {
  margin-top: 0.75rem;
}

.prose hr {
  margin-block: 2.5rem;
  border: 0;
  border-top: 1px solid var(--border);
}

.prose figure {
  margin: 2rem 0;
}

.prose figcaption {
  margin-top: 0.6rem;
  font-size: 0.875rem;
  color: var(--text-muted);
}

.prose img {
  border-radius: var(--r-md);
  border: 1px solid var(--border);
}

/* Wide tables scroll inside their own box; the page never scrolls sideways. */
.prose .table-scroll,
.table-scroll {
  overflow-x: auto;
  margin-top: 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.prose table,
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.prose table {
  display: block;
  overflow-x: auto;
}

.prose th,
.prose td,
table.data th,
table.data td {
  text-align: left;
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.prose tbody tr:last-child td,
table.data tbody tr:last-child td {
  border-bottom: 0;
}

.prose th,
table.data th {
  font-weight: 500;
  font-size: 0.8125rem;
  color: var(--text-muted);
  background: var(--bg-subtle);
}

table.data td.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- 404 */

.notfound {
  max-width: 50ch;
  padding-block: 2rem 4rem;
}

.notfound-code {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  color: var(--text-muted);
  margin-bottom: 1rem;
}

/* ==========================================================================
   Tool primitives

   Every tool page is built from these, so a new tool is markup plus a small
   script rather than new CSS. Keep additions here rather than inline.
   ========================================================================== */

.tool-panel {
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-subtle);
}

.tool-panel + .tool-panel {
  margin-top: 1.25rem;
}

.tool-notes {
  max-width: var(--measure);
  margin-top: 3.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border);
}

.tool-notes-title {
  font-size: 1.0625rem;
  margin-bottom: 1rem;
}

/* fields ------------------------------------------------------------------ */

.field-row {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.field {
  display: grid;
  align-content: start;
  gap: 0.4rem;
  min-width: 0;
}

.field > label,
.field-label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text);
}

.field input[type='text'],
.field input[type='number'],
.field input[type='password'],
.field input[type='search'],
.field input[type='email'],
.field input[type='url'],
.field input[type='tel'],
.field input[type='date'],
.field input[type='time'],
.field input[type='datetime-local'],
.field select,
.field textarea,
.input {
  width: 100%;
  padding: 0.55rem 0.7rem;
  font: inherit;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

/* A single-line input and a <select> with identical padding still don't
   match: Chromium's number spin-button forces extra intrinsic height on
   input[type=number], and <select> ignores the inherited line-height in
   favour of its own UA default. Fixing the height explicitly is the only
   way to make every control in a field-row the same size. Excludes
   textarea, which sizes itself via min-height instead.

   The type list here and above must stay in sync: any text-like input type
   a tool uses has to appear in both, or it falls back to completely
   unstyled UA chrome (this is exactly how type=search and
   type=datetime-local went unstyled until this was caught). */
.field input[type='text'],
.field input[type='number'],
.field input[type='password'],
.field input[type='search'],
.field input[type='email'],
.field input[type='url'],
.field input[type='tel'],
.field input[type='date'],
.field input[type='time'],
.field input[type='datetime-local'],
.field select,
.input:not(textarea) {
  height: 2.5rem;
}

.field textarea,
textarea.input {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  line-height: 1.6;
  min-height: 8rem;
  resize: vertical;
}

.field input.mono,
.field select.mono,
.input.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.field input:hover,
.field select:hover,
.field textarea:hover,
.input:hover {
  border-color: var(--accent);
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible,
.input:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

/* Placeholder contrast is checked: this sits above 4.5:1 on both grounds. */
.field ::placeholder,
.input::placeholder {
  color: var(--text-muted);
  opacity: 1;
}

.field-hint {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.field-error {
  font-size: 0.8125rem;
  color: var(--error);
  font-weight: 500;
}

.field-error:empty {
  display: none;
}

.field.has-error input,
.field.has-error select,
.field.has-error textarea {
  border-color: var(--error);
}

/* checkbox and radio rows ------------------------------------------------- */

.check-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
}

.check {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.9375rem;
  cursor: pointer;
  transition: color 0.15s var(--ease);
}

.check:hover {
  color: var(--accent);
}

.check input {
  accent-color: var(--accent);
  width: 1rem;
  height: 1rem;
  margin: 0;
  cursor: pointer;
}

/* actions ---------------------------------------------------------------- */

.tool-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.25rem;
}

.tool-actions .spacer {
  margin-inline-start: auto;
}

/* results ---------------------------------------------------------------- */

.result {
  margin-top: 1.25rem;
}

/* Grid lines come from each item's own border, not gaps over a coloured
   container background: with auto-fit columns the last row is often
   incomplete, and a leftover empty track would otherwise expose the
   container's own background as a solid block the width of a full cell. An
   item-less track has nothing to paint over it, so it just stays blank. */
.result-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}

.result-item {
  padding: 0.85rem 1rem;
  background: var(--bg);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

/* When the last row is incomplete, the final item is not in the grid's
   last column, so its own border-right lands mid-row with nothing beside
   it: a short vertical tick floating in otherwise blank space. It always
   sits at the true right edge of the box only when the last row happens
   to be full, in which case dropping it changes nothing. */
.result-item:last-child {
  border-right: none;
}

.result-label {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-bottom: 0.25rem;
}

.result-value {
  font-family: var(--font-mono);
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  word-break: break-word;
}

.result-value.lg {
  font-size: 1.25rem;
}

.output {
  width: 100%;
  padding: 0.85rem 1rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--font-mono);
  font-size: 0.875rem;
  line-height: 1.65;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  min-height: 3rem;
}

/* empty and status states ------------------------------------------------ */

.empty {
  padding: 1.75rem 1rem;
  text-align: center;
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-md);
  color: var(--text-muted);
  font-size: 0.9375rem;
}

.status {
  margin-top: 0.85rem;
  font-size: 0.875rem;
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
}

.status:empty {
  display: none;
}

.status[data-state='ok'] {
  color: var(--ok);
}

.status[data-state='warn'] {
  color: var(--warn);
}

.status[data-state='error'] {
  color: var(--error);
}

/* role lookup: search-and-browse list beside a detail panel. Generic enough
   under the .role- prefix that a second reference tool could reuse it. ---- */

.risk-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.85rem;
}

.risk-filter-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  padding: 0.35rem 0.75rem;
  color: var(--text-muted);
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}

.risk-filter-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.risk-filter-btn[data-risk='all'][aria-current='true'] {
  color: var(--accent);
  background: var(--accent-wash);
  border-color: var(--accent);
}

.risk-filter-btn.risk-high[aria-current='true'] {
  color: var(--error);
  background: color-mix(in srgb, var(--error) 14%, transparent);
  border-color: var(--error);
}

.risk-filter-btn.risk-medium[aria-current='true'] {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 16%, transparent);
  border-color: var(--warn);
}

.risk-filter-btn.risk-low[aria-current='true'] {
  color: var(--ok);
  background: color-mix(in srgb, var(--ok) 14%, transparent);
  border-color: var(--ok);
}

.role-layout {
  display: grid;
  grid-template-columns: 16rem 1fr;
  gap: 1.25rem;
  align-items: start;
  margin-top: 1.25rem;
}

.role-list {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  height: 32rem;
  overflow-y: auto;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-subtle);
}

.role-list-item {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  min-height: 3.4rem;
  padding: 0.55rem 0.65rem;
  font: inherit;
  font-size: 0.875rem;
  line-height: 1.3;
  text-align: left;
  color: var(--text);
  background: none;
  border: 0;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color 0.15s var(--ease), background-color 0.15s var(--ease);
}

.role-list-item:hover {
  background: var(--accent-wash);
  color: var(--accent);
}

.role-list-item[aria-current='true'] {
  background: var(--accent-wash);
  color: var(--accent);
  font-weight: 500;
}

.role-risk-dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: 50%;
}

.role-risk-dot.risk-high {
  background: var(--error);
}

.role-risk-dot.risk-medium {
  background: var(--warn);
}

.role-risk-dot.risk-low {
  background: var(--ok);
}

.role-detail {
  height: 32rem;
  overflow-y: auto;
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg);
}

.role-detail-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem 1.25rem;
}

.role-detail-title {
  font-size: 1.25rem;
}

.role-detail-desc {
  margin-top: 0.6rem;
  color: var(--text-muted);
  font-size: 0.9375rem;
  max-width: var(--measure);
}

.risk-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex: none;
  padding: 0.3rem 0.65rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-radius: 999px;
}

.risk-badge::before {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
}

.risk-badge.risk-high {
  color: var(--error);
  background: color-mix(in srgb, var(--error) 14%, transparent);
}

.risk-badge.risk-medium {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 16%, transparent);
}

.risk-badge.risk-low {
  color: var(--ok);
  background: color-mix(in srgb, var(--ok) 14%, transparent);
}

.role-section {
  margin-top: 1.5rem;
}

.role-section-label {
  margin-bottom: 0.6rem;
  font-size: 0.75rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.role-cap-list {
  list-style: none;
  display: grid;
  gap: 0.45rem;
}

.role-cap-list li {
  position: relative;
  padding-left: 1.1rem;
  font-size: 0.9375rem;
}

.role-cap-list li::before {
  content: '\2013';
  position: absolute;
  left: 0;
  color: var(--text-muted);
}

.role-use-case {
  font-size: 0.9375rem;
  max-width: var(--measure);
}

.role-alt-list {
  list-style: none;
  display: grid;
  gap: 0.6rem;
}

.role-alt-item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.6rem;
}

.role-alt-chip {
  flex: none;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  padding: 0.3rem 0.7rem;
  color: var(--text);
  background: var(--bg-subtle);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  cursor: pointer;
}

.role-alt-chip:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.role-alt-reason {
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.role-alt-empty {
  font-size: 0.875rem;
  color: var(--text-muted);
}

/* CA simulator: policy builder cards, tag inputs and a decision banner.
   Generic enough under these names for a second what-if-style tool. ------- */

.notice {
  max-width: var(--measure);
  margin-bottom: 1.75rem;
  padding: 0.9rem 1.1rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--text);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 35%, var(--border));
  border-radius: var(--r-md);
}

.notice a {
  color: inherit;
  text-decoration: underline;
}

.tool-panel-title {
  font-size: 1.0625rem;
}

.hidden-field {
  display: none !important;
}

.tag-input {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.6rem;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

.tag-input:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

.tag-list {
  display: contents;
}

.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.3rem 0.2rem 0.55rem;
  font-size: 0.8125rem;
  color: var(--text);
  background: var(--bg-inset);
  border-radius: 999px;
}

.tag-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  font-size: 0.9375rem;
  line-height: 1;
  color: var(--text-muted);
  background: none;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.tag-chip-remove:hover {
  color: var(--error);
  background: color-mix(in srgb, var(--error) 14%, transparent);
}

.tag-input input {
  flex: 1 1 8rem;
  min-width: 8rem;
  padding: 0.2rem 0;
  font: inherit;
  font-size: 0.875rem;
  color: var(--text);
  background: none;
  border: 0;
}

.tag-input input:focus-visible {
  outline: none;
}

.policy-list {
  display: grid;
  gap: 0.85rem;
}

.policy-card {
  padding: 1rem 1.1rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.policy-card.policy-disabled {
  opacity: 0.55;
}

.policy-card.policy-card-blocking {
  border-color: var(--error);
}

.policy-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
}

.policy-toggle {
  font-size: 0.9375rem;
}

.policy-name {
  font-weight: 500;
  color: var(--text);
}

.policy-card-actions {
  display: flex;
  gap: 0.4rem;
}

.policy-line {
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  color: var(--text-muted);
}

.policy-controls {
  font-family: var(--font-mono);
}

.policy-controls-block {
  color: var(--error);
}

.policy-off {
  font-style: italic;
}

.decision-banner {
  margin-top: 1.25rem;
  display: grid;
  gap: 0.35rem;
  padding: 1rem 1.1rem;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
}

.decision-label {
  font-size: 1.0625rem;
  font-weight: 600;
}

.decision-detail {
  font-size: 0.875rem;
  color: var(--text-muted);
}

.decision-banner.decision-allow {
  border-color: color-mix(in srgb, var(--ok) 40%, var(--border));
  background: color-mix(in srgb, var(--ok) 10%, transparent);
}

.decision-banner.decision-allow .decision-label {
  color: var(--ok);
}

.decision-banner.decision-controls {
  border-color: color-mix(in srgb, var(--warn) 40%, var(--border));
  background: color-mix(in srgb, var(--warn) 10%, transparent);
}

.decision-banner.decision-controls .decision-label {
  color: var(--warn);
}

.decision-banner.decision-block {
  border-color: color-mix(in srgb, var(--error) 40%, var(--border));
  background: color-mix(in srgb, var(--error) 10%, transparent);
}

.decision-banner.decision-block .decision-label {
  color: var(--error);
}

/* ------------------------------------------------------------- responsive */

@media (max-width: 480px) {
  .term-titlebar-name {
    display: none;
  }
}

@media (max-width: 768px) {
  main {
    padding-block: 2.5rem 3.5rem;
  }

  .shell {
    padding-inline: 1.25rem;
  }

  /* High-variance layouts collapse to a single column below 768px. */
  .tool-rows,
  .nearby,
  .result-grid,
  .field-row,
  .role-layout {
    grid-template-columns: 1fr;
  }

  .role-list {
    height: 16rem;
  }

  .role-detail {
    height: auto;
    overflow-y: visible;
  }

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

  .intro-title {
    font-size: clamp(1.875rem, 8vw, 2.25rem);
  }

  .page-head {
    margin-bottom: 2.25rem;
  }

  .tool-panel {
    padding: 1.1rem;
  }

  .footer-inner {
    flex-direction: column;
    gap: 0.75rem;
  }
}

@media (max-width: 560px) {
  .header-inner {
    gap: 0.85rem;
  }

  .nav {
    gap: 1rem;
  }

  .nav-link {
    font-size: 0.875rem;
  }

  .theme-toggle {
    width: 2rem;
    height: 2rem;
  }

  .btn {
    width: 100%;
  }

  .intro-actions {
    flex-direction: column;
  }
}

/* At the narrowest widths the wordmark yields so nav stays on one line. */
@media (max-width: 380px) {
  .wordmark {
    font-size: 0.9375rem;
  }
}

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .btn:active,
  .theme-toggle:active {
    transform: none;
  }
}

/* ------------------------------------------------------------------ print */

@media print {
  .site-header,
  .site-footer,
  .nearby,
  .skip-link,
  .tool-actions {
    display: none;
  }

  body {
    background: #fff;
    color: #000;
  }
}
