/* Hallmark · shape: Index-First · genre: modern-minimal · theme: Manifest (custom, first built for this index) · job: try · draw: none, the theme is named for the manifest sheet a dispatch desk files
 * hero: headline, one line, search and its actions on the sheet, the count strip and the replacements ledger directly below · nav: wordmark left, three links right, stacks under 40rem · footer: a mast, the wordmark over the links, attribution under the rule · imagery: each project's own mark from the selfh.st icon pack hotlinked at the row, the GitHub owner avatar behind it, a letter tile where a project has neither · signature: every listing names the hosted product it replaces, keyed with the orange rule
 * axes: light/grotesk-condensed/two-signal · posture: restrained · contrast: pass · fold: pass · v2.0.0 */
/* Hallmark · pre-emit critique: P4 H4 E4 S5 R5 V4 · weakest: V, a catalogue earns its distinction in the row; the icon column and the two signals carry it */
/* Hallmark · dismissed 58 · the home page is an index; its sections are the listings themselves, parted by bar rules, and it is read by scanning */

:root {
  color-scheme: light;
  --space-3xs: 2px; --space-2xs: 4px; --space-xs: 8px; --space-sm: 12px; --space-md: 16px; --space-lg: 24px; --space-xl: 40px; --space-2xl: 64px;
  --text-xs: 0.75rem; --text-sm: 0.8125rem; --text-md: 0.9375rem; --text-lg: 1.0625rem; --text-xl: 1.375rem; --text-2xl: 1.75rem;
  --text-display: clamp(2rem, 1.375rem + 2.75vw, 2.75rem); --text-section: 1.5rem; --text-title: 1.25rem; --text-lead: 1.125rem;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1); --ease-in: cubic-bezier(0.7, 0, 0.84, 0); --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-micro: 120ms; --dur-short: 220ms; --dur-long: 420ms;
  --color-error: oklch(52% 0.19 27); --color-success: oklch(50% 0.12 150);
  --container: 76rem; --gutter: 24px; --gap-columns: 24px; --section-gap-s: 56px; --section-gap: 72px;
  --z-base: 1; --z-raised: 10; --z-dropdown: 100; --z-sticky: 200; --z-sticky-nav: 300; --z-modal: 400; --z-toast: 500; --z-tooltip: 600;
  --color-paper: oklch(92.5% 0.016 152); --color-paper-2: oklch(90% 0.018 152); --color-paper-3: oklch(86.5% 0.02 152);
  --color-sheet: oklch(98.7% 0.004 130); --color-band: oklch(96.4% 0.013 150); --color-hover: oklch(93% 0.03 152);
  --color-ink: oklch(19% 0.024 162); --color-ink-2: oklch(30% 0.02 162); --color-muted: oklch(36% 0.018 160);
  --color-rule: oklch(84.5% 0.014 150); --color-rule-2: oklch(19% 0.024 162);
  --color-accent: oklch(50% 0.155 155); --color-accent-deep: oklch(43% 0.14 155); --color-accent-ink: oklch(98.7% 0.004 130);
  --color-accent-2: oklch(54% 0.175 38); --color-accent-2-deep: oklch(45% 0.155 38);
  --color-focus: oklch(45% 0.15 155);
  --font-display: "Barlow Condensed", "Arial Narrow", ui-sans-serif, sans-serif;
  --font-body: "Barlow", ui-sans-serif, system-ui, sans-serif;
  --font-outlier: "DM Mono", ui-monospace, SFMono-Regular, monospace;
  --radius-card: 6px; --radius-pill: 999px; --radius-icon: 8px; --rule-hair: 1px; --rule-bar: 3px; --shadow-card: none;
  --tracking-display: -0.005em; --display-weight: 600; --icon-size: 40px;
}

*, *::before, *::after { box-sizing: border-box; }
html, body { overflow-x: clip; }
html { scrollbar-color: var(--color-muted) var(--color-paper); scroll-padding-top: var(--space-2xl); }
body { margin: 0; background: var(--color-paper); color: var(--color-ink-2); font-family: var(--font-body); font-size: var(--text-md); line-height: 1.5; -webkit-font-smoothing: antialiased; caret-color: var(--color-accent); }
::selection { background: color-mix(in oklab, var(--color-accent) 22%, transparent); color: var(--color-ink); }
:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
a { color: var(--color-ink); text-decoration: underline; text-decoration-color: var(--color-accent); text-decoration-thickness: 1px; text-underline-offset: 0.15em; transition: text-decoration-thickness var(--dur-micro) var(--ease-out); }
@media (hover: hover) { a:hover { text-decoration-thickness: 2px; } }
h1, h2, h3 { margin: 0; font-family: var(--font-display); font-weight: var(--display-weight); font-synthesis: none; letter-spacing: var(--tracking-display); line-height: 1.08; overflow-wrap: break-word; min-width: 0; text-wrap: balance; color: var(--color-ink); }
h1 { max-width: 24ch; font-size: var(--text-display); }
h2 { max-width: 34ch; font-size: var(--text-section); text-transform: uppercase; line-height: 1.15; letter-spacing: 0.01em; }
h3 { max-width: 44ch; font-size: var(--text-title); line-height: 1.2; }
.lead { font-size: var(--text-lead); line-height: 1.45; max-width: 54ch; color: var(--color-ink-2); }
p { margin: 0; text-wrap: pretty; }
img, video { max-width: 100%; height: auto; display: block; }
[hidden] { display: none !important; }
button, input, select, textarea { font: inherit; }
.num { font-family: var(--font-outlier); font-variant-numeric: tabular-nums; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@media (max-width: 60rem) { :root { --gutter: 20px; --section-gap-s: 48px; --section-gap: 64px; } }
@media (max-width: 40rem) { :root { --gutter: 16px; --section-gap-s: 40px; --section-gap: 56px; --gap-columns: 16px; --icon-size: 36px; } }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }

/* The sheet: the manifest page laid on the dispatch stock. */
.sheet { width: 100%; max-width: var(--container); min-height: 100dvh; margin-inline: auto; padding-inline: var(--gutter); background: var(--color-sheet); border-top: var(--rule-bar) solid var(--color-ink); overflow-wrap: break-word; }

/* Chrome */
.site-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-xs) var(--space-lg); padding-block: var(--space-sm); border-bottom: var(--rule-hair) solid var(--color-rule); }
.wordmark { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-xl); letter-spacing: var(--tracking-display); color: var(--color-ink); text-decoration: none; display: inline-flex; align-items: center; min-height: 44px; }
.site-nav { display: flex; flex-wrap: wrap; gap: var(--space-2xs) var(--space-md); }
.site-nav a { font-weight: 500; font-size: var(--text-sm); color: var(--color-ink-2); white-space: nowrap; display: inline-flex; align-items: center; min-height: 44px; padding-inline: var(--space-2xs); }

/* Hero */
.hero { padding-block: var(--space-2xl) 0; }
.hero .lead { margin-top: var(--space-md); }
.search { margin-top: var(--space-xl); max-width: 44rem; }
.search label { display: block; font-weight: 500; font-size: var(--text-sm); color: var(--color-ink-2); margin-bottom: var(--space-xs); }
.search input { width: 100%; height: 52px; padding-inline: var(--space-md); border: var(--rule-hair) solid var(--color-muted); border-radius: var(--radius-card); background: var(--color-sheet); color: var(--color-ink); font-size: var(--text-md); }
.search input::placeholder { color: var(--color-muted); }
.search input:disabled { color: var(--color-muted); border-color: var(--color-rule); }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); margin-top: var(--space-lg); }
.btn { display: inline-flex; align-items: center; min-height: 48px; padding-inline: var(--space-lg); border: var(--rule-hair) solid var(--color-ink); border-radius: var(--radius-card); font-family: var(--font-display); font-weight: 500; font-size: var(--text-lg); color: var(--color-ink); background: transparent; text-decoration: none; white-space: nowrap; }
.btn--primary { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-ink); }
@media (hover: hover) { .btn--primary:hover { background: var(--color-accent-deep); border-color: var(--color-accent-deep); } .btn:hover { text-decoration: none; } }
a:active { text-decoration-thickness: 2px; }
.btn:active { background: var(--color-paper-3); }
.btn--primary:active { background: var(--color-accent-deep); border-color: var(--color-accent-deep); }
#results { margin-top: var(--space-lg); }
#results:empty { margin-top: 0; }
.results-note { font-size: var(--text-sm); color: var(--color-muted); margin-bottom: var(--space-sm); }

/* The count strip: the manifest header figures, set as large as the heads. */
.count-strip { list-style: none; margin: var(--space-xl) 0 0; padding: var(--space-md) 0; display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-xl); border-top: var(--rule-bar) solid var(--color-ink); border-bottom: var(--rule-hair) solid var(--color-rule); }
.count-num { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-2xl); line-height: 1.05; color: var(--color-ink); font-variant-numeric: tabular-nums; }
.count-label { margin-top: var(--space-3xs); font-size: var(--text-sm); color: var(--color-muted); }

/* Sections */
.section { margin-top: var(--section-gap-s); }
.section--wide { margin-top: var(--section-gap); }
/* A list that directly answers a page head sits closer than a new section. */
.page-head + .section { margin-top: var(--space-xl); }
.section-head { margin-bottom: var(--space-md); }
.section-head--long h2 { font-size: var(--text-xl); max-width: 60ch; }
.section-head p { margin-top: var(--space-xs); max-width: 65ch; color: var(--color-ink-2); }
.page-head { padding-top: var(--space-xl); }
.page-head h1 { max-width: 26ch; }
.page-head .lead { margin-top: var(--space-sm); }
.page-head--alt { border-top: var(--rule-bar) solid var(--color-accent-2); margin-top: var(--space-md); padding-top: var(--space-lg); }
.page-head--app { display: flex; gap: var(--space-md); align-items: flex-start; }
.page-head--app .icon { margin-top: var(--space-2xs); }
.page-head-main { min-width: 0; }
.crumb { font-size: var(--text-sm); font-weight: 500; color: var(--color-muted); margin-bottom: var(--space-xs); }
.crumb a { color: var(--color-ink-2); display: inline-flex; align-items: center; min-height: 44px; padding-inline: var(--space-2xs); }
.count-line { margin-top: var(--space-xs); font-size: var(--text-sm); color: var(--color-muted); }

/* Entries: one manifest line each. Icon, the filing, then its figures. */
.entries { list-style: none; margin: 0; padding: 0; border-top: var(--rule-bar) solid var(--color-rule-2); }
.entry { display: grid; grid-template-columns: var(--icon-size) minmax(0, 1fr) auto; grid-template-areas: "icon main figs"; gap: var(--space-2xs) var(--space-md); padding-block: var(--space-sm); padding-inline: var(--space-xs); border-bottom: var(--rule-hair) solid var(--color-rule); transition: background-color var(--dur-micro) var(--ease-out); }
.entries .entry:nth-child(even) { background: var(--color-band); }
/* Hover must outrank the band rule above (same specificity, later in the
   file) and read in a still image: a deeper tint, the green keyline at the
   row's left edge, and the name taking the signal. */
@media (hover: hover) {
  .entries .entry:hover { background: var(--color-hover); box-shadow: inset var(--rule-bar) 0 0 var(--color-accent); }
  .entries .entry:hover .entry-name a { color: var(--color-accent-deep); text-decoration-color: var(--color-accent-deep); }
}
.icon { grid-area: icon; position: relative; width: var(--icon-size); height: var(--icon-size); border-radius: var(--radius-icon); background: var(--color-paper-2); overflow: hidden; }
/* The letter stays hidden while an image is in play, so first paint is the
   plain tile, never a placeholder glyph that swaps out. It is revealed only
   when there is no image (icon--noimg) or the image failed (icon--fallback,
   set by the img's onerror). */
.icon-letter { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-xl); color: var(--color-ink-2); opacity: 0; }
.icon--noimg .icon-letter, .icon--fallback .icon-letter { opacity: 1; }
.icon-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: var(--color-paper-2); }
.icon--lg { width: 56px; height: 56px; border-radius: 10px; }
.icon--lg .icon-letter { font-size: var(--text-2xl); }
.entry-main { grid-area: main; min-width: 0; }
.entry-name a { display: inline-flex; align-items: center; min-height: 44px; margin-block: calc(-1 * var(--space-xs)); padding-block: var(--space-xs); }
.entry-desc { margin-top: var(--space-3xs); max-width: 72ch; }
.entry-replaces { margin-top: var(--space-xs); padding-left: var(--space-sm); border-left: var(--rule-bar) solid var(--color-accent-2); font-size: var(--text-sm); line-height: 1.5; }
.entry-replaces .replaces-label { font-weight: 600; color: var(--color-ink); }
.entry-replaces a { text-decoration-color: var(--color-accent-2); }
/* Figures ride in two lines, not a four-line stack: stars and the status
   chip on the first, date and release on the second. The row's height is
   set by the filing, and the column is capped so the rail stays close. */
.entry-figs { grid-area: figs; min-width: 0; max-width: 14rem; }
.fig-top { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; column-gap: var(--space-xs); row-gap: var(--space-2xs); }
.fig-sub { display: flex; flex-wrap: wrap; justify-content: flex-end; column-gap: var(--space-sm); row-gap: var(--space-3xs); margin-top: var(--space-2xs); }
.fig-line { font-family: var(--font-outlier); font-size: var(--text-md); color: var(--color-ink); font-variant-numeric: tabular-nums; line-height: 1.3; white-space: nowrap; }
.fig-unit { font-size: var(--text-xs); color: var(--color-muted); }
.fig-note { font-family: var(--font-body); font-size: var(--text-sm); color: var(--color-muted); }
.chip { display: inline-block; padding: var(--space-3xs) var(--space-xs); border-radius: var(--radius-pill); font-weight: 600; font-size: var(--text-xs); line-height: 1.5; white-space: nowrap; }
.chip-active { background: var(--color-accent); color: var(--color-accent-ink); }
.chip-quiet { border: var(--rule-hair) solid var(--color-rule); color: var(--color-muted); font-weight: 500; }
.fig-date, .fig-release { font-family: var(--font-outlier); font-size: var(--text-xs); color: var(--color-muted); font-variant-numeric: tabular-nums; }
.fig-release { overflow-wrap: anywhere; }

/* The replacements ledger on the home page. */
.ledger { list-style: none; margin: 0; padding: 0; border-top: var(--rule-bar) solid var(--color-rule-2); }
.ledger li { display: grid; grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); gap: var(--space-2xs) var(--space-lg); padding-block: var(--space-xs); padding-inline: var(--space-xs); border-bottom: var(--rule-hair) solid var(--color-rule); align-items: baseline; }
.ledger-product { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-lg); display: inline-flex; align-items: center; min-height: 44px; margin-block: calc(-1 * var(--space-xs)); padding-block: var(--space-xs); }
.ledger-apps { min-width: 0; }

/* Link columns for the category and product indexes: name and count in one flex row. */
.linkcols { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 var(--gap-columns); border-top: var(--rule-bar) solid var(--color-rule-2); }
.linkcols li { border-bottom: var(--rule-hair) solid var(--color-rule); min-width: 0; }
.linkcols a { display: flex; align-items: baseline; gap: var(--space-xs); min-height: 44px; padding-block: var(--space-2xs); text-decoration: none; }
.linkcols a:hover { text-decoration: underline; text-decoration-color: var(--color-accent); }
.linkcols .item-name { flex: 1 1 auto; min-width: 0; text-wrap: balance; }
.linkcols .n { flex: none; font-family: var(--font-outlier); font-size: var(--text-xs); color: var(--color-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The record: an app page's facts, filed as ruled cells. */
.altto { margin-top: var(--space-lg); padding: var(--space-md); background: var(--color-band); border-left: var(--rule-bar) solid var(--color-accent-2); }
.altto-label { display: block; font-weight: 600; font-size: var(--text-sm); color: var(--color-ink); margin-bottom: var(--space-2xs); }
.altto-list { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-xl); line-height: 1.3; color: var(--color-ink); max-width: 44ch; }
.altto-list a { text-decoration-color: var(--color-accent-2); }
.record { margin-top: var(--space-lg); border-top: var(--rule-bar) solid var(--color-rule-2); }
.record > div { display: grid; grid-template-columns: minmax(0, 12rem) minmax(0, 1fr); gap: var(--space-2xs) var(--space-lg); padding-block: var(--space-xs); border-bottom: var(--rule-hair) solid var(--color-rule); }
.record dt { font-weight: 500; font-size: var(--text-sm); color: var(--color-muted); }
.record dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.record .num { font-size: var(--text-md); }

/* The comparison table and differences list (Pass 8): two records set
   line against line, fanfold banding on alternate rows like the entry
   lists, figures in the mono face. Cells wrap anywhere (Pass 9, the
   .record rule): a website URL or a release tag is one unbreakable
   token, and the sheet's break-word never shrinks an auto-layout
   column's min-content, so the widest cells set the table's width. */
.compare-table { width: 100%; border-collapse: collapse; border-top: var(--rule-bar) solid var(--color-rule-2); }
.compare-table th, .compare-table td { text-align: left; vertical-align: top; padding: var(--space-xs) var(--space-sm); border-bottom: var(--rule-hair) solid var(--color-rule); min-width: 0; overflow-wrap: anywhere; }
.compare-table thead th { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-lg); line-height: 1.2; color: var(--color-ink); }
.compare-table thead th:first-child { width: 12rem; }
.compare-table tbody th { font-weight: 500; font-size: var(--text-sm); color: var(--color-muted); }
.compare-table tbody tr:nth-child(even) { background: var(--color-band); }
.cmp-head { display: flex; align-items: center; gap: var(--space-xs); min-width: 0; }
.diffs { list-style: none; margin: 0; padding: 0; border-top: var(--rule-bar) solid var(--color-rule-2); max-width: 65ch; }
.diffs li { padding-block: var(--space-xs); padding-inline: var(--space-xs); border-bottom: var(--rule-hair) solid var(--color-rule); }
.diffs li:nth-child(even) { background: var(--color-band); }
@media (max-width: 40rem) {
  .compare-table th, .compare-table td { padding-inline: var(--space-xs); }
  .compare-table thead th { font-size: var(--text-md); }
  .cmp-head { flex-wrap: wrap; }
  /* Pass 9: on a phone the table restacks, each row a labelled block —
     the field name over the two records side by side in minmax(0, 1fr)
     columns (Hallmark F25 and the tables rule in responsive.md: keep
     the columns that name and compare in view, restacked; the same
     move .record makes at this width). Measured before the fix, 189
     of 426 pair tables had a min-content wider than the 328px a
     360px phone leaves inside the sheet — the widest 450px — so
     overflow-x: clip cut the second record off the edge. Restacked,
     each record gets half the sheet and wraps anywhere; nothing is
     clipped and the page never scrolls sideways. */
  .compare-table, .compare-table thead, .compare-table tbody { display: block; }
  .compare-table thead tr { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .compare-table thead th:first-child { display: none; }
  .compare-table tbody tr { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border-bottom: var(--rule-hair) solid var(--color-rule); }
  .compare-table tbody th { grid-column: 1 / -1; border-bottom: none; padding-bottom: 0; }
  .compare-table tbody td { border-bottom: none; }
}

/* Entry names are h2 on category pages (heading order runs h1, then the
   entries) and h3 under a section head elsewhere. The class carries the
   title styling either way, so the printed row never changes. */
h2.entry-name { font-size: var(--text-title); text-transform: none; line-height: 1.2; letter-spacing: var(--tracking-display); max-width: 44ch; }

/* The screenshot: the project's own og:image, framed like a filed print
   under the app page head. If the image fails, the figure removes itself
   (inline onerror) and the page keeps its shape. */
.shot { margin: var(--space-lg) 0 0; padding: var(--space-sm); border: var(--rule-hair) solid var(--color-rule); background: var(--color-band); }
.shot img { width: 100%; border-radius: var(--radius-card); }

/* Long-form text column (the About page). */
.text-col { max-width: 65ch; }
.text-col p + p { margin-top: var(--space-sm); }

/* Footer */
.site-foot { margin-top: var(--section-gap-s); padding-block: var(--space-xl) var(--space-2xl); border-top: var(--rule-bar) solid var(--color-rule-2); }
.foot-mark { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-xl); letter-spacing: var(--tracking-display); color: var(--color-ink); }
.foot-nav { display: flex; flex-wrap: wrap; gap: var(--space-2xs) var(--space-md); margin-top: var(--space-sm); }
.foot-nav a { font-weight: 500; font-size: var(--text-sm); color: var(--color-ink-2); white-space: nowrap; display: inline-flex; align-items: center; min-height: 44px; padding-inline: var(--space-2xs); }
.site-foot p { max-width: 65ch; }
.foot-note { margin-top: var(--space-md); font-size: var(--text-sm); color: var(--color-muted); }

/* Small screens: one column, figures become a row under the filing. */
@media (max-width: 60rem) {
  .linkcols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ledger li { grid-template-columns: minmax(0, 11rem) minmax(0, 1fr); }
}
@media (max-width: 40rem) {
  .linkcols { grid-template-columns: minmax(0, 1fr); }
  .ledger li { grid-template-columns: minmax(0, 1fr); gap: var(--space-3xs); }
  .entry { grid-template-columns: var(--icon-size) minmax(0, 1fr); grid-template-areas: "icon main" ". figs"; row-gap: var(--space-xs); }
  .entry-figs { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2xs) var(--space-md); max-width: none; }
  .fig-top, .fig-sub { display: contents; }
  .record > div { grid-template-columns: minmax(0, 1fr); }
  .record dd { margin-top: var(--space-3xs); }
  .hero { padding-top: var(--space-xl); }
  .page-head { padding-top: var(--space-lg); }
  .page-head--app { gap: var(--space-sm); }
}
