/* The project pages: /sieve, /journey, /beyond-argument and /binaural.

   One layout for all four, so sections, spacing and sizes cannot drift apart
   page by page. Each page's own stylesheet sets only its theme: the tokens
   below, its fonts, and anything only it has. Every size here is a role, not
   a page: one heading scale, one paragraph scale, one caption scale.

   Tokens each page defines:
     --page --card --ink --ink2 --muted --rule --accent --wash --scrim
     --shadow --on-accent      colours
     --font-head --font-text   headings and labels; reading text
     --head-weight             700, or 300 for a light display face
     --cap-gap                 the room the heading face leaves above a
                               capital, as a fraction of its size, measured
                               in a browser; the hero screen aligns to it

   The rules every section follows:
   - One line per section, the divider above it. Nothing else draws a rule:
     rows and points are separated by space alone.
   - A section's introduction sits under its heading in the left column; the
     right column holds only its content: screenshots, rows or points.
   - No three columns of mixed content, and no shaded boxes side by side.
     Screenshots are the framed things; .aside is one box, alone. */

[hidden] { display: none !important; }
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; }
img { max-width: 100%; }
p { margin: 0; }
figure { margin: 0; }

:root { --gap: clamp(20px, 2.4vw, 28px); }

body {
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-underline-offset: 0.18em; }
strong { font-weight: 700; }

.wrap {
  position: relative;
  max-width: 1120px;
  margin: 0 auto;
  padding-inline: clamp(20px, 5vw, 48px);
  padding-block: clamp(64px, 8vw, 104px) clamp(48px, 6vw, 72px);
  display: grid;
  gap: clamp(64px, 9vw, 112px);
}

/* The way back to /projects, in the top margin. Absolutely placed, so it
   takes no space in the grid and moves nothing. */
.crumb {
  position: absolute;
  top: clamp(20px, 3vw, 36px);
  left: clamp(20px, 5vw, 48px);
  font-family: var(--font-head);
  font-size: 14px;
  line-height: 1;
}
.crumb a {
  color: var(--muted);
  text-decoration: none;
  display: inline-flex;
  gap: 6px;
  align-items: center;
  padding: 6px 0;
}
.crumb a:hover, .crumb a:focus-visible { color: var(--accent); }

/* Type, by role. */
h1, h2 {
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  margin: 0;
  text-wrap: balance;
}
h1 {
  font-size: clamp(36px, 5.4vw, 60px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  max-width: 16ch;
  margin-top: 8px;
}
h2 {
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.12;
  letter-spacing: -0.022em;
}
.eyebrow {
  font-family: var(--font-head);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}
.lede {
  font-size: clamp(18px, 1.9vw, 21px);
  line-height: 1.5;
  color: var(--ink2);
  max-width: 54ch;
}
.story {
  font-size: clamp(17px, 1.6vw, 19px);
  line-height: 1.55;
  color: var(--ink2);
  max-width: 60ch;
}
.title {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 17px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.text { font-size: 16px; line-height: 1.5; color: var(--ink2); }
.note { font-size: 15px; font-style: italic; color: var(--muted); max-width: 60ch; }

/* The opening: icon and name on their own row, then the claim, one paragraph
   and three facts beside the product's main screen; stacked below 900px.
   .hero-text is display: contents so its children sit on the hero's grid;
   the screen spans the text rows plus a last 1fr row, which takes its extra
   height instead of spreading the text apart. It starts level with the
   heading's capitals, not its box. */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  grid-template-rows: auto auto auto auto 1fr;
  column-gap: clamp(32px, 5vw, 64px);
  row-gap: 24px;
  align-items: start;
}
.hero-text { display: contents; }
.hero-text > * { grid-column: 1; }
.hero-text > .brand { grid-column: 1 / -1; }
.hero-shot {
  margin: calc(8px + var(--cap-gap) * clamp(36px, 5.4vw, 60px)) 0 0;
  grid-column: 2;
  grid-row: 2 / 6;
}
.hero-phones {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
  max-width: 560px;
}
.brand { display: flex; align-items: center; gap: 12px; }
.app-icon { width: 52px; height: 52px; display: block; }
.wordmark {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 20px;
  letter-spacing: -0.01em;
}
.facts { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 4px; }
/* What a visitor can do with the app: open it, download it, ask for an
   invite. Written by notes/build-previews.py from its ACTIONS list, under the
   opening paragraph and again in the closing section, so a page offers the
   same things at its top and its bottom. Compact pills in the page's own face
   and accent: the first filled, the rest outlined. --accent-fill lets a theme
   fill with a deeper colour than its text accent; --on-accent is the text on
   that fill and must read against it in both modes. */
.actions { display: flex; flex-wrap: wrap; gap: 10px; padding-top: 4px; }
.act {
  font-family: var(--font-head);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  color: var(--ink);
  border: 1px solid var(--rule-strong, var(--muted));
  border-radius: 999px;
  padding: 11px 18px 12px;
  display: inline-flex;
  gap: 6px;
  align-items: center;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.act:hover, .act:focus-visible { border-color: var(--accent); color: var(--accent); }
.act:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.act-primary {
  color: var(--on-accent);
  background: var(--accent-fill, var(--accent));
  border-color: var(--accent-fill, var(--accent));
}
.act-primary:hover, .act-primary:focus-visible { color: var(--on-accent); filter: brightness(1.08); border-color: var(--accent-fill, var(--accent)); }
.act-out { width: 0.8em; height: 0.8em; flex: none; margin-right: -2px; }

/* The closing section: the app's icon and name, then its actions, which sit
   level with the name rather than at the top of the column. */
.band-get .band-body { align-content: center; }
.band-get .actions { padding-top: 0; }

.facts span {
  font-family: var(--font-head);
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  background: var(--wash);
  border-radius: 999px;
  padding: 6px 12px;
  white-space: nowrap;
}

/* A section: heading in a narrow left column, content in a wide right one,
   on the page grid. A wide section runs its content the full width. */
.band {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  column-gap: clamp(32px, 5vw, 64px);
  row-gap: 28px;
  border-top: 1px solid var(--rule);
  padding-top: 28px;
}
.band-wide { grid-template-columns: minmax(0, 1fr); }
.band-head { display: grid; gap: 12px; align-content: start; }
.band-head .story { font-size: 17px; line-height: 1.55; margin-top: 4px; max-width: 46ch; }
.band-wide .band-head .story { max-width: 62ch; }
.band-body { display: grid; gap: var(--gap); align-content: start; }

/* Rows: a name on the left, a sentence or two on the right, separated by
   space alone. */
.rows { margin: 0; display: grid; gap: 22px; }
.row {
  display: grid;
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
  gap: 4px 24px;
}
.row dd { margin: 0; }

/* Short points as columns, two or four across, separated by space alone. */
.points { display: grid; gap: 32px 40px; }
.points-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.points-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.point { display: grid; gap: 6px; align-content: start; }

/* One framed box, alone in its section: a labeled example. */
.aside {
  background: var(--card);
  border-radius: 6px;
  padding: 22px 24px;
  box-shadow: 0 0 0 1px var(--rule), 0 14px 34px -26px var(--shadow);
  display: grid;
  gap: 12px;
}

/* Screens. A screenshot button opens the viewer; a framed window is a
   desktop capture, a phone is rounded like the device. */
.shot {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
.shot img { display: block; width: 100%; height: auto; }
.shot:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.window {
  border-radius: 12px;
  overflow: hidden;
  background: var(--card);
  box-shadow: 0 0 0 1px var(--rule), 0 28px 64px -34px var(--shadow), 0 4px 12px -8px var(--shadow);
}
.phone .shot, .hero-phones .shot {
  border-radius: 28px;
  overflow: hidden;
  box-shadow: 0 0 0 1px var(--rule), 0 18px 40px -26px var(--shadow);
}
figcaption { display: grid; gap: 4px; }
.screen { display: grid; gap: 20px; align-content: start; }
.screen figcaption { max-width: 56ch; }
.screens { display: grid; gap: clamp(32px, 4vw, 48px); }
.screens-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap); }
.screens-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gap); }
.screens-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px 20px; }
.screens-4 .title, .phone .title { font-size: 16px; }
.screens-4 .text, .phone .text { font-size: 15px; line-height: 1.45; }

/* Phones fill the content column: three at up to 220px, two sharing the
   column equally. Two at the three-phone width left the right third of the
   section empty on desktop, so a pair now takes the full width with the one
   gap between them, capped so a stacked section on a tablet does not blow
   the pair up past the size of a desktop column. */
.phones { display: grid; gap: var(--gap); }
.phones-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 640px; }
.phones-3 { grid-template-columns: repeat(3, minmax(0, 220px)); }
.phone { display: grid; gap: 20px; align-content: start; }

/* A tall screenshot beside the list it illustrates: the picture and the
   rows split the content column, the rows stacked name over sentence. */
.split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 3.5vw, 44px);
  align-items: start;
}
.split .row { grid-template-columns: minmax(0, 1fr); }

footer {
  border-top: 1px solid var(--rule);
  padding-top: 24px;
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  column-gap: clamp(32px, 5vw, 64px);
  row-gap: 12px;
  font-family: var(--font-head);
  font-size: 14.5px;
  color: var(--muted);
}
footer .notes { display: grid; gap: 6px; }

/* The full-size viewer, /js/preview.js. */
.lightbox {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  display: grid;
  /* Both tracks are declared, and the row is the one that matters. Without
     it the single implicit row is auto sized, so it takes its height from
     the image inside it, and the max-height: 100% below has no definite
     height to resolve against and behaves as none. A 1206 by 2622 phone
     screenshot then kept its natural height and painted 2616px tall in a
     900px window: the viewer opened on the top strip of the shot, which
     reads as not opening at all. Landscape Mac shots fit under max-width
     alone, so the whole set looked fine until a portrait one was clicked.
     Same shape as the minmax(0, 1fr) rule the record rows need. */
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  place-items: center;
  padding: clamp(12px, 3vw, 40px);
  padding-top: calc(env(safe-area-inset-top, 0px) + clamp(12px, 3vw, 40px));
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + clamp(12px, 3vw, 40px));
  z-index: 10;
  cursor: zoom-out;
}
.lightbox img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 12px;
}
.lightbox-close {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 14px);
  right: 16px;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 14px;
  color: #FBF9F4;
  background: rgba(255, 255, 255, 0.14);
  border: 0;
  border-radius: 999px;
  padding: 8px 14px;
  cursor: pointer;
}
.lightbox-close:focus-visible { outline: 2px solid #FBF9F4; outline-offset: 2px; }

/* The arrows and the counter sit on the scrim rather than in the grid, so
   they do not take part in sizing the image. min-width and min-height make
   a thin glyph into a tap target a thumb can actually hit. */
.lightbox-nav {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  min-width: 3rem;
  min-height: 3rem;
  padding: 8px;
  font-family: var(--font-head);
  font-size: 3rem;
  line-height: 1;
  color: #FBF9F4;
  background: none;
  border: 0;
  opacity: 0.6;
  cursor: pointer;
}
.lightbox-prev { left: calc(env(safe-area-inset-left, 0px) + 2px); }
.lightbox-next { right: calc(env(safe-area-inset-right, 0px) + 2px); }
.lightbox-nav:hover, .lightbox-nav:focus-visible { opacity: 1; }
.lightbox-nav:focus-visible { outline: 2px solid #FBF9F4; outline-offset: 2px; }

.lightbox-count {
  position: fixed;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
  left: 0;
  right: 0;
  margin: 0;
  text-align: center;
  font-family: var(--font-head);
  font-size: 13px;
  letter-spacing: 0.04em;
  color: #FBF9F4;
  opacity: 0.75;
}



/* The unlock card: all a visitor sees before the password. The page itself is
   in preview.enc, decrypted into #preview by /js/preview.js. */
.unlock {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding-inline: 16px;
  padding-block: 48px;
}
.unlock-card {
  width: min(100%, 23rem);
  display: grid;
  gap: 18px;
}
.unlock-card h1 { font-size: clamp(26px, 6vw, 32px); margin: 0; max-width: none; }
.unlock-card .lede { font-size: 17px; }
.unlock-form { display: grid; gap: 10px; }
.unlock-form label { font-weight: 600; font-size: 14px; color: var(--ink2); }
.unlock-row { display: flex; gap: 10px; }
.unlock-row input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 16px;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 10px 12px;
}
.unlock-row input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.unlock-row button {
  font: inherit;
  font-weight: 600;
  font-size: 16px;
  color: var(--on-accent);
  background: var(--accent);
  border: 0;
  border-radius: 10px;
  padding: 10px 18px;
  cursor: pointer;
}
.unlock-row button:disabled { opacity: 0.6; cursor: progress; }
.unlock-row button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.unlock-status { margin: 0; font-size: 14px; color: var(--ink2); min-height: 1.5em; }
.unlock-status.is-error { color: var(--accent); }


@media (max-width: 1000px) {
  .points-4, .screens-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .band, footer, .hero, .split { grid-template-columns: minmax(0, 1fr); }
  .hero { grid-template-rows: none; }
  .hero-shot { grid-column: auto; grid-row: auto; margin-top: 16px; }
  .split .screen { max-width: 480px; }
  .points-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  /* The facts stay one row on a phone: at most three short ones per page,
     which notes/validate.py holds to, in pills that scale with the screen
     from 12px down, padding in em so the whole row shrinks together. Every
     page was measured to fit at 320, 360, 375 and 390 wide. */
  .facts { gap: 0.4em; flex-wrap: nowrap; font-size: clamp(9.5px, 3.05vw, 12px); }
  .facts span { font-size: 1em; padding: 0.42em 0.85em; }
  .screens-2 { grid-template-columns: minmax(0, 1fr); }
  .screens-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .wrap { padding-top: 72px; }
  .points-2, .points-4 { grid-template-columns: minmax(0, 1fr); }
  .row { grid-template-columns: minmax(0, 1fr); }
  .phones-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
  .phones-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
  .phones-3 .phone .shot { border-radius: 16px; }
  .phones-3 .phone .title { font-size: 14px; }
  .phones-3 .phone .text { display: none; }
}
@media (max-width: 480px) {
  .screens-4 { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: no-preference) {
  .shot img { transition: transform 0.25s ease; }
  .phone .shot:hover img { transform: scale(1.012); }
}
