/* Tokens read from thomascabral.tech's compiled stylesheet, so the blog and
   the personal site read as one property. */
:root {
  --ground:      #f8fafc;  /* slate-50  */
  --surface:     #ffffff;
  --ink:         #1e293b;  /* slate-800 */
  --ink-strong:  #0f172a;  /* slate-900 */
  --ink-muted:   #64748b;  /* slate-500 */
  --ink-faint:   #94a3b8;  /* slate-400 */
  --rule:        #e2e8f0;  /* slate-200 */
  --rule-soft:   #f1f5f9;  /* slate-100 */
  --accent:      #0891b2;  /* cyan-600, darkened from #22d3ee for text contrast */
  --accent-from: #22d3ee;  /* cyan-400 */
  --accent-to:   #3b82f6;  /* blue-500 */
  --code-ground: #0f172a;  /* slate-900 */

  --sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
          "Segoe UI", sans-serif;
  --mono: SFMono-Regular, "Cascadia Code", "Roboto Mono", ui-monospace,
          Menlo, Consolas, monospace;

  --measure: 68ch;
  --gutter: 5.5rem;   /* width of the year rail */
}

/* Dark theme.
   Applied when the reader's system asks for it, unless they have explicitly
   chosen light, and applied unconditionally when they have chosen dark. The
   toggle writes data-theme on <html>; a small script in the head reads it
   before first paint so the page never flashes the wrong theme. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  --ground:      #0b1220;
  --surface:     #111b2e;
  --ink:         #dbe4f0;
  --ink-strong:  #f1f5f9;
  --ink-muted:   #94a3b8;
  --ink-faint:   #64748b;
  --rule:        #1f2b40;
  --rule-soft:   #172136;
  /* Cyan-600 is a light-background choice; on a dark ground it goes muddy.
     These are the brighter cyan-400 and blue-400 from the same ramp. */
  --accent:      #38d6ee;
  --accent-from: #22d3ee;
  --accent-to:   #60a5fa;
  --code-ground: #060c17;
  }
}

:root[data-theme="dark"] {
  --ground:      #0b1220;
  --surface:     #111b2e;
  --ink:         #dbe4f0;
  --ink-strong:  #f1f5f9;
  --ink-muted:   #94a3b8;
  --ink-faint:   #64748b;
  --rule:        #1f2b40;
  --rule-soft:   #172136;
  /* Cyan-600 is a light-background choice; on a dark ground it goes muddy.
     These are the brighter cyan-400 and blue-400 from the same ramp. */
  --accent:      #38d6ee;
  --accent-from: #22d3ee;
  --accent-to:   #60a5fa;
  --code-ground: #060c17;
}

/* Tell the browser which form controls and scrollbars to draw. */
:root[data-theme="dark"] { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.7;
  font-feature-settings: "cv02", "cv03", "cv04", "cv11";
  -webkit-font-smoothing: antialiased;
}

/* Keyboard users must always be able to see where they are. */
:focus-visible {
  outline: 2px solid var(--accent-to);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: 1rem;
  top: 1rem;
  z-index: 10;
  background: var(--surface);
  padding: 0.5rem 0.875rem;
  border-radius: 6px;
  box-shadow: 0 1px 3px rgb(15 23 42 / 0.15);
}

.shell {
  max-width: 62rem;
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* An article still reads at ~68ch; only the index and the lead use the
   extra width. */
.article { max-width: calc(var(--measure) + 8rem); }

/* ---------- masthead ---------- */

.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.75rem 2rem;
  padding: 2.5rem 0 1.75rem;
  border-bottom: 1px solid var(--rule);
}

.masthead__title {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 650;
  letter-spacing: -0.015em;
}

.masthead__title a { color: var(--ink-strong); text-decoration: none; }

/* The one place the cyan-to-blue gradient appears at full strength. */
.masthead__title a::after {
  content: "";
  display: block;
  height: 2px;
  margin-top: 0.3rem;
  background: linear-gradient(90deg, var(--accent-from), var(--accent-to));
  border-radius: 2px;
}

.masthead__tagline {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.9375rem;
}

.masthead__nav {
  margin-left: auto;
  display: flex;
  gap: 1.25rem;
  font-size: 0.9375rem;
}

.masthead__nav a {
  color: var(--ink-muted);
  text-decoration: none;
}
.masthead__nav a:hover { color: var(--accent); }

/* ---------- signal ----------
   A generated backsplash drawn from the post itself: one bar per section,
   each bar's height that section's share of the longest one. Two posts look
   different because they are structured differently, not because of a random
   seed. Hue is stable per slug; a post that is substantially code gets a
   segmented treatment.

   Everything here is CSS driven by custom properties the server sets. There
   is no canvas, no script, and nothing to download. */

.signal {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 0;
  height: 100%;
  width: 100%;
  overflow: hidden;
  border-radius: inherit;
  background:
    radial-gradient(130% 100% at 20% 100%,
      hsl(var(--hue) 80% 40% / 0.28), transparent 62%),
    radial-gradient(120% 90% at 85% 8%,
      hsl(calc(var(--hue) + 30) 88% 58% / 0.22), transparent 68%),
    var(--code-ground);
  isolation: isolate;
}

/* Each span is one section of the post, carrying four values the server
   computed from the article: its height (section length), its own hue
   (fanned out by how irregular the writing is), its width (how far that
   section departs from the average), and its own cycle length (so a
   restless post never settles into one rhythm).

   It draws twice: a wide soft glow that gives the field its atmosphere,
   and a thin bright cap that traces the actual silhouette. Solid bars read
   as a chart; this reads as light. */
.signal span {
  position: relative;
  flex: var(--g, 1) 1 0;
  min-width: 0;
  height: var(--h);
  transform-origin: bottom;
  animation: signal-breathe var(--dur, var(--pace)) ease-in-out infinite alternate;
  animation-delay: var(--d);
  will-change: transform;
}

.signal span::before {
  content: "";
  position: absolute;
  /* A chaotic post spreads its glow wider, so the field blurs together
     rather than resolving into separate columns. */
  inset: -6% calc(-25% - var(--chaos) * 30%) 0 calc(-25% - var(--chaos) * 30%);
  border-radius: 40% 40% 0 0;
  background: linear-gradient(
    to top,
    hsl(var(--bh, var(--hue)) 85% 52% / 0.42),
    hsl(calc(var(--bh, var(--hue)) + 28) 92% 68% / 0.55));
  filter: blur(calc(12px + var(--chaos) * 10px));
}

.signal span::after {
  content: "";
  position: absolute;
  top: 0;
  left: 6%;
  right: 6%;
  height: 2px;
  border-radius: 2px;
  background: hsl(calc(var(--bh, var(--hue)) + 24) 95% 74% / 0.85);
  box-shadow: 0 0 12px hsl(calc(var(--bh, var(--hue)) + 24) 95% 70% / 0.65);
}

/* A restless post tilts its caps, so the trace reads as unsteady rather
   than as a level series of steps. */
.signal--restless span::after {
  transform: rotate(calc((var(--g) - 1) * 2.5deg));
}

/* A post that is substantially code gets a rung pattern climbing each
   section, so density is visible at a glance. */
.signal--dense span::before {
  background-image:
    linear-gradient(to top,
      hsl(var(--bh, var(--hue)) 85% 52% / 0.42),
      hsl(calc(var(--bh, var(--hue)) + 28) 92% 68% / 0.55)),
    repeating-linear-gradient(to top,
      transparent 0 7px, hsl(var(--hue) 20% 4% / 0.7) 7px 9px);
  filter: blur(calc(7px + var(--chaos) * 6px));
}

@keyframes signal-breathe {
  from { transform: scaleY(0.86); }
  to   { transform: scaleY(1.05); }
}

/* Two drifting light fields, at different speeds and directions, so the
   surface never repeats visibly. Chaos raises their reach and opacity. */
.signal::before,
.signal::after {
  content: "";
  position: absolute;
  inset: -20%;
  pointer-events: none;
  z-index: 2;
}

.signal::before {
  background: radial-gradient(45% 60% at 30% 70%,
    hsl(calc(var(--hue) + 40) 95% 70% / calc(0.12 + var(--chaos) * 0.16)),
    transparent 70%);
  animation: signal-drift calc(var(--pace) * 6) ease-in-out infinite alternate;
}

.signal::after {
  background: linear-gradient(100deg,
    transparent 34%,
    hsl(var(--hue) 95% 80% / calc(0.08 + var(--chaos) * 0.12)) 50%,
    transparent 64%);
  background-size: 260% 100%;
  animation: signal-sweep calc(var(--pace) * 4) linear infinite;
}

@keyframes signal-drift {
  from { transform: translate3d(-12%, 6%, 0) scale(1); }
  to   { transform: translate3d(14%, -8%, 0) scale(1.15); }
}

@keyframes signal-sweep {
  from { background-position: 150% 0; }
  to   { background-position: -70% 0; }
}

/* ---------- lead post ----------
   The newest post carries the front page. Everything below it is a quiet,
   scannable index, so this is the only place on the page that spends
   contrast, scale, or an image. */

.lead {
  padding: 2.25rem 0 0;
}

.lead__link {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 2rem;
  align-items: center;
  text-decoration: none;
  color: inherit;
  padding: 1.25rem;
  margin: 0 -1.25rem;
  border-radius: 12px;
  border: 1px solid transparent;
  transition: border-color 160ms ease, background-color 160ms ease;
}
.lead__link:hover,
.lead__link:focus-visible {
  border-color: var(--rule);
  background: var(--surface);
}

/* No feature image: the text takes the full width rather than leaving a
   hole where the picture would have been, and carries more typographic
   weight so the lead still reads as the lead. The two most recent posts
   have no feature image, so this is the common case, not the exception. */
/* A hairline in the accent gradient, tying the lead to the masthead rule. */
.lead__eyebrow::after {
  content: "";
  display: block;
  width: 2.5rem;
  height: 2px;
  margin-top: 0.55rem;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent-from), var(--accent-to));
}

.lead__media {
  position: relative;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--rule);
  aspect-ratio: 16 / 10;
}

/* When a post has a real feature image, it sits over the signal, which then
   only shows as a coloured ground while the image loads. */
.lead__media img {
  position: absolute;
  inset: 0;
}
.lead__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.lead__eyebrow {
  margin: 0 0 0.6rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}

.lead__title {
  margin: 0 0 0.7rem;
  font-size: clamp(1.6rem, 1.1rem + 1.6vw, 2.3rem);
  font-weight: 680;
  line-height: 1.15;
  letter-spacing: -0.03em;
  color: var(--ink-strong);
  overflow-wrap: break-word;
}

.lead__excerpt {
  margin: 0 0 0.8rem;
  color: var(--ink-muted);
  font-size: 1.0625rem;
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- archive heading ---------- */

.archive__head {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  padding: 2rem 0 0;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 0.25rem;
}
.archive__title {
  margin: 0 0 0.6rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 500;
}
.archive__summary {
  margin: 0 0 0.6rem;
  margin-left: auto;
  font-size: 0.8125rem;
  color: var(--ink-faint);
}

/* The toggle is a quiet control: it should not compete with the nav. */
.theme-toggle {
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--ink-muted);
  cursor: pointer;
  padding: 0.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
}
.theme-toggle:hover { color: var(--accent); border-color: var(--rule); }
.theme-toggle svg { width: 1.05rem; height: 1.05rem; display: block; }

/* Each icon shows only in the theme it switches away from. */
.theme-toggle .icon-moon { display: block; }
.theme-toggle .icon-sun  { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun  { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .icon-sun  { display: block; }
}

/* ---------- archive: posts grouped under the year they were written ----------
   The archive spans Dec 2022 to 2026 with long gaps. A flat reverse-
   chronological list hides that; the year rail makes the real cadence part
   of the page. */

.archive { padding: 1rem 0 4rem; }

.year {
  display: grid;
  grid-template-columns: var(--gutter) minmax(0, 1fr);
  gap: 0 1.5rem;
  padding: 2.25rem 0 0.5rem;
}

.year__label {
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--ink-faint);
  letter-spacing: 0.06em;
  padding-top: 0.55rem;
  position: sticky;
  top: 1.5rem;
  align-self: start;
}

.year__count {
  display: block;
  font-size: 0.6875rem;
  color: var(--ink-faint);
  letter-spacing: 0.02em;
  margin-top: 0.1rem;
}

.year__posts { list-style: none; margin: 0; padding: 0; }

.entry {
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--rule-soft);
}
.entry:last-child { border-bottom: 0; }

.entry__link {
  overflow-wrap: break-word;
  color: var(--ink-strong);
  text-decoration: none;
  font-weight: 600;
  font-size: 1.1875rem;
  letter-spacing: -0.018em;
  line-height: 1.35;
  display: inline-block;
  background-image: linear-gradient(90deg, var(--accent-from), var(--accent-to));
  background-size: 0% 2px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size 180ms ease;
}
.entry__link:hover,
.entry__link:focus-visible { background-size: 100% 2px; }

.entry__excerpt {
  margin: 0.3rem 0 0;
  color: var(--ink-muted);
  font-size: 0.9375rem;
  line-height: 1.55;
  /* Two lines in the index. The lead is where a summary gets room. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.4rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-faint);
  letter-spacing: 0.01em;
}
.meta__dot { color: var(--rule); }

/* Tags sit on their own row. Inline, they wrapped mid-line and stranded a
   separator dot at the end of the previous line. */
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.5rem 0 0;
  padding: 0;
}

.tag {
  color: var(--accent);
  text-decoration: none;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--surface);
  white-space: nowrap;
}
.tag:hover,
.tag:focus-visible {
  border-color: var(--accent-from);
  color: var(--ink-strong);
}

.badge-draft {
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: #b45309;
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: 3px;
  padding: 0.05rem 0.35rem;
}

/* ---------- article ---------- */

.article { padding: 2.5rem 0 5rem; }

/* On an article the signal is a band above the title: present, but not
   competing with the first paragraph. */
.article > .signal {
  position: relative;
  height: 7.5rem;
  border-radius: 10px;
  border: 1px solid var(--rule);
  margin-bottom: 1.75rem;
  padding: 0 0.5rem;
}

.article__header {
  max-width: var(--measure);
  margin: 0 0 2rem;
}

.article__title {
  margin: 0 0 0.6rem;
  overflow-wrap: break-word;
  font-size: clamp(1.875rem, 1.4rem + 2vw, 2.5rem);
  font-weight: 680;
  line-height: 1.15;
  letter-spacing: -0.03em;
  color: var(--ink-strong);
}

.article__lede {
  margin: 0;
  color: var(--ink-muted);
  font-size: 1.125rem;
  line-height: 1.6;
}

.article__feature {
  display: block;
  width: 100%;
  /* Ghost feature images are arbitrary stock photos at arbitrary aspect
     ratios. Uncapped, a tall one pushes the first paragraph off screen. */
  max-height: 22rem;
  object-fit: cover;
  border-radius: 8px;
  margin: 0 0 2rem;
  border: 1px solid var(--rule);
}

.prose {
  max-width: var(--measure);
  font-size: 1.125rem;
  line-height: 1.75;
  /* A long unbroken URL or identifier would otherwise push the whole page
     sideways on a phone. */
  overflow-wrap: break-word;
}

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

.prose h2, .prose h3 {
  color: var(--ink-strong);
  letter-spacing: -0.02em;
  line-height: 1.3;
  margin-top: 2.25em;
  margin-bottom: 0.6em;
}
.prose h2 { font-size: 1.4375rem; font-weight: 650; }
.prose h3 { font-size: 1.1875rem; font-weight: 650; }

.prose a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.prose strong { font-weight: 640; color: var(--ink-strong); }

.prose img {
  max-width: 100%;
  height: auto;
  border-radius: 6px;
  border: 1px solid var(--rule);
}

/* Photographs are still lit for a white page; dimming them slightly stops
   them glaring out of a dark one. */
:root[data-theme="dark"] .prose img,
:root[data-theme="dark"] .article__feature { filter: brightness(0.88); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .prose img,
  :root:not([data-theme="light"]) .article__feature { filter: brightness(0.88); }
}

.prose ul, .prose ol { padding-left: 1.35em; }
.prose li + li { margin-top: 0.4em; }

.prose blockquote {
  margin-left: 0;
  padding-left: 1.1em;
  border-left: 2px solid var(--accent-from);
  color: var(--ink-muted);
}

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

/* Inline code stays light; blocks go dark, matching the personal site. */
.prose :not(pre) > code {
  font-family: var(--mono);
  font-size: 0.875em;
  background: var(--rule-soft);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.1em 0.32em;
  color: var(--ink-strong);
}

.prose pre {
  font-family: var(--mono);
  font-size: 0.8438rem;
  line-height: 1.65;
  background: var(--code-ground) !important;
  color: #e2e8f0;
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 1rem 1.1rem;
  /* Long lines scroll inside the block; the page never scrolls sideways. */
  overflow-x: auto;
}
.prose pre code {
  background: none;
  border: 0;
  padding: 0;
  font-size: inherit;
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
  display: block;
  overflow-x: auto;
}
.prose th, .prose td {
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--rule);
}
.prose th { font-weight: 620; color: var(--ink-strong); }

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

.page-head {
  padding: 2.5rem 0 0.5rem;
  max-width: var(--measure);
}
.page-head__eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin: 0 0 0.4rem;
}
.page-head__title {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 660;
  letter-spacing: -0.025em;
  color: var(--ink-strong);
}

.backlink {
  display: inline-block;
  margin-top: 3rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-muted);
  text-decoration: none;
}
.backlink:hover { color: var(--accent); }

.footer {
  border-top: 1px solid var(--rule);
  padding: 1.75rem 0 3rem;
  color: var(--ink-faint);
  font-size: 0.8438rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
}
.footer a { color: var(--ink-muted); text-decoration: none; }
.footer a:hover { color: var(--accent); }

.empty {
  max-width: var(--measure);
  padding: 3rem 0 5rem;
  color: var(--ink-muted);
}

/* ---------- narrow screens ---------- */

@media (max-width: 40rem) {
  body { font-size: 1rem; }
  .shell { padding: 0 1.125rem; }

  /* The masthead was three stacked rows and ~170px of chrome before any
     content. Title and nav share a row; the tagline is decorative here. */
  .masthead {
    padding: 1.25rem 0 0.875rem;
    gap: 0;
    align-items: center;
  }
  .masthead__tagline { display: none; }
  .masthead__nav {
    margin-left: auto;
    width: auto;
    gap: 0.25rem;
    font-size: 0.875rem;
  }
  /* Tap targets, not text targets. */
  .masthead__nav a {
    padding: 0.5rem 0.4rem;
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  /* Paddings tuned for the desktop year rail left a dead band on mobile. */
  .archive { padding: 0 0 3rem; }
  .year {
    grid-template-columns: 1fr;
    gap: 0;
    padding: 1.5rem 0 0;
  }
  .year__label {
    position: static;
    padding: 0 0 0.75rem;
    font-size: 0.75rem;
    color: var(--ink-muted);
    border-bottom: 1px solid var(--rule);
    margin-bottom: 0.25rem;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
  }
  .year__count { display: inline; margin: 0; }

  .entry { padding: 1rem 0; }
  .entry__link { font-size: 1.125rem; }

  /* A 280-character excerpt is a desktop length; six lines of grey text
     buries the next headline. */
  .entry__excerpt {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 0.9375rem;
  }

  .lead { padding: 1.25rem 0 0; }
  .lead__link {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.9rem;
    padding: 0;
    margin: 0;
    border: 0;
  }
  .lead__link:hover, .lead__link:focus-visible { background: none; border: 0; }
  .lead__media { aspect-ratio: 16 / 9; }
  .archive__head { padding: 2rem 0 0; }

  .article { padding: 1.5rem 0 3.5rem; }
  .article__title { letter-spacing: -0.02em; }
  /* 22rem is over 40% of a phone viewport. */
  .article__feature { max-height: 11rem; margin-bottom: 1.5rem; }

  .prose { font-size: 1.0625rem; }
  .prose pre {
    font-size: 0.8125rem;
    padding: 0.875rem;
    /* Bleed to the screen edges: a code block boxed inside 1.125rem of
       padding has almost no room left for the code. */
    margin-left: -1.125rem;
    margin-right: -1.125rem;
    border-radius: 0;
  }

  .page-head { padding: 1.5rem 0 0.25rem; }
  .footer { padding: 1.25rem 0 2.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
  /* The signal is information, not decoration, so it stays visible when
     motion is off. It simply stops moving, at full height. */
  .signal span { transform: none; animation: none; }
  .signal::before, .signal::after { animation: none; }
}
