/* ---------------------------------------------------------------------------
   richardham.es
   A stacked-pane reading surface. Panes are sticky columns in a horizontal
   scroller: each new pane slides over the last, leaving a spine behind.
   --------------------------------------------------------------------------- */

/* Source Serif 4 (variable, 400–600), self-hosted. SIL Open Font License 1.1.
   Latin and Latin Extended subsets only, roman and italic. */

@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/source-serif-4-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/source-serif-4-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Source Serif 4";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/source-serif-4-latin-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Source Serif 4";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/source-serif-4-latin-ext-italic.woff2")
    format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --paper: #fdfbf7;
  --ink: #22201d;
  --ink-muted: rgba(34, 32, 29, 0.66);
  --ink-faint: rgba(34, 32, 29, 0.42);
  --rule: rgba(34, 32, 29, 0.11);
  --rule-strong: rgba(34, 32, 29, 0.18);
  --accent: #8a3149;
  --accent-strong: #5e2031;

  --pane-w: 26rem;
  --spine: 2.75rem;

  --serif: "Source Serif 4", "Iowan Old Style", "Palatino Linotype", Palatino,
    Georgia, "Times New Roman", serif;
  --sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* --- Links ---------------------------------------------------------------- */

a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: rgba(138, 49, 73, 0.32);
  transition: color 120ms ease, text-decoration-color 120ms ease;
}

a:hover {
  color: var(--accent-strong);
  text-decoration-color: currentColor;
}

a:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 3px;
  text-decoration: none;
}

/* --- The stack ------------------------------------------------------------ */

.panes {
  position: relative;
  display: flex;
  align-items: stretch;
  height: 100vh;
  height: 100dvh;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
}

.pane {
  position: sticky;
  left: calc(var(--i, 0) * var(--spine));
  z-index: var(--i, 0);
  flex: 0 0 var(--pane-w);
  width: var(--pane-w);
  height: 100vh;
  height: 100dvh;
  display: flex;
  background: var(--paper);
  border-left: 1px solid var(--rule);
}

.pane:first-child {
  border-left: 0;
}

/* The spine: a permanent left gutter that carries the running title once the
   pane has been covered by the one after it. */

.pane__spine {
  position: relative;
  flex: 0 0 var(--spine);
  border-right: 1px solid transparent;
  transition: border-color 180ms ease;
}

.pane__spine-label {
  position: absolute;
  top: 2.25rem;
  left: 50%;
  transform: translateX(-50%);
  writing-mode: vertical-rl;
  white-space: nowrap;
  font-family: var(--sans);
  font-size: 0.7rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-faint);
  opacity: 0;
  transition: opacity 220ms ease;
  pointer-events: none;
  user-select: none;
}

.pane.is-collapsed .pane__spine-label {
  opacity: 1;
}

.pane.is-collapsed .pane__spine {
  border-right-color: var(--rule);
  cursor: pointer;
}

.pane.is-collapsed .pane__spine:hover .pane__spine-label {
  color: var(--ink);
}

.pane__body {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 3.25rem 2.25rem 6rem 1rem;
}

/* --- Typography ----------------------------------------------------------- */

h1,
h2,
h3 {
  font-weight: 600;
  line-height: 1.2;
  margin: 0;
  text-wrap: balance;
}

.wordmark {
  font-size: 2.4rem;
  letter-spacing: -0.015em;
  margin-bottom: 1.5rem;
}

.pane__title {
  font-size: 1.4rem;
  letter-spacing: -0.01em;
}

/* A project page carries its title at wordmark scale. */
.pane__title--page {
  font-size: 2rem;
  letter-spacing: -0.015em;
}

.pane__standfirst {
  margin: 0.5rem 0 0;
  font-size: 0.95rem;
  font-style: italic;
  color: var(--ink-muted);
  text-wrap: pretty;
}

.pane__header {
  padding-bottom: 1.75rem;
}

/* The standfirsts run to two or three lines, which left the first entry in
   each column starting at a different height. A floor under the header holds
   them level; the columns are a fixed width, so the wrap is stable. */
@media (min-width: 901px) {
  .pane__header {
    min-height: 8.75rem;
  }
}

.eyebrow {
  margin: 0 0 0.6rem;
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

p {
  margin: 0 0 1.1em;
  text-wrap: pretty;
}

p:last-child {
  margin-bottom: 0;
}

.lede > p {
  margin-bottom: 1.25em;
}

.quiet {
  color: var(--ink-muted);
}

/* --- Social links --------------------------------------------------------- */

.social {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.9rem;
  margin: 0.5em 0 0;
  padding: 0;
  list-style: none;
  font-family: var(--sans);
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.social a {
  color: var(--ink-faint);
  text-decoration-color: var(--rule-strong);
  text-underline-offset: 0.3em;
}

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

/* --- Scroll hint ----------------------------------------------------------
   The panes read left to right, which nothing else on the page announces.
   A quiet marker in the corner, gone the moment it has been understood. */

.scroll-hint {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 999;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper);
  font-family: var(--sans);
  font-size: 0.7rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-faint);
  pointer-events: none;
  user-select: none;
  transition: opacity 400ms ease, transform 400ms ease;
}

.scroll-hint__arrow {
  font-size: 0.95rem;
  line-height: 1;
  color: var(--accent);
  animation: scroll-hint-nudge 1.9s ease-in-out infinite;
}

.scroll-hint.is-hidden {
  opacity: 0;
  transform: translateX(0.5rem);
}

@keyframes scroll-hint-nudge {
  0%,
  100% {
    transform: translateX(0);
  }
  50% {
    transform: translateX(0.3rem);
  }
}

/* --- Entries (the project cards) ------------------------------------------ */

.entry {
  padding: 1.9rem 0;
  border-top: 1px solid var(--rule);
}

.entry:last-child {
  border-bottom: 1px solid var(--rule);
}

.entry__title {
  font-size: 1.12rem;
  letter-spacing: -0.005em;
}

/* Titles read as text first, links second. */
.entry__title a {
  color: var(--ink);
  text-decoration-color: var(--rule-strong);
  text-underline-offset: 0.22em;
}

.entry__title a:hover {
  color: var(--accent);
  text-decoration-color: rgba(138, 49, 73, 0.45);
}

.entry__body {
  margin: 0.55rem 0 0;
  font-size: 0.95rem;
  color: var(--ink-muted);
}

/* --- The picture each entry keeps underneath it ---------------------------
   Hovering an entry slides its image out beneath the text — a drawer, not a
   pop-up: the row grows from 0fr to 1fr so the column below is pushed rather
   than covered. Where there is no hover (touch, narrow layouts) the images
   just sit open in the flow. */

.entry__figure {
  display: grid;
  grid-template-rows: 0fr;
  margin: 0;
  transition: grid-template-rows 460ms cubic-bezier(0.22, 0.75, 0.2, 1),
    margin-top 460ms cubic-bezier(0.22, 0.75, 0.2, 1);
}

/* The inner block is what the row measures: an image is sized from its own
   intrinsic height when it is the grid item, which leaves dead space under
   short pictures. */
.entry__figure-inner {
  min-height: 0;
  overflow: hidden;
}

.entry__figure img {
  display: block;
  width: 100%;
  height: auto;
  opacity: 0;
  transform: translateY(-0.5rem);
  transition: opacity 260ms ease, transform 460ms cubic-bezier(0.22, 0.75, 0.2, 1);
}

/* Covers and other upright pictures are held to a height instead, so a
   portrait does not run the length of the column. */
.entry__figure--tall img {
  width: auto;
  height: 14rem;
}

.entry:hover .entry__figure,
.entry:focus-within .entry__figure {
  grid-template-rows: 1fr;
  margin-top: 1.15rem;
}

.entry:hover .entry__figure img,
.entry:focus-within .entry__figure img {
  opacity: 1;
  transform: none;
  transition-delay: 90ms;
}

@media (hover: none) {
  .entry__figure {
    grid-template-rows: 1fr;
    margin-top: 1.15rem;
  }

  .entry__figure img {
    opacity: 1;
    transform: none;
  }
}

/* --- Lists ---------------------------------------------------------------- */

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

.list li {
  position: relative;
  padding-left: 1.15rem;
  margin-bottom: 0.7em;
}

.list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 4px;
  height: 1px;
  background: var(--ink-faint);
}

.list--numbered {
  counter-reset: item;
}

.list--numbered li {
  padding-left: 1.6rem;
}

.list--numbered li::before {
  counter-increment: item;
  content: counter(item) ".";
  top: 0;
  width: auto;
  height: auto;
  background: none;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* --- Back link and outbound calls to action ------------------------------- */

.pane__back {
  display: inline-block;
  margin-bottom: 2.5rem;
  font-family: var(--sans);
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--ink-faint);
  text-decoration-color: var(--rule-strong);
}

.pane__back:hover {
  color: var(--ink);
}

.cta {
  display: inline-block;
  margin-top: 2.25rem;
  font-size: 0.95rem;
  color: var(--accent);
  text-decoration-color: rgba(138, 49, 73, 0.32);
}

/* A row of them shares one top margin, so a page can offer the thing itself
   and the source behind it without the second sitting a paragraph lower. */

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.75rem;
  margin-top: 2.25rem;
}

.cta-row .cta {
  margin-top: 0;
}

/* --- Subscribe ------------------------------------------------------------ */

.subscribe {
  margin-top: 2.75rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--rule);
}

.subscribe__label {
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
  color: var(--ink-muted);
}

.subscribe__row {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 0.4rem;
}

.subscribe__row:focus-within {
  border-bottom-color: var(--accent);
}

.subscribe input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  padding: 0;
  font-family: var(--serif);
  font-size: 0.95rem;
  color: var(--ink);
}

.subscribe input::placeholder {
  color: var(--ink-faint);
}

.subscribe input:focus {
  outline: none;
}

.subscribe button {
  flex: 0 0 auto;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--sans);
  font-size: 0.72rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--accent);
}

.subscribe button:hover {
  color: var(--accent-strong);
}

.subscribe__note {
  margin: 0.6rem 0 0;
  font-family: var(--sans);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
}

/* --- Standalone (no-JS / direct visit to a project page) -------------------
   Same pane, read on its own: a slightly wider measure, since nothing is
   competing for the space. Only the container carries `.standalone`, so a
   pane pulled into the stack keeps its column width. */

.standalone .pane {
  border-left: 0;
  width: 40rem;
  flex: 0 0 40rem;
}

.standalone .pane__body {
  padding-left: 1.5rem;
  padding-right: 3rem;
}

/* --- Narrow viewports: the stack becomes an ordinary page ----------------- */

@media (max-width: 900px) {
  .panes {
    display: block;
    height: auto;
    overflow: visible;
  }

  /* Nothing scrolls sideways here, so there is nothing to hint at. */
  .scroll-hint {
    display: none;
  }

  .pane {
    position: static;
    display: block;
    width: auto;
    height: auto;
    flex: none;
    border-left: 0;
    border-top: 1px solid var(--rule);
  }

  .pane:first-child {
    border-top: 0;
  }

  .pane__spine {
    display: none;
  }

  /* The standalone measure is set with a more specific selector, so it has to
     be unwound explicitly here or a project page overflows the viewport. */
  .standalone .pane {
    width: auto;
    flex: none;
  }

  .pane__body,
  .standalone .pane__body {
    height: auto;
    overflow: visible;
    max-width: 38rem;
    padding: 2.75rem 1.5rem 3.25rem;
  }

  .wordmark {
    font-size: 2.1rem;
  }

  /* Nothing to hover here, so the images are simply part of the entry. */
  .entry__figure {
    grid-template-rows: 1fr;
    margin-top: 1.15rem;
  }

  .entry__figure img {
    opacity: 1;
    transform: none;
    max-height: 18rem;
  }
}

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

  .scroll-hint__arrow {
    animation: none;
  }

  * {
    transition-duration: 0.01ms !important;
  }
}
