/* ---------------------------------------------------------------- fonts -- */

@font-face {
  font-family: "Martian Mono";
  src: url("/static/fonts/martian-mono-latin.woff2") format("woff2");
  font-weight: 100 800;
  font-stretch: 75% 112.5%;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("/static/fonts/inter-variable-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}

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

:root {
  --black: #0c0b0b;
  --white: #f3f3f3;
  --blue-100: #2a79f0;
  --blue-200: #165cca;
  --sky: #a0d9ff;
  --grass: #d4ffbc;
  --shadow-blue: #4078e8;

  --paper: #ffffff;
  --sky-pale: #cfeaff;
  --ink-soft: #4a4746;

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

  /* Type scale. Primitives: only the text styles below may reference these. No component names
     a size directly — that rule is what keeps the scale from drifting. Four roles, one ladder
     each, named so a size's neighbours are obvious. */
  --text-heading-xl: clamp(2.5rem, 6.4vw, 4.15rem);
  --text-heading-lg: clamp(1.9rem, 4vw, 2.8rem);
  --text-heading-md: 1.36rem;
  --text-heading-sm: 1.15rem;
  --text-heading-xs: 0.92rem;

  --text-body-lg: 1.15rem;
  --text-body: 1.0625rem;
  --text-body-sm: 0.92rem;

  --text-label: 0.75rem;
  --text-code: 0.88rem;

  /* Inter's measured x-height, in em. Mono is normalised to it (font-size-adjust) so a
     monospace size means the same optical size as the equivalent body size. */
  --x-height-body: 0.546;

  --track-heading-xl: -0.045em;
  --track-heading: -0.02em;
  --track-label-caps: 0.1em;

  --external-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 5h5v2H4v5h5V9h2v5H2V5z'/%3E%3Cpath d='M9 2h5v5h-2V5.41L8.71 8.71 7.29 7.29 10.59 4H9V2z'/%3E%3C/svg%3E");
  --menu-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 3h12v2H2zM2 7h12v2H2zM2 11h12v2H2z'/%3E%3C/svg%3E");
  --close-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.41 3 13 11.59 11.59 13 3 4.41z'/%3E%3Cpath d='M11.59 3 13 4.41 4.41 13 3 11.59z'/%3E%3C/svg%3E");

  --page-width: 1120px;
  --gutter: 24px;
  --border: 1.4px solid var(--black);
  --pixel: 8px;

  /* Hard offset shadows: buttons throw blue, every other raised surface throws black. */
  --shadow-button: 4px 4px 0 var(--shadow-blue);
  --shadow-button-hover: 1px 1px 0 var(--shadow-blue);
  --shadow-button-active: 0 0 0 var(--shadow-blue);
  --shadow-raised: 6px 6px 0 var(--black);
}

/* ------------------------------------------------------------------ base -- */

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

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--white);
  color: var(--black);
  font-family: var(--sans);
  font-size: var(--text-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Short pages: push the footer to the bottom of the viewport. */
main {
  flex: 1 0 auto;
}

h1,
h2,
h3,
h4 {
  margin: 0;
}

p {
  margin: 0;
}

a {
  color: var(--blue-200);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
}

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

:focus-visible {
  outline: 3px solid var(--blue-100);
  outline-offset: 3px;
}

img,
svg {
  display: block;
}

code,
pre {
  font-family: var(--mono);
  font-stretch: 87.5%;
  font-size-adjust: var(--x-height-body);
}

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

/* ----------------------------------------------------------- text styles -- */

/*
  Four roles, each a single ladder. Every piece of type on the site is one of them, and each style
  owns the whole bundle — family, size, weight, width, tracking, case, leading — because those
  properties only ever move together. Components below set layout and colour; none set a size.

    heading   xl lg md sm xs   every headline and subheading, in Martian Mono
    body      lg · sm          prose, in Inter; the middle step is the page default
    label     · caps           mono small type doing a job: UI, metadata, micro-headings
    code                       monospaced type that is literally code

  Heading condenses as it grows: xl and lg run at 75% width, the rest at 87.5%.

  Each block lists the components that *are* that style, so a new component means adding a
  selector here rather than inventing a size at the use site. New markup can use the .t-* class
  directly instead.
*/

.t-heading-xl,
.t-heading-lg,
.t-heading-md,
.t-heading-sm,
.t-heading-xs,
h1,
.apps-header h1,
.section-title,
.post-header h1,
.post-item-title,
.post-body h2,
.path h3,
.post-body h3,
.use h2,
.step h3,
.piece h3,
.app-title,
.post-body h4 {
  font-family: var(--mono);
  font-weight: 600;
  font-stretch: 87.5%;
  line-height: 1.2;
  letter-spacing: var(--track-heading);
}

.t-heading-xl,
h1 {
  font-size: var(--text-heading-xl);
  font-stretch: 75%;
  line-height: 1.02;
  letter-spacing: var(--track-heading-xl);
}

.t-heading-lg,
.apps-header h1,
.section-title,
.post-header h1 {
  font-size: var(--text-heading-lg);
  font-stretch: 75%;
  line-height: 1.1;
}

.t-heading-md,
.post-item-title,
.post-body h2 {
  font-size: var(--text-heading-md);
}

.t-heading-sm,
.path h3,
.post-body h3 {
  font-size: var(--text-heading-sm);
}

.t-heading-xs,
.use h2,
.step h3,
.piece h3,
.app-title,
.post-body h4 {
  font-size: var(--text-heading-xs);
}

/* body's middle step is set on <body> and inherited; these are the two off-default sizes. */
.t-body-lg,
.lede,
.statement-text {
  font-size: var(--text-body-lg);
  line-height: 1.5;
}

.t-body-sm,
.use p,
.step p,
.piece p,
.path-body,
.post-item-summary,
.app-desc,
.path-points,
.footer-license,
.footer-column ul,
.post-source,
.catalog-source,
.post-body table {
  font-size: var(--text-body-sm);
  line-height: 1.5;
}

.t-label,
.t-label-caps,
.button,
.text-link,
.search input,
.hero-note,
.path-cost,
.post-byline,
.post-footer,
.empty-state,
.eyebrow,
.nav-links,
.chip,
.post-date,
.result-count,
.footer-column h2,
.footer-base-inner,
.code-label,
.post-body thead th,
.path-number,
.step-number {
  font-family: var(--mono);
  font-size: var(--text-label);
  font-weight: 600;
  font-stretch: 87.5%;
  line-height: 1.5;
  letter-spacing: 0;
  text-transform: none;
}

/* caps is the same style shouting: the site's micro-headings and metadata chips. */
.t-label-caps,
.eyebrow,
.nav-links,
.chip,
.post-date,
.result-count,
.footer-column h2,
.footer-base-inner,
.code-label,
.post-body thead th,
.path-number,
.step-number {
  letter-spacing: var(--track-label-caps);
  text-transform: uppercase;
}

.t-code,
.step-command,
.own-app-code pre,
.post-body pre > code {
  font-family: var(--mono);
  font-size: var(--text-code);
  font-stretch: 87.5%;
  font-size-adjust: var(--x-height-body);
  line-height: 1.7;
  font-variant-ligatures: none;
  tab-size: 2;
}

/* ---------------------------------------------------------------- lockup -- */

/* The brand lockup: the icon, plus the wordmark set in type rather than shipped as artwork, so the
   nav can animate the icon on its own. Size the whole thing by setting --lockup-height. The
   wordmark's size, weight and tracking are tuned to sit on assets/'s single-line lockup — change
   one and re-check it against that file. */
.lockup {
  --lockup-height: 30px;
  display: flex;
  align-items: center;
  color: var(--black);
  text-decoration: none;
}

/* Sized off the icon's own 26x18, and spaced with a margin rather than a flex gap so the nav can
   collapse the icon away to nothing. */
.lockup-mark {
  height: var(--lockup-height);
  width: calc(var(--lockup-height) * 26 / 18);
  margin-right: calc(var(--lockup-height) * 0.19);
  image-rendering: pixelated;
}

/* 0.73 puts the cap height exactly on the brand lockup's. The width won't match it — the brand
   wordmark is drawn in a face narrower than Martian Mono goes — so match the optical size and let
   the lockup run a little longer. */
.lockup-wordmark {
  font-family: var(--mono);
  font-size: calc(var(--lockup-height) * 0.73);
  font-weight: 500;
  font-stretch: 75%;
  letter-spacing: -0.02em;
  /* Monospace gives the space the same advance as a letter, which reads as a gap in a three-word
     lockup. Pulled back to the word gaps the brand lockup draws. */
  word-spacing: -0.2em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

/* --------------------------------------------------------------- chrome  -- */

.nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--white);
  border-bottom: var(--border);
}

.nav-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 12px var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}

/* Two bottles on screen is one too many: while the hero's own bottle is in view the nav's is
   collapsed away, and it pops up into the header once the hero scrolls under it. */
.nav-logo .lockup-mark {
  transform-origin: 50% 100%;
  transition:
    width 0.2s ease,
    margin-right 0.2s ease,
    opacity 0.14s ease,
    transform 0.3s cubic-bezier(0.2, 1.75, 0.5, 1);
}

.hero-bottle-visible .nav-logo .lockup-mark {
  width: 0;
  margin-right: 0;
  opacity: 0;
  transform: scale(0.2) translateY(16px);
}

.nav-links {
  display: flex;
  gap: 22px;
}

/* Symmetric top edge: the hover underline would otherwise push the text off centre. */
.nav-links a {
  color: var(--black);
  text-decoration: none;
  white-space: nowrap;
  padding: 2px 0;
  border-top: 2px solid transparent;
  border-bottom: 2px solid transparent;
}

.nav-links a:hover {
  border-bottom-color: var(--blue-100);
}

/* Leaves the site — mark it, in the wordmark's own pixel idiom. */
.nav-links a[target="_blank"]::after,
.button[target="_blank"]::after {
  content: "";
  display: inline-block;
  width: 0.95em;
  height: 0.95em;
  margin-left: 0.42em;
  vertical-align: baseline;
  background: currentColor;
  -webkit-mask: var(--external-glyph) no-repeat center / contain;
  mask: var(--external-glyph) no-repeat center / contain;
}

/* Narrow-layout menu button. Hidden until the media query below turns the links into a panel. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  color: var(--black);
  background: var(--paper);
  border: var(--border);
  box-shadow: var(--shadow-button);
  cursor: pointer;
  transition: transform 0.08s steps(2), box-shadow 0.08s steps(2);
}

.nav-toggle:active {
  transform: translate(4px, 4px);
  box-shadow: var(--shadow-button-active);
}

.nav-toggle::before {
  content: "";
  width: 20px;
  height: 20px;
  background: currentColor;
  -webkit-mask: var(--menu-glyph) no-repeat center / contain;
  mask: var(--menu-glyph) no-repeat center / contain;
}

.nav-open .nav-toggle::before {
  -webkit-mask-image: var(--close-glyph);
  mask-image: var(--close-glyph);
}

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

.button {
  display: inline-block;
  text-decoration: none;
  color: var(--black);
  background: var(--paper);
  border: var(--border);
  padding: 11px 18px;
  box-shadow: var(--shadow-button);
  transition: transform 0.08s steps(2), box-shadow 0.08s steps(2);
}

.button:hover {
  color: var(--black);
  transform: translate(2px, 2px);
  box-shadow: var(--shadow-button-hover);
}

.button:active {
  transform: translate(4px, 4px);
  box-shadow: var(--shadow-button-active);
}

.button-primary {
  background: var(--blue-200);
  color: var(--white);
}

.button-primary:hover {
  color: var(--white);
  background: var(--blue-100);
}

.text-link {
  background: none;
  border: 0;
  padding: 0;
  color: var(--blue-200);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ------------------------------------------------------------------ sky  -- */

/* Graph-paper grid. Lives on the band, under its content — anything inside that should sit on
   top needs its own stacking position (see .hero-inner, .paths-inner). */
.grid-surface {
  position: relative;
}

.grid-surface::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(12, 11, 11, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(12, 11, 11, 0.045) 1px, transparent 1px);
  background-size: 16px 16px;
  pointer-events: none;
}

/* No bottom border: the .horizon dither is the edge. */
.hero {
  overflow: hidden;
  background: linear-gradient(180deg, var(--sky-pale) 0%, var(--sky) 100%);
}

.hero-inner {
  position: relative;
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 88px var(--gutter) 116px;
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: 48px;
  align-items: center;
}

.hero-short .hero-inner {
  padding: 72px var(--gutter) 104px;
  grid-template-columns: minmax(0, 1fr);
}

.eyebrow {
  color: var(--blue-200);
  margin-bottom: 18px;
}

h1 {
  margin-bottom: 22px;
}

.lede {
  max-width: 33em;
}

.hero-actions {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 32px;
}

.hero-note {
  color: var(--ink-soft);
  margin-top: 26px;
  max-width: 34em;
}

.hero-art {
  display: flex;
  justify-content: center;
}

.bottle {
  width: min(100%, 340px);
  height: auto;
  image-rendering: pixelated;
  animation: bob 6s ease-in-out infinite;
  filter: drop-shadow(10px 12px 0 rgba(12, 11, 11, 0.12));
}

@keyframes bob {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-10px) rotate(-1.2deg); }
}

/* clouds */

.sky-clouds {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}

.cloud {
  position: absolute;
  will-change: transform;
}

.cloud-shape {
  width: 100%;
  height: auto;
}

.cloud-1 { top: 12%; width: 150px; animation: drift 68s linear infinite; }
.cloud-2 { top: 34%; width: 96px; animation: drift 96s linear infinite -22s; }
.cloud-3 { top: 62%; width: 210px; animation: drift 124s linear infinite -60s; }
.cloud-4 { top: 6%; width: 64px; animation: drift 84s linear infinite -45s; }

@keyframes drift {
  from { transform: translateX(-260px); }
  to { transform: translateX(calc(100vw + 260px)); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .cloud { animation: none; }
  .cloud-1 { left: 6%; }
  .cloud-2 { left: 62%; }
  .cloud-3 { left: 28%; }
  .cloud-4 { left: 84%; }
  .bottle { animation: none; }
  .button { transition: none; }
  .nav-logo .lockup-mark { transition: none; }
}

/* Dithered pixel transition between colour bands: three 8px rows stepping from
   25% to 75% coverage, the way an 8-bit sky fades into the ground. */

.horizon {
  --dither: var(--white);
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 24px;
  background:
    linear-gradient(90deg, var(--dither) 0 8px, transparent 8px 32px) 0 0 / 32px 8px repeat-x,
    linear-gradient(90deg, var(--dither) 0 8px, transparent 8px 16px) 0 8px / 16px 8px repeat-x,
    linear-gradient(90deg, transparent 0 8px, var(--dither) 8px 32px) 0 16px / 32px 8px repeat-x;
}

/* --------------------------------------------------------------- sections -- */

.section {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 84px var(--gutter);
}

.section-title {
  margin-bottom: 16px;
}

.section-lede {
  max-width: 46em;
  color: var(--ink-soft);
}

.section-lede.narrow {
  max-width: 34em;
}

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  margin-bottom: 40px;
  flex-wrap: wrap;
}

/* statement */

.statement {
  padding-top: 76px;
  padding-bottom: 40px;
}

.statement-text {
  text-wrap: pretty;
}

.statement-text + .statement-text {
  margin-top: 1em;
}

.statement-follow {
  margin-top: 26px;
  max-width: 34em;
  color: var(--ink-soft);
}

/* uses */

.uses {
  padding-top: 40px;
}

.use-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 2px;
  background: var(--black);
  border: var(--border);
}

.use {
  background: var(--paper);
  padding: 26px 24px 30px;
}

.use h2 {
  margin-bottom: 10px;
}

.use p {
  color: var(--ink-soft);
}

/* catalog */

.app-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 14px;
}

.app-card {
  display: block;
  background: var(--paper);
  border: var(--border);
  padding: 14px;
  text-decoration: none;
  color: var(--black);
  transition: transform 0.08s steps(2), box-shadow 0.08s steps(2);
}

.app-card[hidden] {
  display: none;
}

.app-card:hover {
  color: var(--black);
  transform: translate(-2px, -2px);
  box-shadow: var(--shadow-raised);
}

.app-title {
  display: block;
  margin-bottom: 4px;
}

.app-desc {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: var(--ink-soft);
}

/* paths */

.paths {
  border-top: var(--border);
  border-bottom: var(--border);
  max-width: none;
  overflow: hidden;
  background: var(--paper);
}

.paths-inner {
  position: relative;
  max-width: var(--page-width);
  margin: 0 auto;
}

.path-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 22px;
  margin-top: 42px;
}

.path {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: var(--border);
  box-shadow: var(--shadow-raised);
  padding: 26px 24px 28px;
}

.path-managed {
  background: var(--sky-pale);
}

.path-number {
  color: var(--blue-200);
  margin-bottom: 12px;
}

.path h3 {
  margin-bottom: 22px;
}

.path-body {
  color: var(--ink-soft);
}

.path-points {
  list-style: none;
  padding: 0;
  margin: 18px 0 0;
}

.path-points li {
  position: relative;
  padding-left: 20px;
  margin-bottom: 9px;
  line-height: 1.45;
}

.path-points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 8px;
  height: 8px;
  background: var(--blue-100);
}

.path-cost {
  margin: 20px 0 22px;
  padding-top: 16px;
  border-top: 1.4px dotted rgba(12, 11, 11, 0.25);
}

.path .button {
  margin-top: auto;
  align-self: flex-start;
}

/* bring your own app */

.own-app-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: 56px;
  align-items: start;
}

.step-list {
  margin-top: 36px;
  display: grid;
  gap: 24px;
}

/* Baseline, not start: the number is smaller than the heading it labels, so aligning the boxes
   leaves it visibly high. */
.step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
}

.step-number {
  color: var(--blue-200);
}

.step h3 {
  margin-bottom: 5px;
}

.step p {
  color: var(--ink-soft);
}

.step-command {
  display: block;
  margin-top: 10px;
  padding: 8px 12px;
  background: var(--black);
  color: var(--white);
  overflow-x: auto;
  white-space: nowrap;
}

.step-command::before {
  content: "$ ";
  color: var(--grass);
}

/* how it works */

.piece-list {
  margin-top: 34px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
  gap: 26px 32px;
}

.piece h3 {
  margin-bottom: 5px;
}

.piece p {
  color: var(--ink-soft);
  max-width: 40em;
}

.own-app-code {
  border: var(--border);
  background: var(--black);
  box-shadow: var(--shadow-raised);
}

.code-label {
  color: var(--black);
  background: var(--sky);
  padding: 8px 14px;
  border-bottom: var(--border);
}

.own-app-code pre {
  margin: 0;
  padding: 20px 18px;
  overflow-x: auto;
  color: var(--white);
}

.own-app-code .text-link {
  display: block;
  padding: 14px 18px;
  border-top: 1.4px solid rgba(243, 243, 243, 0.25);
  color: var(--sky);
}

/* project */

.project-card {
  border: var(--border);
  background: var(--paper);
  padding: 40px 36px;
  box-shadow: var(--shadow-raised);
}

.project-card p {
  max-width: 46em;
  color: var(--ink-soft);
}

.project-actions {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 28px;
}

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

.footer {
  position: relative;
  flex-shrink: 0;
  background: var(--grass);
  margin-top: 20px;
}

/* The sky dither, mirrored: white settling down into the grass. */
.footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 24px;
  background:
    linear-gradient(90deg, transparent 0 8px, var(--white) 8px 32px) 0 0 / 32px 8px repeat-x,
    linear-gradient(90deg, var(--white) 0 8px, transparent 8px 16px) 0 8px / 16px 8px repeat-x,
    linear-gradient(90deg, var(--white) 0 8px, transparent 8px 32px) 0 16px / 32px 8px repeat-x;
}

.footer-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 56px var(--gutter) 64px;
  display: grid;
  grid-template-columns: minmax(0, 1.8fr) repeat(3, minmax(0, 1fr));
  gap: 40px;
}

.footer-lockup {
  --lockup-height: 30px;
  margin-bottom: 18px;
}

.footer-license {
  max-width: 30em;
  color: var(--ink-soft);
}

.footer-column h2 {
  margin-bottom: 14px;
}

.footer-column ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
}

.footer-column a {
  color: var(--black);
  text-decoration-color: rgba(12, 11, 11, 0.35);
}

.footer-column a:hover {
  text-decoration-color: var(--black);
}

.footer-base {
  border-top: var(--border);
}

.footer-base-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 16px var(--gutter);
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

/* ----------------------------------------------------------- apps page  -- */

.apps-header {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, var(--sky-pale) 0%, var(--sky) 100%);
}

.apps-header-inner {
  position: relative;
  max-width: var(--page-width);
  margin: 0 auto;
  padding: 62px var(--gutter) 82px;
}

.apps-body {
  padding-top: 46px;
}

.filters {
  display: grid;
  gap: 18px;
  margin-bottom: 22px;
}

/* Exception to the control style, and a platform constraint rather than a design choice: iOS
   Safari zooms the viewport on focus if an input is under 16px, and stays zoomed. */
.search input {
  font-size: 1rem;
  width: 100%;
  max-width: 420px;
  padding: 12px 14px;
  border: var(--border);
  background: var(--paper);
  color: var(--black);
}

.search input::placeholder {
  color: rgba(12, 11, 11, 0.45);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip {
  background: var(--paper);
  border: var(--border);
  padding: 7px 11px;
  cursor: pointer;
  color: var(--black);
}

.chip span {
  color: var(--ink-soft);
  margin-left: 4px;
}

.chip:hover {
  background: var(--sky);
}

.chip.is-active {
  background: var(--black);
  color: var(--white);
}

.chip.is-active span {
  color: var(--sky);
}

.result-count {
  color: var(--ink-soft);
  margin-bottom: 18px;
}

.app-grid-full {
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.empty-state {
  padding: 40px 0;
}

.catalog-source {
  margin-top: 34px;
  color: var(--ink-soft);
}

/* ----------------------------------------------------------- blog  -- */

/* Baseline, not centre: the feed link is much smaller than the title it sits beside. */
.blog-title-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px 24px;
  flex-wrap: wrap;
}

.post-list-section {
  padding-top: 46px;
}

/* Left edge lines up with the header above; the cap is so a summary line stays readable. */
.post-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 800px;
  border-top: var(--border);
}

.post-item {
  border-bottom: var(--border);
}

.post-item-link {
  display: block;
  padding: 26px 4px 28px;
  text-decoration: none;
  color: var(--black);
}

.post-item-link:hover {
  background: var(--sky-pale);
}

.post-date {
  display: block;
  color: var(--ink-soft);
}

.post-item-title {
  margin: 8px 0 8px;
}

.post-item-link:hover .post-item-title {
  color: var(--blue-200);
}

.post-item-summary {
  color: var(--ink-soft);
  max-width: 46em;
}

/* post page */

.post {
  max-width: 1000px;
  margin: 0 auto;
  padding: 40px var(--gutter) 84px;
}

.post-header {
  padding-bottom: 22px;
  margin-bottom: 34px;
  border-bottom: var(--border);
}

.post-header .eyebrow a {
  color: inherit;
  text-decoration: none;
}

.post-header .eyebrow a:hover {
  color: var(--blue-200);
}

.post-header h1 {
  margin: 0 0 14px;
  text-wrap: balance;
}

.post-byline {
  color: var(--ink-soft);
}

/* The post body is markdown-it's output, so it's plain tags with no classes to hang styles on.
   These rules deliberately track the manual's .docs-article ones in docs_site/assets/manual.css —
   the two stylesheets are separate because the book's is mdBook's theme, not this site's. */

.post-body h2,
.post-body h3,
.post-body h4 {
  scroll-margin-top: 80px;
}

.post-body h2 {
  margin: 2.2em 0 0.7em;
}

.post-body h3 {
  margin: 1.9em 0 0.6em;
}

.post-body h4 {
  margin: 1.7em 0 0.5em;
}

/* markdown-it's anchors plugin wraps each heading's permalink in this. */
.post-body .header-anchor {
  color: var(--blue-100);
  text-decoration: none;
  opacity: 0;
  margin-left: 0.35em;
}

.post-body :is(h2, h3):hover .header-anchor,
.post-body .header-anchor:focus-visible {
  opacity: 1;
}

.post-body p,
.post-body ul,
.post-body ol,
.post-body blockquote,
.post-body pre,
.post-body table {
  margin: 0 0 1.15em;
}

.post-body li {
  margin-bottom: 0.4em;
}

.post-body li > ul,
.post-body li > ol {
  margin: 0.4em 0;
}

.post-body hr {
  border: 0;
  border-top: 1.4px solid rgba(12, 11, 11, 0.15);
  margin: 2.5em 0;
}

.post-body img {
  max-width: 100%;
  height: auto;
  border: var(--border);
}

.post-body blockquote {
  padding: 14px 20px;
  background: #eaf6ff;
  border-left: 6px solid var(--blue-100);
}

.post-body blockquote > :last-child {
  margin-bottom: 0;
}

.post-body table {
  border-collapse: collapse;
  border: var(--border);
  display: block;
  overflow-x: auto;
}

.post-body th,
.post-body td {
  border: 1px solid rgba(12, 11, 11, 0.25);
  padding: 9px 12px;
  text-align: left;
  vertical-align: top;
}

.post-body thead th {
  background: var(--sky);
  border-bottom: var(--border);
}

/* Inline code sizes in em because it has to track whatever it sits inside — a table cell or a
   heading as readily as a paragraph. 1em is exact rather than a fudge: font-size-adjust above
   already scales the mono face to Inter's x-height, so the two match optically at equal size. */
.post-body :not(pre) > code {
  background: #e8f3ff;
  color: var(--blue-200);
  font-size: 1em;
  padding: 1px 4px;
  overflow-wrap: anywhere;
}

.post-body pre {
  background: var(--black);
  border: var(--border);
  box-shadow: var(--shadow-raised);
  padding: 0;
  overflow: hidden;
}

.post-body pre > code {
  display: block;
  padding: 18px 20px;
  overflow-x: auto;
  background: transparent;
  color: #e6e6e6;
}

.post-footer {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 56px;
  padding-top: 22px;
  border-top: var(--border);
}

.post-nav {
  max-width: 20em;
}

.post-nav-older {
  margin-left: auto;
  text-align: right;
}

.post-source {
  margin-top: 26px;
  color: var(--ink-soft);
}

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

/* Below this the six links no longer share a line with the lockup, so they fold into a panel
   behind the menu button. nav.js opens it and matches this width. */
@media (max-width: 820px) {
  .js .nav-inner {
    flex-wrap: nowrap;
  }

  .js .nav-toggle {
    display: flex;
  }

  .js .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    padding: 14px var(--gutter) 20px;
    background: var(--white);
    border-bottom: var(--border);
  }

  .js .nav-open .nav-links {
    display: flex;
  }

  .js .nav-links a {
    padding: 8px 0;
  }
}

@media (max-width: 900px) {
  .hero-inner,
  .own-app-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .hero-inner {
    padding-top: 60px;
    padding-bottom: 96px;
  }

  .hero-art {
    justify-content: flex-start;
  }

  .bottle {
    width: 180px;
  }
}

/* The lockup needs ~305px; below this the four-column footer can't give the brand column that. */
@media (max-width: 1024px) {
  .footer-inner {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer-brand {
    grid-column: 1 / -1;
  }
}

@media (max-width: 560px) {
  .section {
    padding: 56px var(--gutter);
  }

  .nav-logo {
    --lockup-height: 24px;
  }

  .footer-lockup {
    --lockup-height: 28px;
  }

  .project-card {
    padding: 28px 22px;
  }

  .post-footer {
    display: grid;
  }

  .post-nav-older {
    margin-left: 0;
    text-align: left;
  }

  .footer-inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
  }

  .footer-lockup {
    --lockup-height: 26px;
  }

  /* On a phone these are a long scroll past a link that already says the same thing. */
  .catalog-preview .app-grid,
  .own-app-code {
    display: none;
  }

  /* The head's bottom margin was spacing for the grid above. */
  .catalog-preview .section-head {
    margin-bottom: 0;
  }

  .narrow-hide {
    display: none;
  }
}
