/* Screencappy site. One stylesheet, no external requests.

   Type, colour and motion come from the Smollet design system: a cream
   scholarly shell by default, the ink shell under prefers-color-scheme: dark.
   Text tiers are alpha of a single ink colour rather than separate greys, and
   emphasis is a solid ink fill rather than a coloured accent. */

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

@font-face {
  font-family: 'Instrument Serif';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/InstrumentSerif-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/InstrumentSerif-Italic.woff2') format('woff2');
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/InstrumentSans-Variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url('/fonts/Newsreader-Regular-Variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 300 600;
  font-display: swap;
  src: url('/fonts/Newsreader-Italic-Variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 300 500;
  font-display: swap;
  src: url('/fonts/SplineSansMono-Variable.woff2') format('woff2');
}

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

:root {
  color-scheme: light dark;

  --ink-950: oklch(0.145 0.005 285);
  --ink-900: oklch(0.185 0.007 285);
  --paper-cream: oklch(0.973 0.013 88);
  --paper-cream-ink: oklch(0.255 0.02 66);

  --font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --font-serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-ui: 'Instrument Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-mono: 'Spline Sans Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --label-tracking: 0.18em;

  --radius-xs: 7px;
  --radius-sm: 11px;
  --radius-md: 16px;
  --radius-lg: 18px;

  --shadow-sm: 0 1px 4px oklch(0.25 0.02 280 / 0.1);
  --shadow-md: 0 10px 34px oklch(0.25 0.02 280 / 0.16), 0 1px 4px oklch(0.25 0.02 280 / 0.1);

  --ease-soft-out: cubic-bezier(0.22, 1, 0.36, 1);
  --duration-instant: 0.15s;
  --duration-fast: 0.25s;
  --duration-view: 0.9s;

  --content-max-width: 960px;
  --content-max-width-reading: 700px;

  /* Cream shell: the default look. */
  --surface-canvas: var(--paper-cream);
  --surface-canvas-deep: oklch(0.948 0.015 86);
  --surface-card: oklch(0.955 0.013 86);
  --surface-card-hover: oklch(0.93 0.015 82);

  --text-primary: var(--paper-cream-ink);
  --text-secondary: color-mix(in oklch, var(--paper-cream-ink) 66%, transparent);
  --text-tertiary: color-mix(in oklch, var(--paper-cream-ink) 48%, transparent);
  --text-inverse: var(--paper-cream);

  --border-hairline: color-mix(in oklch, var(--paper-cream-ink) 10%, transparent);
  --border-default: color-mix(in oklch, var(--paper-cream-ink) 16%, transparent);
  --border-strong: color-mix(in oklch, var(--paper-cream-ink) 32%, transparent);

  --accent: var(--text-primary);
  --accent-strong: oklch(0.16 0.012 66);
  --accent-tint: color-mix(in oklch, var(--paper-cream-ink) 7%, transparent);
  --text-on-accent: var(--paper-cream);
  --focus-ring: color-mix(in oklch, var(--paper-cream-ink) 55%, transparent);
  --link-hover: color-mix(in oklch, var(--paper-cream-ink) 58%, transparent);
}

/* Ink shell: same type, same components, flipped aliases. */
@media (prefers-color-scheme: dark) {
  :root {
    --surface-canvas: var(--ink-950);
    --surface-canvas-deep: oklch(0.12 0.004 285);
    --surface-card: var(--ink-900);
    --surface-card-hover: color-mix(in oklch, var(--ink-900) 84%, white 7%);

    --text-primary: oklch(0.95 0.006 88);
    --text-secondary: color-mix(in oklch, oklch(0.95 0.006 88) 70%, transparent);
    --text-tertiary: color-mix(in oklch, oklch(0.95 0.006 88) 50%, transparent);
    --text-inverse: var(--ink-950);

    --border-hairline: color-mix(in oklch, white 12%, transparent);
    --border-default: color-mix(in oklch, white 20%, transparent);
    --border-strong: color-mix(in oklch, white 38%, transparent);

    --accent: var(--text-primary);
    --accent-strong: white;
    --accent-tint: color-mix(in oklch, white 12%, transparent);
    --text-on-accent: var(--ink-950);
    --focus-ring: color-mix(in oklch, white 55%, transparent);
    --link-hover: color-mix(in oklch, oklch(0.95 0.006 88) 62%, transparent);
  }
}

/* --------------------------------------------------------------- shared */

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface-canvas);
  color: var(--text-primary);
  font: 400 16px/1.6 var(--font-ui);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

main {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0 28px 96px;
}

main.prose {
  max-width: var(--content-max-width-reading);
  padding-top: 8px;
}

a { color: var(--text-primary); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--link-hover); }
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; border-radius: 3px; }

.eyebrow {
  font: 500 12px/1.4 var(--font-mono);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

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

/* Only the inner pages carry a header; the home page masthead is its hero,
   so this tracks the reading column those pages use. */
.site-header {
  max-width: var(--content-max-width-reading);
  margin: 0 auto;
  padding: 26px 28px 0;
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  color: var(--text-primary);
  font: italic 400 25px/1 var(--font-display);
}
.wordmark:hover { color: var(--text-primary); }
.wordmark img { width: 22px; height: 22px; border-radius: 6px; align-self: center; }

.byline {
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.byline a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--border-default); }
.byline a:hover { color: var(--text-primary); border-bottom-color: var(--border-strong); }

/* "Smollet" is a wordmark, so it is set as one: the approved lowercase
   logotype in Instrument Serif italic, rather than swept up into the tracked
   mono label that introduces it. The markup keeps the name capitalised so the
   entity name and the screen reader text stay "Smollet". */
.byline a, .colophon a {
  font: italic 400 15px/1 var(--font-display);
  letter-spacing: 0;
  text-transform: lowercase;
  vertical-align: -0.08em;
}

/* The home page masthead is the hero itself, so the byline sits under it as an
   imprint line rather than repeating the name in a header. */
.imprint { margin: 14px 0 0; }

/* ------------------------------------------------------------------ hero */

/* One column, one left edge. Every child is a flex item flush to the same
   start line, so the wordmark, the tagline, the button and the browser row
   share an edge instead of each inventing their own. Rhythm comes from the
   gap plus per-child margin-top, never from a wrapper box: there are no
   panels, cards or pills in the hero, which is what keeps it reading as one
   masthead rather than four stacked widgets. */
.hero {
  padding: 56px 0 8px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.hero > * { margin-left: 0; margin-right: 0; }

.hero .lens { width: 64px; height: 64px; border-radius: var(--radius-md); display: block; }

.hero h1 {
  font: italic 400 clamp(56px, 11vw, 84px) / 1.02 var(--font-display);
  margin: 22px 0 0;
  letter-spacing: -0.005em;
}

.hero .tag {
  font: 400 20px/1.55 var(--font-serif);
  color: var(--text-secondary);
  max-width: 470px;
  margin: 16px 0 0;
}

.cta { margin-top: 32px; display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }

/* The line that sits beside the download button and explains why the button
   is not a store link yet. Sized to sit under it on narrow screens. */
.cta-note {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
  max-width: 30ch;
}

.btn {
  display: inline-block;
  padding: 11px 22px;
  border-radius: var(--radius-xs);
  text-decoration: none;
  font: 500 14.5px/1.35 var(--font-ui);
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  background: transparent;
  transition: background-color var(--duration-fast) var(--ease-soft-out),
              border-color var(--duration-fast) var(--ease-soft-out),
              color var(--duration-fast) var(--ease-soft-out);
}
.btn:hover { background: var(--accent-tint); color: var(--text-primary); }
.btn:active { transition-duration: var(--duration-instant); background: var(--accent-tint); }

.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
}
.btn.primary:hover {
  background: color-mix(in oklch, var(--accent) 88%, var(--surface-canvas));
  border-color: color-mix(in oklch, var(--accent) 88%, var(--surface-canvas));
  color: var(--text-on-accent);
}
.btn.primary:active { background: var(--accent-strong); border-color: var(--accent-strong); }

/* Project health badges. These are the only remote images on the site: they
   are pure decoration, so if shields.io is unreachable the alt text still
   reads as a sentence and no page content depends on them. They live in the
   developer strip in the footer, never in the hero, so they get no box of
   their own: a bare row of images on the footer's own ground. */
.health {
  list-style: none;
  max-width: 100%;
  margin: 14px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 12px;
}
.health li { display: flex; }
/* line-height 0 stops the inline image leaving a descender gap inside the link */
.health a { display: block; line-height: 0; border-radius: 4px; }
.health img { display: block; height: 20px; width: auto; }

/* Browser support, set monochrome so it reads as a line of type, not a
   logo wall. Chrome and Edge are tested continuously; Opera and Brave run the
   same Chromium build; Firefox is a separate build attached to each release.
   The icons are decorative and the sentence beside them carries the meaning,
   so screen readers get the sentence only. */
.works { margin-top: 30px; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.works ul { list-style: none; display: flex; align-items: center; gap: 13px; margin: 0; padding: 0; }
.works svg { display: block; width: 19px; height: 19px; fill: var(--text-tertiary); }
.works p { margin: 0; font-size: 14px; color: var(--text-secondary); }

/* --------------------------------------------------------- index content */

.answer { margin-top: 64px; }
.answer h2 { font: 500 30px/1.22 var(--font-serif); margin: 0 0 16px; }
.answer p { margin: 0 0 16px; max-width: 66ch; font-size: 16.5px; color: var(--text-secondary); }
.answer p strong { color: var(--text-primary); font-weight: 600; }

.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2px;
  margin-top: 56px;
  background: var(--border-hairline);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.card {
  background: var(--surface-card);
  padding: 22px 24px 24px;
  transition: background-color var(--duration-fast) var(--ease-soft-out);
}
.card:hover { background: var(--surface-card-hover); }
.card h3 { margin: 0 0 7px; font: 600 18px/1.3 var(--font-serif); }
.card p { margin: 0; color: var(--text-secondary); font-size: 14.5px; line-height: 1.55; }

.keys {
  margin-top: 48px;
  color: var(--text-secondary);
  font: 500 12.5px/2 var(--font-mono);
  letter-spacing: 0.04em;
}

kbd {
  background: var(--surface-canvas-deep);
  border: 1px solid var(--border-default);
  border-radius: 5px;
  padding: 2px 6px;
  font: 500 12.5px var(--font-mono);
  color: var(--text-primary);
}

/* ------------------------------------------------------- install steps */

/* Three steps, drawn rather than described. The art is inline SVG on
   currentColor so it inherits the shell's ink and needs no second file and no
   dark mode variant. Numbering comes from the ordered list itself, so the
   sequence survives with styles off. */
.install { margin-top: 64px; }
.install h2 { font: 500 30px/1.22 var(--font-serif); margin: 0 0 14px; }
.install > p { margin: 0 0 8px; max-width: 62ch; font-size: 16.5px; color: var(--text-secondary); }

.steps {
  counter-reset: step;
  list-style: none;
  margin: 34px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  background: var(--border-hairline);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.steps li {
  counter-increment: step;
  background: var(--surface-card);
  padding: 24px 24px 26px;
  transition: background-color var(--duration-fast) var(--ease-soft-out);
}
.steps li:hover { background: var(--surface-card-hover); }

.step-art { display: block; width: 72px; height: 72px; color: var(--text-tertiary); }
.step-art svg { display: block; width: 100%; height: 100%; }

.steps h3 {
  margin: 16px 0 6px;
  font: 600 17px/1.35 var(--font-serif);
  display: flex;
  gap: 9px;
  align-items: baseline;
}
/* The step number is generated, so it reads as ornament to a screen reader
   walking the list rather than being announced twice. */
.steps h3::before {
  content: counter(step);
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: var(--label-tracking);
  color: var(--text-tertiary);
}
.steps p { margin: 0; color: var(--text-secondary); font-size: 14.5px; line-height: 1.6; }

.install > p.install-foot { margin: 22px 0 0; font-size: 15px; color: var(--text-secondary); max-width: 62ch; }

/* ------------------------------------------------------------- long form */

.crumbs {
  font: 500 11.5px/1.5 var(--font-mono);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 40px 0 30px;
}
.crumbs a { color: inherit; }

.prose h1 {
  font: italic 400 clamp(38px, 7vw, 52px) / 1.06 var(--font-display);
  margin: 0 0 14px;
}
.prose h2 { font: 500 27px/1.26 var(--font-serif); margin: 48px 0 12px; }
.prose h3 { font: 600 19px/1.4 var(--font-serif); margin: 32px 0 8px; }

.prose p, .prose li { font: 400 18px/1.72 var(--font-serif); color: var(--text-secondary); }
.prose p strong, .prose li strong { color: var(--text-primary); font-weight: 600; }
.prose ul, .prose ol { padding-left: 20px; }
.prose li { margin-bottom: 10px; }
.prose ol li::marker, .prose ul li::marker { color: var(--text-tertiary); }

.verified, .prose .verified {
  font: 500 11.5px/1.5 var(--font-mono);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 34px;
}

.lede, .prose .lede { font-size: 21px; line-height: 1.62; color: var(--text-primary); }

code {
  background: var(--surface-canvas-deep);
  border: 1px solid var(--border-hairline);
  border-radius: 5px;
  padding: 1px 6px;
  font: 400 0.82em var(--font-mono);
  color: var(--text-primary);
}
pre {
  background: var(--surface-canvas-deep);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  padding: 16px 18px;
  overflow-x: auto;
}
pre code { background: none; border: 0; padding: 0; font-size: 13.5px; }

/* Tables scroll inside their own box rather than the page */
.table-wrap { overflow-x: auto; margin: 22px 0; }
table { border-collapse: collapse; width: 100%; min-width: 520px; }
th, td {
  text-align: left;
  padding: 12px 16px 12px 0;
  border-bottom: 1px solid var(--border-hairline);
  vertical-align: top;
  font: 400 15.5px/1.55 var(--font-ui);
  color: var(--text-secondary);
}
thead th {
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-secondary);
  border-bottom-color: var(--border-default);
}
tbody th { color: var(--text-primary); font-weight: 600; }

.note {
  background: var(--surface-card);
  border-radius: var(--radius-md);
  border-left: 2px solid var(--border-strong);
  padding: 18px 22px;
  margin: 28px 0;
}
.note p { margin: 0 0 12px; font-size: 16.5px; line-height: 1.65; color: var(--text-secondary); }
.note p:last-child { margin: 0; }

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

/* One footer, byte for byte identical on every page: site nav, then the quiet
   developer strip, then the byline. Nothing else on any page carries a link
   row, so a page ends exactly once. */
footer {
  margin-top: 72px;
  padding-top: 26px;
  border-top: 1px solid var(--border-hairline);
  color: var(--text-secondary);
  font: 400 14.5px/1.7 var(--font-ui);
}
footer p, .prose footer p { margin: 0 0 6px; font: inherit; color: inherit; max-width: none; }
footer a { color: var(--text-secondary); }
footer a:hover { color: var(--text-primary); }

.footer-nav { display: flex; flex-wrap: wrap; gap: 8px 22px; font-size: 15px; }
.footer-nav a { color: var(--text-primary); }
.footer-nav a:hover { color: var(--link-hover); }

/* The developer strip. Everything aimed at people who want the source lives
   here and only here: it is deliberately set below the reader-facing nav, in
   smaller type, so it is findable without being the first thing anyone meets. */
.dev-strip {
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid var(--border-hairline);
}
.dev-label {
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin: 0 0 8px;
}
.dev-strip p, .prose footer .dev-strip p { font-size: 14px; max-width: 62ch; }
.dev-strip p a { text-decoration-thickness: 1px; }

/* Trademark and affiliation notices belong to the page that makes the
   comparison, not to the shared footer, so they sit at the end of the article
   above it and the footer stays identical everywhere. */
.trademark {
  margin: 40px 0 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-tertiary);
}
.prose .trademark { font: 400 13.5px/1.6 var(--font-ui); color: var(--text-tertiary); }

.colophon, .prose footer .colophon {
  margin: 26px 0 0;
  font: 500 11px/1.5 var(--font-mono);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.colophon a { color: inherit; }
.colophon a:hover { color: var(--text-primary); }

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

.rise { animation: rise-in var(--duration-view) var(--ease-soft-out) both; }
.rise-2 { animation-delay: 0.08s; }
.rise-3 { animation-delay: 0.16s; }

@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); filter: blur(6px); }
  to { opacity: 1; transform: translateY(0); filter: blur(0); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01s !important;
    animation-delay: 0s !important;
    transition-duration: 0.01s !important;
  }
}

/* ------------------------------------------------- cross-document motion */

/* Page to page navigation, without a single line of JavaScript.
   `navigation: auto` is the only opt-in there is; both the page being left and
   the page being entered have to declare it, which is why this lives in the
   shared stylesheet every page already loads. Browsers that do not implement
   it drop the whole at-rule and navigate normally, which is a complete and
   acceptable outcome, not a fallback to patch around. */
@view-transition {
  navigation: auto;
}

/* Timing goes on the group, which the image pair and the old and new
   snapshots inherit, so the fade cannot drift out of step with the morph.
   Kept short: this should register as continuity, not as an animation. */
::view-transition-group(root) {
  animation-duration: 0.24s;
  animation-timing-function: var(--ease-soft-out);
}

/* The masthead is the one thing common to every inner page, so naming it lets
   it hold still while the article underneath it changes. The name is a bare
   identifier, must be unique in the document, and has to match on both sides
   of the navigation, which a shared stylesheet guarantees. The home page
   masthead is its hero rather than this header, so on that one leg the header
   animates in on its own, which is the correct graceful outcome. */
.site-header { view-transition-name: site-header; }

::view-transition-group(site-header) {
  animation-duration: 0.24s;
  animation-timing-function: var(--ease-soft-out);
}

/* The blanket reduced-motion rule above cannot reach these: `*` does not match
   view transition pseudo-elements. Cutting the animation leaves the transition
   mechanism in place and simply swaps instantly, which is what the reduced
   motion request actually asks for. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

@media (max-width: 860px) {
  .steps { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  main { padding: 0 20px 72px; }
  .site-header { padding: 22px 20px 0; }
  .hero { padding-top: 40px; }
  .hero h1 { font-size: clamp(44px, 13vw, 64px); }
  .hero .tag { max-width: none; font-size: 18px; }
  .grid { grid-template-columns: 1fr; }
  .prose p, .prose li { font-size: 17px; }
  .cta-note { max-width: none; }
}
