/* Front of house — lamplit maroon, editorial type, a feed you scroll.

   Shaped like a social app: one column of cards, faces along the top, a grid
   to explore, a tab bar under your thumb. The palette stays warm rather than
   the usual blue-white, and titles stay in serif, so it reads like something
   written rather than something posted.

   The colours are the icon's (site/icon.svg): an amber form lit against a
   maroon ground. The site was charcoal-and-blush for a while and the name has
   been AmberFeed since it moved to amberfeed.com, so the accent was the one
   colour the brand was named after and did not use. Now the tab, the home
   screen and the page agree.

   Blush did not leave, it moved: it is --warm, the mark on written pieces,
   where it used to be a gold that amber would now sit on top of. Three hues
   that stay apart — amber for the site's own marks, blush for written, blue
   for scenes — is the whole system. */

:root {
  --bg: #190d12;
  --surface: #241419;
  --surface-2: #2f1c22;
  --line: #3d2630;
  --line-soft: #2c1a21;
  --text: #f4ebe4;
  --dim: #b8a69c;
  --faint: #85706a;
  --accent: #f0a53c;         /* amber — labels, links, small marks */
  /* Resin, for solid fills. Darker than the icon's #C9641C, which carries
     white at 3.9:1 and is a button nobody with tired eyes can read; this
     clears 5.5:1 and is the same colour one step further from the lamp. */
  --accent-deep: #a94e13;
  --accent-soft: rgba(240, 165, 60, 0.13);
  --cool: #8fb3c9;           /* scene */
  --warm: #d98b8b;           /* written */
  --radius: 10px;
  --radius-lg: 16px;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --serif: ui-serif, Georgia, "Iowan Old Style", "Times New Roman", serif;
  --page: 1220px;
  --feed: 604px;
  --tabbar: 0px;
  /* Drawn over a photograph or a dark scrim — the same in either theme. */
  --on-media: #f6efeb;
  --on-media-dim: rgba(246, 239, 235, .72);
  --scrim: rgba(25, 13, 18, .55);
  --scrim-hi: rgba(25, 13, 18, .82);
  /* Behind a picture being shown for its own sake: a shade off the page, so
     the edges of a photograph are the photograph's and not the panel's. */
  --stage: #110a0d;
  color-scheme: dark;
}

/* Light mode is the same room with the lights on: the paper the icon is
   mounted on rather than the dark it glows in, down to the ground being the
   mockup's own #EDE6DF family and the ink its #2B1D22. Amber has to darken a
   long way to hold on paper — a lamp colour is not a text colour — so the
   accent here is nearer the resin than the flame.
   The theme is stamped onto <html> before first paint, so there is no flash
   of the wrong one — see the head of index.html. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f7f0e8;
  --surface: #fffcf8;
  --surface-2: #f0e7dc;
  --line: #e2d5c8;
  --line-soft: #ede4d9;
  --text: #2b1d22;
  --dim: #6b5850;
  --faint: #93807a;
  --accent: #9c4a0c;
  --accent-deep: #a9500f;
  --accent-soft: rgba(156, 74, 12, .10);
  --cool: #3c6b8a;
  --warm: #a8465c;
  --stage: #e8ded1;
}

* { box-sizing: border-box; }

html {
  /* iOS Safari inflates text in blocks it decides are too narrow, which widens
     the content past the viewport and is invisible in any desktop emulator —
     it is the reason a phone scrolled sideways while Chrome at the same size
     did not. Left at 100% the text is the size the CSS says it is. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0; background: var(--bg); color: var(--text);
  font: 15px/1.6 var(--sans); -webkit-font-smoothing: antialiased;
  /* The bar's own height plus whatever the home indicator takes, or the last
     card sits underneath it. */
  padding-bottom: calc(var(--tabbar) + env(safe-area-inset-bottom));
}
a { color: inherit; text-decoration: none; }
img { display: block; }
button { font: inherit; color: inherit; cursor: pointer; }
/* One focus ring for the whole site, in its own colour. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.wrap {
  max-width: var(--page); margin: 0 auto;
  /* `viewport-fit=cover` is set, so the page extends under the rounded corners
     and the notch. The gutter has to clear them. */
  padding-left: max(32px, env(safe-area-inset-left));
  padding-right: max(32px, env(safe-area-inset-right));
}
.wrap.narrow { max-width: calc(var(--feed) + 64px); }
/* Explore, Saved and a profile grid all sit over the same three-up column. */
.wrap.mid { max-width: 1004px; }

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

/* The page never scrolls sideways.
 *
 * Everything above is laid out so it does not need to, and the rules that let
 * the header shrink are the real fix — this is the guarantee, for the next
 * long word or wide table that gets added.
 *
 * `clip` rather than `hidden` on purpose: `hidden` makes these a scroll
 * container, which silently stops `position: sticky` working on the header
 * below. `clip` just clips.
 */
html, body { overflow-x: clip; max-width: 100%; }

.top {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line-soft);
}
.top-in {
  max-width: var(--page); margin: 0 auto; padding: 14px 32px;
  display: flex; align-items: center; gap: 28px;
  /* A flex item will not shrink below its content unless told it may, and the
     content here is an email address of unknown length. Without this the row
     pushes the whole page wider than the phone. */
  min-width: 0;
}
.top-in > * { min-width: 0; }
.wordmark { font-family: var(--serif); font-size: 23px; font-style: italic; letter-spacing: -.02em; }
.top-nav { display: flex; gap: 22px; }
.top-nav a {
  position: relative; display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint); padding-bottom: 3px; border-bottom: 1px solid transparent;
}
.top-nav a:hover { color: var(--text); }
.top-nav a.on { color: var(--accent); border-bottom-color: var(--accent); }
.pip {
  min-width: 17px; height: 17px; padding: 0 5px; border-radius: 999px;
  background: var(--accent-deep); color: #fff;
  font-size: 10px; font-weight: 700; letter-spacing: 0;
  display: inline-grid; place-items: center;
}
.pip[hidden] { display: none; }
.top-end { margin-left: auto; display: flex; align-items: center; gap: 14px; min-width: 0; }
.theme {
  display: grid; place-items: center; width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--line); border-radius: 50%; background: var(--surface);
  color: var(--dim);
}
.theme:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.theme svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
/* The switch shows where it takes you, not where you are: a sun on the dark
   page, a moon on the light one — which is what its label says too. */
.theme .moon { display: none; }
:root[data-theme="light"] .theme .moon { display: block; }
:root[data-theme="light"] .theme .sun { display: none; }

.top-right {
  font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--faint);
  display: flex; align-items: center; gap: 12px; min-width: 0;
}

/* Phone tab bar — the four places, thumb-height. Hidden on a wide screen,
   where the top nav does the same job. */
.tabbar { display: none; }

/* ------------------------------------------------------------ small pieces */

.kind {
  font-size: 10px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--faint);
}
.kind.k-scene { color: var(--cool); }
.kind.k-short { color: var(--accent); }
.kind.k-post  { color: var(--warm); }
.meta { font-size: 11.5px; color: var(--faint); }

.ico { width: 17px; height: 17px; flex: none; }

/* Somebody with no portrait yet: a quiet disc, not a hole in the layout. */
.noart { display: block; border-radius: 50%; background: linear-gradient(140deg, var(--surface-2), var(--line)); }
.turn-avatar .noart { width: 34px; height: 34px; }
.cast-chip .noart { width: 25px; height: 25px; }

.btn {
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text);
  border-radius: 999px; padding: 8px 18px; font-size: 13px; font-weight: 650;
  transition: background .15s, border-color .15s, color .15s;
}
.btn:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }
.btn.sm { padding: 5px 14px; font-size: 12px; }
.follow { background: var(--accent-deep); border-color: var(--accent-deep); color: #fff; }
.follow:hover { background: color-mix(in srgb, var(--accent-deep) 80%, #fff); border-color: transparent; }
/* Already following: the button steps back and becomes a way out, not a call. */
.follow.on { background: transparent; border-color: var(--line); color: var(--dim); }
.follow.on:hover { color: var(--accent); border-color: var(--accent); }

.btn.save { display: inline-flex; align-items: center; gap: 9px; padding: 8px 18px 8px 15px; }
.btn.save .ico { width: 16px; height: 16px; }
.btn.save.on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }

.act {
  display: inline-grid; place-items: center; width: 34px; height: 34px;
  border: 0; background: none; border-radius: 50%; color: var(--dim);
}
.act:hover { background: var(--surface-2); color: var(--text); }
.act.on { color: var(--accent); }
.act .ico { width: 19px; height: 19px; }

.tag { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--faint); }
.tag .ico { width: 15px; height: 15px; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 20px; }
.chip {
  padding: 6px 15px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); font-size: 12.5px; color: var(--dim);
}
.chip:hover { color: var(--text); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

.lede { padding: 30px 0 18px; }
.lede h1 { margin: 0 0 4px; font-family: var(--serif); font-size: 30px; font-weight: 400; letter-spacing: -.02em; }
.lede p { margin: 0; color: var(--faint); font-size: 13.5px; }

.note {
  margin: 22px 0 14px; font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint);
}
.row-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 14px; }
.row-head h2 { margin: 0; font-family: var(--serif); font-size: 19px; font-weight: 400; letter-spacing: -.01em; }
.row-head span { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--faint); }

.back { display: inline-block; margin: 24px 0 14px; color: var(--faint); font-size: 12.5px; }
.back:hover { color: var(--accent); }

.more { display: inline-block; margin-top: 10px; font-size: 12.5px; color: var(--accent); }
.more:hover { text-decoration: underline; text-underline-offset: 3px; }

.sentinel { height: 1px; }

/* ----------------------------------------------------------------- faces */

.av { position: relative; display: inline-block; padding: 2px; border-radius: 50%; background: var(--line); }
/* The ring carries the only two things worth seeing from across the page:
   a plain one is somebody you do not follow, a solid one somebody you do and
   are caught up with, and a lit one somebody who has posted since. */
.av.on { background: color-mix(in srgb, var(--accent-deep) 60%, var(--line)); }
.av.on.fresh { background: conic-gradient(from 210deg, var(--accent), var(--warm), var(--accent-deep), var(--accent)); }
.av-in {
  display: block; width: 38px; height: 38px; border-radius: 50%; overflow: hidden;
  background: var(--surface-2); border: 2px solid var(--bg);
}
.av-in img { width: 100%; height: 100%; object-fit: cover; }
.av-in .noart { width: 100%; height: 100%; }
.av.lg .av-in { width: 64px; height: 64px; }
.av.xl .av-in { width: 112px; height: 112px; border-width: 3px; }

/* ---------------------------------------------------------------- stories */

.stories {
  display: flex; gap: 18px; overflow-x: auto; padding: 22px 2px 20px;
  border-bottom: 1px solid var(--line-soft);
  scrollbar-width: none;
}
.stories::-webkit-scrollbar { display: none; }
.story { width: 78px; flex: none; text-align: center; }
.story-art { position: relative; display: inline-block; }
.av-add {
  position: absolute; right: -1px; bottom: 1px; width: 22px; height: 22px;
  border-radius: 50%; border: 2px solid var(--bg); background: var(--accent-deep);
  color: #fff; display: grid; place-items: center; padding: 0; line-height: 1;
}
.av-add::after { content: "+"; font-size: 14px; font-weight: 700; margin-top: -1px; }
.av-add:hover { background: color-mix(in srgb, var(--accent-deep) 75%, #fff); }
.av-add.on { background: var(--surface-2); border-color: var(--bg); color: var(--accent); }
.av-add.on::after { content: "✓"; font-size: 11px; }
.story-name {
  display: block; margin-top: 7px; font-size: 11.5px; color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.story-name:hover { color: var(--text); }

/* --------------------------------------------------------------- accounts */

.suggest { padding: 34px 0 10px; margin-top: 30px; border-top: 1px solid var(--line-soft); }
.accts { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); gap: 10px; }
.acct {
  display: flex; align-items: center; gap: 12px; padding: 11px 13px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  /* A grid item will not shrink below its content unless told it may, so a
     long display name made this wider than its own column. */
  min-width: 0;
}
.acct:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }
.acct-who { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1; }
.acct-text { min-width: 0; }
.acct-text b { display: block; font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-who:hover b { color: var(--accent); }
.acct-text .meta { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ------------------------------------------------------------------ cards */

.feed { display: flex; flex-direction: column; gap: 26px; padding-top: 24px; }
.card {
  max-width: var(--feed); width: 100%;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  overflow: hidden;
}
/* The whole card is a way into the piece, so it says so. Not the header: the
   name goes to the profile and the button follows them, and a hand over
   either of those would be promising the wrong thing. */
.card[data-piece] { cursor: pointer; }
.card[data-piece] .card-head { cursor: default; }
.card-head { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.card-who { display: flex; align-items: center; gap: 11px; min-width: 0; flex: 1; }
.card-who-text { min-width: 0; }
.card-who-text b { display: block; font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-who:hover b { color: var(--accent); }
.card-who-text .meta { display: block; }

.card-shot { display: block; background: var(--surface-2); }
.card-shot img, .car-cell img {
  /* 7:6 is 518px on the 604px column, which is the 520px a picture gets
     inside a piece -- the feed and the piece agree about how much room a
     photograph is worth, and the card is the shorter for it: five cards were
     four screens of scrolling when every one of them was a portrait.
     Pressing one is where the whole picture is, at the height of the panel. */
  width: 100%; aspect-ratio: 7 / 6; object-fit: cover;
  /* A portrait cropped to a box wider than it is tall keeps its middle band,
     and the face is not in the middle of a selfie -- it is above it. */
  object-position: center 32%;
  background: var(--surface-2);
}
.card-clip { display: block; width: 100%; max-height: 640px; background: #000; }

/* Several pictures, swiped. Scroll snapping does the work; the arrows are a
   convenience for a mouse, which cannot swipe. */
.car { position: relative; }
.car-track {
  display: grid; grid-auto-flow: column; grid-auto-columns: 100%;
  overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.car-track::-webkit-scrollbar { display: none; }
.car-cell { scroll-snap-align: center; display: block; }
/* Inside a piece, the same swipe shows the photograph whole. A card crops to
   4:5 so that a column of cards reads as a column; here there is no column to
   keep, and a crop would only be hiding the half somebody opened it to see.
   The cells still share a height — the tallest one's — so swiping does not
   make the page jump under the thumb. */
.car.shots .car-cell { display: grid; align-content: center; }
/* A single picture crosses the reading column on a tall screen; a swiped set
   keeps to it. Not for want of trying -- a scroll container loses its
   geometry when it is pulled about by a transform, and the track showed two
   cells at once for it. The set is still bounded by the same height, so on
   the screens anybody has this changes nothing; it is only the very tall
   window where one picture goes wider and three do not. */
/* Fit to whichever runs out first, the column or the screen, and keep the
   proportions — `object-fit: contain` would do the same arithmetic but paint
   the leftover as bars around the picture, which is a frame nobody asked for. */
.car.shots .car-cell img {
  display: block; margin-inline: auto;
  width: auto; max-width: 100%; height: auto; max-height: var(--shot-max, 93dvh);
  aspect-ratio: auto; object-fit: fill;
  border-radius: var(--radius); border: 1px solid var(--line);
}
/* On a card the dots sit on the photograph, which works because every card
   crops to the same shape. Here the pictures are whole and so each is a
   different height — dots pinned to the bottom of the track would land on the
   tall one and below the short one, and move as you swiped. Under the stage
   they stay put. */
.car.shots .car-dots { position: static; margin-top: 10px; }
/* Off the photograph they no longer need their own dark ground to be seen —
   and on the page's pale paper that ground is the only thing you notice. */
.car.shots .car-dots-in { background: none; backdrop-filter: none; padding: 0; gap: 7px; }
.car.shots .car-dots span { background: var(--line); }
.car.shots .car-dots span.on { background: var(--faint); }
/* Pale dots on a pale picture are no dots at all — and the whole job of these
   is to say there is more than one. So they sit on their own ground rather
   than on whatever the photograph happens to be, which for these is very often
   a bright kitchen or a white shirt. */
.car-dots {
  position: absolute; left: 0; right: 0; bottom: 11px;
  display: flex; justify-content: center; pointer-events: none;
}
.car-dots-in {
  display: flex; gap: 6px; align-items: center;
  padding: 5px 8px; border-radius: 999px;
  background: rgba(0, 0, 0, .42);
  backdrop-filter: blur(6px);
}
.car-dots span {
  width: 6px; height: 6px; border-radius: 50%;
  background: rgba(246, 239, 235, .5);
  transition: background .2s;
}
.car-dots span.on { background: var(--on-media); }
.car-btn {
  position: absolute; top: 50%; translate: 0 -50%;
  width: 30px; height: 30px; display: grid; place-items: center;
  border: 0; border-radius: 50%; padding: 0;
  background: var(--scrim); color: var(--on-media);
  opacity: 0; transition: opacity .2s;
}
.car:hover .car-btn { opacity: 1; }
.car-btn:hover { background: var(--scrim-hi); }
.car-btn.prev { left: 9px; }
.car-btn.next { right: 9px; }

/* A written piece with no picture: the words are the image. */
.card-words {
  position: relative; display: grid; align-items: center; min-height: 230px;
  padding: 34px 32px; overflow: hidden;
  background:
    radial-gradient(120% 90% at 12% 0%, rgba(217, 139, 139, .14), transparent 62%),
    linear-gradient(160deg, var(--surface-2), var(--surface));
}
.card-words p {
  position: relative; z-index: 1; margin: 0;
  font-family: var(--serif); font-size: 21px; line-height: 1.5; color: var(--text);
}
.card-words-mark {
  position: absolute; right: 14px; bottom: -42px;
  font-family: var(--serif); font-size: 150px; line-height: 1;
  color: color-mix(in srgb, var(--accent) 13%, transparent);
}
.card-words:hover p { color: var(--accent); }

/* A status said plainly. No panel, no serif, no mark — the card's own paper
   and the size the rest of the interface speaks at, because that is what it
   is: a line somebody typed, not a passage being exhibited. */
.card-jot {
  display: block; min-height: 0; padding: 14px 16px 2px;
  background: none;
}
.card-jot p { font-family: inherit; font-size: 16px; line-height: 1.56; }
.card-jot:hover p { color: var(--text); }

/* A scene with no picture: the recording itself, set out as the thing you
   came for. */
.listen {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 40px 26px 34px; text-align: center;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(217, 139, 139, .15), transparent 66%),
    linear-gradient(170deg, var(--surface-2), var(--surface));
}
.listen-mark {
  display: grid; place-items: center; width: 52px; height: 52px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent);
}
.listen-mark .ico { width: 25px; height: 25px; }
.listen-title { font-family: var(--serif); font-size: 21px; line-height: 1.3; letter-spacing: -.01em; }
.listen-title:hover { color: var(--accent); }
.listen audio { width: 100%; height: 40px; }

.card-acts { display: flex; align-items: center; justify-content: space-between; padding: 9px 16px 5px 10px; }
.card-acts-left { display: flex; align-items: center; gap: 14px; }
.card-audio { width: calc(100% - 28px); margin: 2px 14px 6px; height: 34px; }
.card-cap { padding: 4px 16px 18px; }
.card-cap p { margin: 0; font-size: 14.5px; line-height: 1.55; }
.card-cap b { font-weight: 650; }
.card-title:hover { color: var(--accent); }
.card-ex { margin-top: 7px !important; font-family: var(--serif); font-size: 15px; color: var(--dim); }
/* Left on its own — a status whose words are already the picture — the link
   has nothing to sit under, and the gap meant for clearing a paragraph reads
   as a gap for nothing. */
.card-cap .more:first-child { margin-top: 0; }

/* ------------------------------------------------------------------- grid */

.cells {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
  max-width: 940px; margin: 0 auto;
}
.cell {
  position: relative; display: block; aspect-ratio: 1 / 1; overflow: hidden;
  background: var(--surface-2); border-radius: 2px;
}
.cell img { width: 100%; height: 100%; object-fit: cover; object-position: center 30%; transition: transform .5s cubic-bezier(.2,.7,.3,1); }
.cell:hover img { transform: scale(1.04); }
.cell-words {
  display: flex; align-items: center; height: 100%; padding: 18px;
  background: linear-gradient(160deg, var(--surface-2), var(--surface));
}
.cell-words > span {
  display: -webkit-box; -webkit-line-clamp: 7; -webkit-box-orient: vertical; overflow: hidden;
  font-family: var(--serif); font-size: 14.5px; line-height: 1.45; color: var(--dim);
}
.cell.k-scene .cell-words, .cell.k-scene .cell-track { background: linear-gradient(155deg, rgba(143, 179, 201, .17), var(--surface) 72%); }
.cell.k-short .cell-words, .cell.k-short .cell-track { background: linear-gradient(155deg, rgba(217, 139, 139, .19), var(--surface) 72%); }
.cell.k-post .cell-words,  .cell.k-post .cell-track  { background: linear-gradient(155deg, rgba(217, 165, 92, .17), var(--surface) 72%); }
.cell:hover .cell-words > span { color: var(--text); }

/* A recording with no picture on it: a tile you can see is something to play. */
.cell-track {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 13px;
  height: 100%; padding: 20px; text-align: center;
  background: linear-gradient(155deg, var(--surface-2), var(--surface) 72%);
}
.cell-track-mark {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent); transition: transform .3s;
}
.cell-track-mark .ico { width: 21px; height: 21px; }
.cell:hover .cell-track-mark { transform: scale(1.09); }
.cell-track-title {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  font-family: var(--serif); font-size: 15.5px; line-height: 1.32; color: var(--dim);
}
.cell:hover .cell-track-title { color: var(--text); }
/* Unseen, marked on the piece itself. */
.cell-new {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  padding: 3px 9px; border-radius: 999px;
  background: var(--accent-deep); color: #fff;
  font-size: 9.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
}
.cell-mark { position: absolute; top: 8px; right: 8px; color: var(--on-media); opacity: .85; filter: drop-shadow(0 1px 3px rgba(0,0,0,.6)); }
.cell-over {
  position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: flex-end; gap: 3px;
  padding: 14px 13px; opacity: 0; transition: opacity .2s; color: var(--on-media);
  background: linear-gradient(to top, rgba(10, 8, 8, .88), rgba(10, 8, 8, .1) 60%, transparent);
}
.cell:hover .cell-over { opacity: 1; }
.cell-over b { font-family: var(--serif); font-size: 15px; font-weight: 400; line-height: 1.25; }
.cell-over span { font-size: 11px; color: var(--on-media-dim); }

/* ------------------------------------------------------------ the timeline */

.strip {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  margin: 32px 0 4px;
}
.strip h2 { margin: 0; font-family: var(--serif); font-size: 21px; font-weight: 400; letter-spacing: -.01em; }
.strip span { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--faint); }

/* Where this one sits in the run.
 *
 * A crop of the chronology rather than two cards saying Next and Before this:
 * when a piece happened relative to the others is part of reading these, in a
 * way it is not on a feed you scroll for pictures. Oldest at the top, so down
 * the page is forward in time and the next piece is below where the reading
 * just ended. The rail fades at whichever end is cropped, and the fade is the
 * control that opens the rest of it.
 */
.next-up {
  margin: 40px 0 8px; padding-top: 22px;
  border-top: 1px solid var(--line-soft);
}
.next-up h3 {
  margin: 0 0 10px; font-size: 10px; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--faint);
}

.tl { position: relative; margin: 0; padding: 0; list-style: none; }
/* Opened on a long run this would be most of a screen, so it is given a view
   to scroll inside. A run short enough to fit never scrolls, which is most of
   them; `contain` is so that reaching the end of one does not carry on into
   the panel behind it. */
.next-up.is-open .tl {
  max-height: min(60vh, 520px); overflow-y: auto; overscroll-behavior: contain;
}

/* One stop. The line is a segment per stop rather than one rule down the whole
   list, so hiding a stop takes its piece of the line with it and the crop
   closes up on its own. The two ends are trimmed back to their own dot. */
.tl-stop { position: relative; }
.tl-stop::before {
  content: ''; position: absolute; left: 6px; top: 0; bottom: 0;
  width: 1px; background: color-mix(in srgb, var(--faint) 45%, transparent);
}
.tl-stop.is-first::before { top: 17px; }
.tl-stop.is-last::before { bottom: calc(100% - 17px); }
.tl-edge.is-first::before { top: 12px; }
.tl-edge.is-last::before { bottom: calc(100% - 12px); }

.tl-item {
  position: relative; display: grid; gap: 1px 12px;
  grid-template-columns: auto 1fr;
  grid-template-areas: 'when what' 'when kind';
  padding: 7px 8px 7px 26px; border-radius: 9px;
  color: var(--text); text-decoration: none;
}
.tl-item::before {
  content: ''; position: absolute; left: 2px; top: 13px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--bg); border: 1px solid var(--faint); box-sizing: border-box;
}
a.tl-item:hover { background: var(--surface-2); }
a.tl-item:hover::before { border-color: var(--accent); }

.tl-when {
  grid-area: when; min-width: 46px; padding-top: 1px;
  font-size: 11px; letter-spacing: .04em; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
/* Two lines and then stop. A status has no headline, so what stands in for
   one is its opening words, and those run on as far as you let them. */
.tl-what {
  grid-area: what; font-size: 14px; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.tl-kind {
  grid-area: kind; font-size: 10px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--faint);
}

/* Where the reader is. A filled dot, the serif the pieces themselves are set
   in, and it says so — the one stop on the rail that is not a way somewhere. */
.tl-now { cursor: default; background: var(--surface-2); }
.tl-now::before {
  background: var(--accent); border-color: var(--accent);
  width: 9px; height: 9px; left: 1px; top: 12px;
}
.tl-now .tl-what { font-family: var(--serif); font-size: 15px; }
.tl-now .tl-kind { color: var(--accent); }

/* The ends of the run itself. Said in the quiet colour the interface uses for
   a thing that is only telling you something, and plainly not pressable. */
.tl-edge .tl-item { padding-top: 3px; padding-bottom: 3px; cursor: default; }
.tl-edge .tl-what {
  font-size: 11.5px; line-height: 1.4; letter-spacing: .02em; color: var(--faint);
}
.tl-edge .tl-item::before {
  width: 5px; height: 5px; left: 3px; top: 9px;
  background: var(--line); border-color: var(--line);
}

/* The crop, and the way out of it. The rail carries on past the last stop
   shown and goes to nothing, which is the whole of the claim: there is more
   this way, and this much of it. */
.tl-cap {
  position: relative; display: flex; align-items: center;
  width: 100%; min-height: 30px; padding: 0 0 0 26px;
  border: 0; background: none; cursor: pointer; font: inherit;
  font-size: 10px; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--faint); text-align: left;
}
/* Both of these set a display of their own, which beats the browser's rule
   for a hidden element — without saying so they stay on screen after the crop
   they belong to has been opened. */
.tl-cap[hidden], .tl-less[hidden] { display: none; }
.tl-cap::before { content: ''; position: absolute; left: 6px; top: 0; bottom: 0; width: 1px; }
.tl-cap-top::before {
  background: linear-gradient(to top, color-mix(in srgb, var(--faint) 45%, transparent), transparent);
}
.tl-cap-end::before {
  background: linear-gradient(to bottom, color-mix(in srgb, var(--faint) 45%, transparent), transparent);
}
.tl-cap:hover { color: var(--accent); }

.tl-less {
  display: block; margin: 10px 0 0 26px; padding: 0;
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 10px; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--faint);
}
.tl-less:hover { color: var(--accent); }

.next-all {
  display: inline-block; margin-top: 14px;
  font-size: 12px; letter-spacing: .04em; color: var(--dim); text-decoration: none;
}
.next-all:hover { color: var(--accent); }

/* The day a run of pieces belongs to, with a rule running out to the margin. */
.day { display: flex; align-items: center; gap: 14px; margin: 26px 0 13px; }
.day span {
  font-size: 11px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint); white-space: nowrap;
}
.day::after { content: ""; flex: 1; height: 1px; background: var(--line-soft); }

/* Smaller than the Explore squares: a day's run should read as a cluster of
   pieces, not as three billboards. */
.gcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); gap: 20px 16px; }
.gcard { display: block; }
.gcard .cell { border-radius: var(--radius); border: 1px solid var(--line-soft); }
.gcard:hover .cell { border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }
.gcard-foot { display: block; padding-top: 10px; }
.gcard-foot b {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-family: var(--serif); font-size: 15px; font-weight: 400; line-height: 1.28; letter-spacing: -.005em;
}
.gcard:hover .gcard-foot b { color: var(--accent); }
.gcard-foot .meta { display: block; margin-top: 5px; }
/* A taste of the piece, clamped: enough to choose by. */
.gcard-ex {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  margin-top: 4px; font-size: 12.5px; line-height: 1.45; color: var(--faint);
}
.gcard:hover .gcard-ex { color: var(--dim); }

/* The head of the timeline: everything before this, rolled up. Shut, it is a
   dashed bar — a gap in the record, with the arrow pointing back up it. Open,
   it becomes the solid top of the page and the years unroll underneath. */
.earlier { margin: 20px 0 0; }
.earlier > summary {
  display: flex; align-items: center; gap: 11px;
  padding: 13px 18px; border-radius: var(--radius);
  border: 1px dashed var(--line); background: var(--surface);
  font-size: 12.5px; letter-spacing: .04em; color: var(--dim);
  cursor: pointer; list-style: none;
}
.earlier > summary::-webkit-details-marker { display: none; }
.earlier > summary:hover { color: var(--text); border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }
.earlier-arrow { display: grid; place-items: center; color: var(--faint); transition: transform .25s; }
.earlier-arrow .ico { width: 16px; height: 16px; }
.earlier > summary:hover .earlier-arrow { color: var(--accent); }
.earlier[open] .earlier-arrow { transform: rotate(180deg); }
.earlier[open] > summary { border-style: solid; }
/* The controls that reach further back, at the top of the block because that
   is where you are standing when you press them. */
.earlier-more { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 18px 0 2px; }
.plain { border: 0; background: none; padding: 0; color: var(--accent); font-size: 12.5px; }
.plain:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Where the story actually starts. */
.earlier-start { display: flex; align-items: center; gap: 12px; padding: 18px 0 0; }
.earlier-start::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--accent-deep); flex: none; }
.earlier-start span {
  font-size: 10.5px; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: var(--faint);
}
.earlier-start::after { content: ""; flex: 1; height: 1px; background: var(--line-soft); }

/* Once open it is simply the earliest stretch of the timeline. */
.earlier-body { position: relative; padding-left: 18px; margin-left: 8px; border-left: 1px solid var(--line-soft); }
.earlier-body .day { margin-top: 20px; }
.earlier[open] { margin-bottom: 4px; }

/* ---------------------------------------------------------------- profile */

.p-head {
  display: grid; grid-template-columns: auto 1fr; gap: 12px 34px;
  padding-bottom: 22px; margin-bottom: 4px; border-bottom: 1px solid var(--line-soft);
}
/* `start`, not the stretch a grid item takes by default: the column beside
   this one is a name, a bio and a cast, so the face's cell is that tall and
   the face was too. Nothing showed it until the picture count arrived — it is
   positioned against this box, and `bottom: 2px` put it level with the cast
   chips, a hundred pixels under the photograph it counts. The focus ring was
   a tall ellipse for the same reason. */
.p-face { grid-row: span 1; align-self: start; }
.p-main { min-width: 0; }
.p-title { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.p-head h1 { margin: 0; font-family: var(--serif); font-size: 31px; line-height: 1.12; font-weight: 400; letter-spacing: -.02em; }
.p-handle { margin-top: 5px; font-size: 12.5px; color: var(--faint); letter-spacing: .02em; }
.p-stats { display: flex; gap: 20px; margin: 15px 0 13px; font-size: 13.5px; color: var(--dim); }
.p-stats b { color: var(--text); font-weight: 650; }
.p-bio { margin: 0; color: var(--dim); font-size: 14.5px; max-width: 62ch; }
.cast { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 15px; }
.cast-chip {
  display: flex; align-items: center; gap: 8px; padding: 5px 13px 5px 5px;
  border-radius: 999px; background: var(--surface); border: 1px solid var(--line);
}
.cast-chip img { width: 25px; height: 25px; border-radius: 50%; object-fit: cover; }
.cast-chip b { font-size: 12.5px; font-weight: 600; }
.cast-chip span { font-size: 11px; color: var(--faint); }

/* Who this person is with. Each one is a link to that person's own feed, so it
   reads as a chip you can follow rather than a line of text about them. */
.p-relations { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 2px; }
.rel-chip {
  display: flex; align-items: center; gap: 8px; padding: 4px 13px 4px 4px;
  border-radius: 999px; background: var(--surface); border: 1px solid var(--line);
  transition: border-color .15s, background .15s;
}
.rel-chip:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }
.rel-chip img, .rel-chip .noart { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; }
.rel-chip span { font-size: 12px; color: var(--dim); }
.rel-chip b { font-weight: 650; color: var(--text); }
.rel-chip i { font-style: normal; color: var(--faint); }


/* -------------------------------------------------------------------- post */

.post { max-width: var(--feed); margin: 0 auto; }
.post-head {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; margin-bottom: 26px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.post-title { margin-bottom: 24px; }
.post-title h1 { margin: 11px 0 13px; font-family: var(--serif); font-size: 36px; line-height: 1.12; font-weight: 400; letter-spacing: -.025em; }
.standfirst { margin: 0; color: var(--dim); font-size: 17px; line-height: 1.55; }
.post-kicker { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.post-meta { font-size: 11.5px; color: var(--faint); }
.post-kicker .post-meta::before { content: "·"; margin-right: 9px; color: var(--line); }

.clip {
  display: block; width: auto; max-width: 100%; max-height: 440px; margin-bottom: 22px;
  border-radius: var(--radius); border: 1px solid var(--line); background: #000;
}
.player { margin: 0 0 34px; padding: 17px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); }
.player audio { width: 100%; height: 38px; }
.quiet {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px;
  border-radius: 999px; background: var(--surface-2); color: var(--faint); font-size: 11.5px;
}

/* A picture inside a piece is as tall as the view will let it be: scroll it
   into the middle and it is very nearly the whole screen.

   It used to be capped at 520px and cropped to fill the column's width, which
   on a portrait is a letterboxed middle of it — a crop, inside the piece
   somebody opened to see the thing uncropped. Now it is whole, and the only
   limit on it is the view it has to fit in. */
.shot { margin: 28px 0; }
/* Pulled to the middle of whatever contains it and back by half its own
   width, so it can be wider than that container without either of them
   having to know the other's gutter.

   `.shot > img`, and the child combinator is the whole of it: a swiped set
   is `figure.shot > .car > .car-track > .car-cell > img`, so written as a
   descendant this shifted every cell's picture half its own width to the
   left and the track showed two at once. */
.shot > img {
  display: block; margin-left: 50%; transform: translateX(-50%);
  width: auto; max-width: var(--shot-wide, 100%); height: auto; max-height: var(--shot-max, 93dvh);
  aspect-ratio: auto; object-fit: fill;
  border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--line);
}
.shot figcaption { margin-top: 8px; color: var(--faint); font-size: 12px; line-height: 1.5; }

.prose p { font-family: var(--serif); font-size: 17.5px; line-height: 1.66; margin: 0 0 1.05em; }
.turn { display: flex; gap: 13px; margin-bottom: 20px; }
.turn-avatar { width: 34px; flex: none; }
.turn-avatar img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; }
.turn-who { font-size: 12px; font-weight: 700; color: var(--accent); letter-spacing: .02em; margin-bottom: 3px; }
.turn-text { font-family: var(--serif); font-size: 17px; line-height: 1.6; white-space: pre-wrap; }
.turn.cont .turn-who { display: none; }

/* The transcript sits under the recording, folded away. */
.transcript { margin-top: 8px; }
.transcript > summary {
  display: inline-flex; align-items: center; gap: 8px; width: fit-content;
  padding: 7px 16px; margin-bottom: 20px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--dim);
  cursor: pointer; list-style: none;
}
.transcript > summary::-webkit-details-marker { display: none; }
.transcript > summary::after { content: "▾"; font-size: 10px; color: var(--faint); }
.transcript[open] > summary::after { content: "▴"; }
.transcript > summary:hover { color: var(--text); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }

.post-acts {
  display: flex; align-items: center; gap: 14px;
  margin-top: 34px; padding-top: 16px; border-top: 1px solid var(--line-soft);
}
.post-acts .more { margin-top: 0; }

/* --------------------------------------------------- a piece, side by side */

/* A window with room for two columns gets them: the picture in one, at the
   full height of the panel, and the reading in the other. The threshold is
   also written in app.js — see WIDE — because the panel's width and its
   contents are decided in different places and must not disagree.

   The reason is height. The pictures on this site are portraits; a portrait
   in a 604px reading column is a stamp with a third of the panel blank on
   either side of it, and the piece it belongs to scrolled off the bottom. */
@media (min-width: 1100px) {
  /* `.modal.modal-wide`, not `.modal-wide`: the panel's own width and height
     are set on `.modal` further down this sheet, and at equal specificity the
     later rule wins. */
  .modal.modal-wide {
    /* Not as wide as the screen will allow, but wide enough that the reading
       is a column and not a gutter. The picture is sized by the height of the
       panel and nothing else — a portrait on a laptop is about 520px across
       whatever the window is doing — so the width is really a question about
       the words: enough for a line of them, plus what the picture takes, and
       what is left over after that is mat. */
    width: min(1280px, 94vw);
    /* A committed height, not "as tall as the piece needs". Sized to its
       contents, a short piece would shrink the panel and take the photograph
       down with it — the stage is only ever as large as the panel allows. */
    height: min(92vh, 1000px); max-height: min(92vh, 1000px);
  }
  /* The halves carry their own padding: the stage runs to the edge of the
     panel and the reading column scrolls on its own. */
  .modal-wide .modal-body { padding: 0; overflow: hidden; }
  .modal-wide .modal-body .post { max-width: none; }

  .post-split {
    display: grid; grid-template-columns: minmax(0, 1fr) clamp(420px, 42%, 560px);
    height: 100%; max-width: none; margin: 0;
  }

  .post-read {
    min-width: 0; padding: 30px 36px 44px;
    overflow-y: auto; overscroll-behavior: contain;
    border-left: 1px solid var(--line-soft);
  }
  /* A shade narrower than a page's column, and the headline set to match. */
  .post-read .post-title h1 { font-size: 30px; margin: 10px 0 12px; }
  .post-read .standfirst { font-size: 16px; }
  .post-read .post-acts { margin-top: 26px; }
  .post-read .player { margin-bottom: 24px; }
}

/* The stage runs the height of the panel, edge to edge. No padding and no row
   for the caption: both are height, and height is the entire point of putting
   the picture in a column of its own. What a picture has to say is written
   over the foot of it instead, the way it is on a card. */
.post-stage {
  position: relative; display: grid; overflow: hidden;
  min-width: 0; min-height: 0;
  background: var(--stage);
}

/* A frame the shape of what is in it — see `stage()` in app.js, which reads
   that shape off the picture. As tall as the stage, as wide as that shape
   makes it, and never wider than the column.

   So the picture is always the full height of the panel: a small one is
   brought up to it rather than sitting in the middle of the ground, and a
   tall one is never the 604px of a reading column again. The one shape that
   cannot have it is the far end of wide — a landscape photograph whose width
   at full height would leave nothing to read beside it. That one is clamped
   to the column and shown whole, short of the full height, rather than
   cropped to fake it. */
.stage-frame {
  position: relative; align-self: stretch; justify-self: center;
  aspect-ratio: var(--shot-ar, 4 / 5);
  max-width: 100%; min-width: 0; min-height: 0;
}
/* Absolute against the frame, and then sized in full percent of it — both
   halves matter. Absolute, because every height above this one is stretched
   or a percentage of something stretched, and a percentage of those resolves
   to nothing at all: that was a photograph at its own size, hanging out of
   the bottom of the panel. And `100%` rather than `auto`, because a replaced
   element is the one thing insets do not size — `width: auto` on a picture
   means its own width, whatever `left` and `right` say, which was the same
   photograph overflowing the same panel for the opposite reason. */
.post-stage .stage-frame > * {
  position: absolute; inset: 0; width: 100%; height: 100%;
  aspect-ratio: auto; background: none; border-radius: 0;
}
/* Whole, always. In a frame of its own shape that fills it exactly; in the
   one case where the frame had to be clamped — a landscape photograph too
   wide to be full height beside the reading — it letterboxes rather than
   crops, because pressing a picture is a promise to show all of it. */
.post-stage .stage-frame > img { object-fit: contain; object-position: center; cursor: zoom-in; }
/* A clip is never cropped: a face cut off the side of a video is not
   something the album can offer to show whole. */
.post-stage .stage-frame > video { object-fit: contain; background: #000; }

/* Over the foot of the picture, on a gradient, clear of the dots. */
.stage-cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  padding: 40px 22px 25px; text-align: center; pointer-events: none;
  color: var(--on-media); font-size: 12px; line-height: 1.5;
  background: linear-gradient(to top, var(--scrim-hi), transparent);
}

/* Several pictures on the stage: the same swipe as everywhere else, sized to
   the column instead of to the text. Each cell is the height of the stage and
   holds a frame of its own, so swiping from a portrait to a landscape moves
   nothing but the picture.

   The carousel is laid into the stage by its insets, which makes its height a
   real one for everything inside it to be measured against. */
.car.stage { position: absolute; inset: 0; min-width: 0; min-height: 0; }
.car.stage .car-track { height: 100%; }
.car.stage .car-cell { display: grid; min-width: 0; min-height: 0; }
/* Off the bottom edge, where the caption is not. */
.car.stage .car-dots { bottom: 9px; }

/* ------------------------------------------------------------------- modal */

body.locked { overflow: hidden; }

.scrim {
  position: fixed; inset: 0; z-index: 60;
  background: color-mix(in srgb, #000 66%, transparent);
  backdrop-filter: blur(5px);
  opacity: 0; transition: opacity .3s;
}
.scrim.on { opacity: 1; }
/* The album can be opened from on top of a piece, so it has a layer of its
   own above the panel — a scrim that went underneath would leave the piece
   lit at full brightness beside the picture it opened. */
.scrim.over { z-index: 62; }

/* Laid out at its final size; the script transforms it back onto the card it
   grew from, so transform-origin has to be the middle of it. */
.modal {
  position: fixed; z-index: 61; left: 50%; top: 50%; translate: -50% -50%;
  width: min(1000px, 94vw);
  /* As tall as the piece needs, never taller than the window. */
  height: auto; max-height: min(90vh, 1080px);
  display: flex; flex-direction: column;
  background: var(--bg); border: 1px solid var(--line); border-radius: 20px;
  box-shadow: 0 40px 90px rgba(0, 0, 0, .55);
  overflow: hidden; transform-origin: 50% 50%; will-change: transform;
}
.modal-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex: none;
  padding: 12px 14px 12px 22px; border-bottom: 1px solid var(--line-soft);
}
/* The trailing space clears the close that floats over it, so the last line
   of a piece is not read from under a button. */
.modal-body { flex: 1; overflow-y: auto; padding: 30px 34px 76px; overscroll-behavior: contain; }
/* How wide a picture inside a piece may be, which is not the same question as
   how wide the writing should be. A photograph is not text and is not held to
   the text column: on a tall screen that column was the only thing stopping
   it -- 61% of a view with room for all of it -- while the height cap it was
   supposedly bounded by never came near binding. So the limit is the view
   instead, and on the common screen nothing moves at all, because there the
   height is still what runs out first. */
:root { --shot-wide: min(92vw, 1100px); }
/* In a panel the view is the panel: the picture may cross the reading column
   but not the body that scrolls it. Same sum as the panel's own width, less
   its padding. */
.modal-body { --shot-wide: min(932px, calc(94vw - 68px)); }

/* Inside a panel the view is the panel, not the window: a picture taller than
   the body can never be seen whole, however far it is scrolled. The panel is
   90vh or 1080px, whichever is less, and its bar comes off that -- so this is
   that same sum, kept a couple of pixels shy of it rather than a wide margin,
   since every pixel held back here is one the photograph does not get. */
.modal-body { --shot-max: min(calc(90dvh - 62px), 1016px); }
.modal-body .post { max-width: var(--feed); }
/* While its piece is being fetched, the card it came from says so. */
.is-opening { opacity: .55; transition: opacity .15s; }

/* The close that comes to you, once you are far enough in to need it. Inside
   the panel rather than the window, so a pull down takes it with the rest. */
.modal-away {
  /* Above the reveal handles, which sit on the photographs and scroll with
     them: the two meet whenever a picture's bottom corner passes the panel's,
     and the one that must stay reachable is the way out. */
  position: absolute; z-index: 3;
  right: 14px; bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  display: grid; place-items: center; width: 46px; height: 46px; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  backdrop-filter: blur(8px);
  color: var(--text);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .28);
  opacity: 0; translate: 0 10px; pointer-events: none;
  transition: opacity .18s ease, translate .18s ease;
}
.modal.is-deep .modal-away { opacity: 1; translate: 0; pointer-events: auto; }
.modal-away .ico { width: 20px; height: 20px; }

/* As the one below arrives, the one in the bar steps back. It keeps its space
   rather than collapsing: the row would otherwise re-lay itself mid-scroll,
   and the thing that moved would be the button beside it, which nobody asked
   to move. */
.modal-shut { transition: opacity .18s ease, scale .18s ease; }
.modal.is-deep .modal-shut { opacity: 0; scale: .8; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .modal-away, .modal-shut { transition: none; }
}

.modal-shut {
  display: grid; place-items: center; width: 34px; height: 34px; padding: 0; flex: none;
  border: 1px solid var(--line); border-radius: 50%; background: var(--surface); color: var(--dim);
}
.modal-shut:hover { color: var(--text); border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }

/* Icon only. The label it used to carry is on `title`, because the row now has
   a byline in it and a byline is worth more than the words "Open as a page". */
.modal-out {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 50%; color: var(--faint);
  transition: color .15s, background .15s;
}
.modal-out:hover { color: var(--accent); background: var(--surface-2); }
.modal-out .ico { width: 15px; height: 15px; }

.modal-acts { display: flex; align-items: center; gap: 4px; flex: none; }

/* The byline, sitting in the bar instead of over the headline. Tighter than
   the card version: no panel of its own, since the bar already is one. */
.modal-bar.has-who { padding: 10px 14px 10px 16px; gap: 12px; }
.modal-who {
  display: flex; align-items: center; gap: 12px;
  min-width: 0; flex: 1;
}
.modal-who .av-in { width: 34px; height: 34px; }
.modal-who .card-who-text b { font-size: 14.5px; }

/* ------------------------------------------------------------------ album */

/* The profile picture, once it has something behind it. */
.p-face.openable { position: relative; cursor: pointer; border-radius: 50%; }
.p-face.openable .av-in { transition: filter .15s; }
.p-face.openable:hover .av-in { filter: brightness(1.08); }
.p-face.openable:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.p-face-count {
  position: absolute; right: 2px; bottom: 2px;
  min-width: 22px; height: 22px; padding: 0 6px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--accent-deep); color: #fff;
  font-size: 11px; font-weight: 700; border: 2px solid var(--bg);
}

.album {
  position: fixed; z-index: 63; left: 50%; top: 50%; translate: -50% -50%;
  /* Roomier than it was, because it is now what "full size" means: a picture
     on a piece's stage already runs the whole height of the panel, and an
     album that showed it smaller than the thing it was opened from would be
     a step backwards dressed up as a step in. It has to clear that height
     with its own bar and caption still in the column. */
  width: min(1200px, 94vw); max-height: 96vh;
  display: flex; flex-direction: column; gap: 10px;
}
.album-bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.album-count { font-size: 12px; letter-spacing: .04em; color: var(--faint); }

/* The picture sets the height; the arrows sit over it rather than beside it,
   so a tall portrait and a wide frame both stay centred. */
.album-stage { position: relative; display: flex; align-items: center; justify-content: center; min-height: 0; }

/* All of them in a row that scrolls, so a thumb can drag the picture and get
   the platform's own momentum, rubber-band and half-swipe-falls-back. Rolling
   that by hand from touch events gets the first 80% and never the rest. */
.album-rail {
  display: flex; width: 100%;
  /* A stage that holds its shape. Without it the rail is as tall as whichever
     picture is in it, so paging from a portrait to a landscape resizes the
     whole panel under the thumb — and a picture whose file does not arrive
     collapses the lightbox to a sliver with nothing to grab. The slides
     stretch to it, so every one of them snaps to the same box. */
  min-height: min(60vh, 520px);
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  /* So a swipe past the last picture does not become the browser's back
     gesture, which would close the page rather than bounce. */
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.album-rail::-webkit-scrollbar { display: none; }
.album-slide {
  flex: 0 0 100%; min-width: 0;
  display: flex; align-items: center; justify-content: center;
  scroll-snap-align: center; scroll-snap-stop: always;
}
.album-shot {
  max-width: 100%; max-height: 86vh; border-radius: 14px;
  object-fit: contain; background: var(--surface-2);
  border: 1px solid var(--line);
  /* A long press offers to save it; a drag across it must not pick it up
     instead of turning the page. */
  -webkit-user-drag: none; user-select: none;
}
.album-step {
  position: absolute; top: 50%; translate: 0 -50%;
  width: 42px; height: 42px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  border: 1px solid var(--line); color: var(--text);
  backdrop-filter: blur(6px); transition: background .15s, border-color .15s;
}
.album-step:hover { background: var(--surface); border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }
.album-step .ico { width: 18px; height: 18px; }
/* `prev`/`fwd`, not `back`/`on`: both of those are taken elsewhere in this
   sheet — `.back` is the back-link and carries a 24px top margin, which
   landed the left arrow a visible notch below the right one. */
.album-step.prev { left: 10px; }
.album-step.fwd { right: 10px; }

.album-cap { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; min-height: 18px; }
.album-words { font-size: 13px; color: var(--dim); }
.album-from { font-size: 12px; color: var(--faint); }
.album-from:hover { color: var(--accent); }

@media (max-width: 760px) {
  .album { width: 100vw; max-height: 100dvh; padding: 0 10px; }
  .album-shot { max-height: 66vh; }
  /* The arrows are a pointer's way through this. A thumb has the rail, and
     two discs sitting on top of the picture are in the way of it — one of
     them exactly where the left edge is grabbed from. */
  .album-step { display: none; }
}

@media (max-width: 760px) {
  .modal { width: 100vw; height: 100dvh; max-height: 100dvh; border-radius: 0; border: 0; }
  /* Full screen already, so the sum is simply the screen less the bar. */
  .modal-body { padding: 24px 18px 40px; --shot-max: calc(100dvh - 56px); }
  /* Narrow: the handle and the relation are the first thing to go, not the
     name and not the way out. */
  .modal-bar.has-who { padding: 8px 8px 8px 12px; }
  .modal-who .card-who-text .meta { display: none; }
  .modal-who .av-in { width: 30px; height: 30px; }
}

/* ------------------------------------------------------------------ states */

.empty { padding: 60px 0; text-align: center; color: var(--faint); }
.empty h2 { margin: 0 0 7px; font-family: var(--serif); font-size: 21px; font-weight: 400; color: var(--text); }
.empty p { margin: 0 0 4px; font-size: 14px; }

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

@media (max-width: 900px) {
  .p-head { grid-template-columns: auto 1fr; gap: 10px 22px; }
  .av.xl .av-in { width: 86px; height: 86px; }
  .p-head h1 { font-size: 25px; }
  .post-title h1 { font-size: 28px; }
}

@media (max-width: 760px) {
  :root { --tabbar: 62px; }
  .wrap, .top-in {
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
  }
  .top-nav { display: none; }
  .top-right { font-size: 10px; }
  /* Truncated to "x9mw75c96b@priv…" it tells you nothing, and it is what
     pushes the theme button off the edge. Sign out says who you are well
     enough on a phone. */
  .reader-email { display: none; }

  .tabbar {
    /* Lifted by however much Safari's toolbar is currently covering.
       `bottom: 0` is the bottom of the LAYOUT viewport, which the toolbar sits
       on top of when it expands on a scroll up — so the bar was there, just
       underneath it. `--vv-bottom` is measured from the visual viewport in
       app.js and is 0 wherever there is nothing in the way. */
    position: fixed; left: 0; right: 0; bottom: var(--vv-bottom, 0px); z-index: 40;
    /* No transition here on purpose. Safari animates its own toolbar and the
       measurement follows it frame by frame; a transition on top of that lags
       behind and reads as the bar jumping about. */
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    /* The inset clears the home indicator, but only when the bar is actually
       sitting on it. Once it has been lifted over Safari's toolbar the lift
       has already cleared it, and adding both leaves the bar floating with
       content showing underneath. */
    padding-bottom: max(0px, calc(env(safe-area-inset-bottom) - var(--vv-bottom, 0px)));
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--line);
  }
  .tabbar a {
    position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 9px 0 8px; color: var(--faint);
  }
  .tabbar a.on { color: var(--accent); }
  .tabbar svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
  .tabbar span { font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; }
  .tabbar .pip { position: absolute; top: 4px; left: 54%; min-width: 15px; height: 15px; font-size: 9px; }

  /* Edge to edge, the way a phone feed runs. */
  /* Inside a piece too, and for the same reason it matters more here: on a
     phone a portrait is stopped by the width of the column long before it is
     stopped by the height of the screen, so the gutter is the only thing
     between it and another eighty pixels. It cannot be the whole screen —
     a 2:3 picture 390px wide is 569px tall and the screen is 844 — but this
     is the most of one there is without cropping it, which is the one thing
     a piece is where you go to avoid.

     `50%` then back by half its own width, rather than negative margins: the
     page's gutter and the panel's are different numbers, and this needs to
     know neither. */
  :root, .modal-body { --shot-wide: 100vw; }
  .shot > img, .car.shots {
    border-radius: 0; border-left: 0; border-right: 0;
  }
  /* Margin arithmetic rather than the transform above, and for a reason: this
     one is the scroll container itself. Under a transform it becomes the
     containing block for its own snap geometry and the track stops paging by
     a full cell -- two photographs side by side, each half out of the window.
     `50% - 50vw` needs no gutter either, and moves nothing after layout. */
  .car.shots { width: 100vw; margin-left: calc(50% - 50vw); }
  .car.shots .car-cell img { border-radius: 0; border-left: 0; border-right: 0; }
  .shot figcaption, .car.shots + figcaption { padding-inline: 16px; }

  .feed { gap: 18px; }
  .card { border-left: 0; border-right: 0; border-radius: 0; margin-left: -16px; margin-right: -16px; width: auto; max-width: none; }
  .cells { gap: 2px; margin-left: -16px; margin-right: -16px; }
  .cell { border-radius: 0; }
  .cell-over { opacity: 1; background: linear-gradient(to top, rgba(10, 8, 8, .8), transparent 55%); }
  .cell-over b { font-size: 12px; }
  .cell-over span { display: none; }

  .accts { grid-template-columns: 1fr; }
  .gcards { grid-template-columns: repeat(2, 1fr); gap: 16px 10px; }
  .earlier-body { padding-left: 11px; margin-left: 4px; }
  .strip h2 { font-size: 18px; }
  .story { width: 68px; }
  .av.lg .av-in { width: 58px; height: 58px; }
  .lede h1 { font-size: 24px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; scroll-behavior: auto !important; }
}

/* ------------------------------------------------------------------ the door */

/* The shell is served to anyone, so it is briefly behind the gate: hide it
   rather than let the feed flash up under the sign-in card. */
body.gated > .top,
body.gated > main,
body.gated > .tabbar { display: none; }

.gate {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--bg);
}

.gate-card {
  width: 100%;
  max-width: 372px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 30px 28px 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.gate-card .wordmark { margin: 0; font-size: 30px; line-height: 1; }
.gate-note { margin: -6px 0 4px; color: var(--dim); font-size: 14px; }

.gate-field { display: flex; flex-direction: column; gap: 5px; }
/* `display` on the element beats the user agent's [hidden] rule, so the invite
   field would stay on screen when it is switched off. */
.gate-field[hidden] { display: none; }
.gate-field > span {
  font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint);
}
.gate-field input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 15px;   /* 16px or more, or iOS zooms the page on focus */
}
.gate-field input:focus { outline: none; border-color: var(--accent); }

.gate-go {
  padding: 11px 14px;
  border: 0;
  border-radius: var(--radius);
  background: var(--accent-deep);
  color: #fff;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.gate-go:disabled { opacity: .6; cursor: default; }

.gate-swap {
  border: 0; background: none; padding: 0;
  color: var(--accent); font: inherit; font-size: 13px; cursor: pointer;
  text-align: center;
}

/* Empty until something goes wrong, and it must not shift the card when it
   fills — a form that jumps as you are reading the reason is its own problem. */
.gate-error { margin: 0; min-height: 1.2em; color: #e88; font-size: 13px; }

.reader-email {
  max-width: min(180px, 30vw); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* The slot it sits in is an uppercase label; an email address is not one. */
  text-transform: none;
}
.sign-out {
  margin-left: 10px; padding: 0;
  border: 0; background: none;
  color: var(--accent); font: inherit; font-size: inherit; cursor: pointer;
}
.sign-out:hover { text-decoration: underline; }

/* --------------------------------------------------------------- reviewing */

/* The same site, reading work nobody has approved yet.
   None of this exists on a deployed site: the switch is only built where the
   server says the preview is available, and without it nothing below matches.

   The rule for the whole section is that it sits *beside* the reading rather
   than inside it. A candidate must still look like the post it is going to be —
   that is the entire point of reviewing here instead of in a queue — so the
   marks are a strip above the picture and a line down the edge, never a tint
   over the card or a change to the type. */

.reviewer {
  appearance: none; -webkit-appearance: none;
  background: transparent; color: var(--dim);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 5px 11px; font: inherit; font-size: 12px; letter-spacing: .02em;
  cursor: pointer; white-space: nowrap;
}
.reviewer:hover { color: var(--text); border-color: var(--faint); }
.reviewer[aria-pressed="true"] {
  color: var(--on-media); background: var(--accent-deep); border-color: var(--accent-deep);
}

/* The strip on a card: what this piece is, and a way to go and change it.
   Wraps rather than squeezes — on a narrow column the facts drop to a second
   line and the edit link stays reachable, which is the one thing here that is
   pointless if it becomes three pixels wide. */
.rev {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
  padding: 7px 14px;
  border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
  font-size: 11.5px; letter-spacing: .02em; color: var(--faint);
}

.rev-state { font-weight: 600; color: var(--dim); }
.rev-when { font-variant-numeric: tabular-nums; }
.rev-week, .rev-level {
  padding: 1px 6px; border: 1px solid var(--line); border-radius: 999px;
}
/* The one thing here that is a job to do rather than a fact. */
.rev-todo { color: var(--warm); }
.rev-edit { margin-left: auto; color: var(--accent); text-decoration: none; font-weight: 600; }
.rev-edit:hover { text-decoration: underline; }

/* Which of the three it is, said once down the edge of the card. */
.card-candidate { border-left: 3px solid var(--warm); }
.card-scheduled { border-left: 3px solid var(--cool); }
.card-candidate .rev-state { color: var(--warm); }
.card-scheduled .rev-state { color: var(--cool); }

/* ------------------------------------------------------- the review columns */

.wrap.wide { max-width: var(--page); }

/* Two feeds in parallel, which is what a couple is. Past two they scroll
   sideways rather than narrowing: a card below about 320px stops being a
   readable post and starts being a thumbnail with text on it. */
.rev-cols {
  display: grid;
  grid-template-columns: repeat(var(--cols, 2), minmax(320px, 1fr));
  gap: 26px;
  align-items: start;
  padding-bottom: 40px;
}
.rev-col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.rev-col .card { max-width: none; }

/* The name stays put while its column scrolls, so it is always clear whose
   post you are reading half way down a season. */
.rev-col-head {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 2px; background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.rev-col-who { display: flex; align-items: center; gap: 9px; text-decoration: none; color: var(--text); min-width: 0; }
.rev-col-who b { font-size: 14.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rev-col-who:hover b { color: var(--accent); }
.rev-col-n { margin-left: auto; color: var(--faint); font-size: 12px; white-space: nowrap; }
.rev-col-empty { padding: 20px 2px; }

/* A week boundary. A rule rather than a heading: it is punctuation between
   posts, and a heading here would compete with the posts for attention. */
.rev-rule {
  display: flex; align-items: center; gap: 10px;
  margin: 10px 0 2px; color: var(--faint); font-size: 11.5px; letter-spacing: .04em;
  text-transform: uppercase;
}
.rev-rule::after {
  content: ''; flex: 1; height: 1px; background: var(--line); order: 3;
}
.rev-rule-n { font-weight: 600; color: var(--dim); }
.rev-rule-c { order: 2; text-transform: none; letter-spacing: 0; }

@media (max-width: 760px) {
  /* One column on a phone. Two 320px columns do not fit, and a season read one
     feed at a time is still the season. */
  .rev-cols { grid-template-columns: minmax(0, 1fr); }
}

/* Which feeds are being read. All of them until you say otherwise — picking two
   is how a couple's own fortnight gets the whole width. */
.rev-chips {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 4px 0 20px;
}
.rev-chips-lbl {
  font-size: 11.5px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--faint); margin-right: 2px;
}
.rev-chip {
  display: inline-flex; align-items: center; gap: 7px;
  appearance: none; -webkit-appearance: none;
  background: var(--surface); color: var(--dim);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 12px; font: inherit; font-size: 12.5px; cursor: pointer;
}
.rev-chip:hover { color: var(--text); border-color: var(--faint); }
.rev-chip.on {
  color: var(--on-media); background: var(--accent-deep); border-color: var(--accent-deep);
}
.rev-chip-n { font-size: 11px; opacity: .7; font-variant-numeric: tabular-nums; }
.rev-chip.clear { border-style: dashed; }
/* On the piece itself the strip is inside the article rather than spanning a
   card, so it needs its own breathing room. */
.post > .rev { border-radius: var(--radius); margin: 0 0 14px; }

/* A slot the day has not filled yet. Only ever drawn while reviewing — a reader
   never sees one, because it is not a post until it has been written. */
.card-waiting { border-left: 3px dashed var(--faint); }
.waiting { padding: 18px 16px 20px; }
.waiting-lede {
  margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--faint);
}
.waiting-about { margin: 0 0 10px; font-size: 15px; line-height: 1.45; color: var(--text); }
.waiting-note { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--faint); }

/* The controls above the columns: which feeds, and how they sit together. */
.rev-controls {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px 20px; padding-bottom: 4px;
}
.rev-layout { display: flex; gap: 6px; padding-bottom: 20px; }

/* One timeline, everybody in it.
   A card already says whose it is, so all this adds is a mark down the side
   where the voice changes — which is what makes an exchange look like an
   exchange instead of a list. */
/* One column at the width of the page is a card a thousand pixels wide with a
   photograph to match — legible as a layout and not as a feed. Merged, it is a
   feed, so it takes a feed's width and sits in the middle of the room. */
.rev-merged { max-width: calc(var(--feed) + 40px); margin: 0 auto; width: 100%; }
.rev-merged .new-voice { position: relative; }
.rev-merged .new-voice::before {
  content: ''; position: absolute; left: -13px; top: 18px; bottom: 18px;
  width: 2px; border-radius: 2px; background: var(--accent-soft);
}
/* Run-on posts by the same person sit in slightly, so a pair of them reads as
   one turn rather than two. */
.rev-merged .same-voice { margin-left: 22px; }

@media (max-width: 760px) {
  .rev-merged .same-voice { margin-left: 12px; }
  .rev-merged .new-voice::before { left: -8px; }
}

/* ------------------------------------------- what a picture has covered */
/*
   Where a picture is covered is decided once and written down as a fraction
   of the photograph; this is only how it is drawn, which is why it can change
   without a stored box moving. The boxes are percentages of their frame, so a
   card, a stage, a lightbox and a phone turned sideways are the same four
   numbers.
*/
.has-cover { position: relative; }
.cover {
  position: absolute; z-index: 1; pointer-events: none;
  border-radius: 14px;
  /* Softened at the edge rather than cut: a hard rectangle on a photograph
     reads as a censor's bar, and the thing it is covering is then the first
     thing anybody looks at. */
  -webkit-mask-image: radial-gradient(ellipse at center, #000 58%, transparent 100%);
  mask-image: radial-gradient(ellipse at center, #000 58%, transparent 100%);
}
/* `--cover-blur` is set from the region's own rendered width, so the same
   photograph is covered by the same amount on a card, in a piece and filling
   a phone. The fallback is for the frame before it has been measured. */
.cover { --cover-blur: 24px; --cover-grid: 10px; }
.cover.is-blur { backdrop-filter: blur(var(--cover-blur)) saturate(.85) contrast(.92); }
.cover.is-pixelate {
  /* There is no pixelate in CSS, so: the detail blurred away entirely and a
     grid laid over what is left, which at a reading distance is the same
     answer and needs no second file. The grid is drawn from the region's own
     size, so it is blocks on a card and blocks in a lightbox rather than a
     fine mesh in one and a coarse one in the other. */
  backdrop-filter: blur(var(--cover-blur)) saturate(.8);
  background-image:
    repeating-linear-gradient(0deg,
      rgba(0, 0, 0, .16) 0 1px, transparent 1px var(--cover-grid)),
    repeating-linear-gradient(90deg,
      rgba(0, 0, 0, .16) 0 1px, transparent 1px var(--cover-grid)),
    repeating-linear-gradient(0deg,
      rgba(255, 255, 255, .10) 0 1px, transparent 1px var(--cover-grid));
  background-position: 0 0, 0 0, 0 1px;
}
.cover.is-solid {
  backdrop-filter: blur(calc(var(--cover-blur) * 1.6)) brightness(.72) saturate(.5);
  mask-image: none; -webkit-mask-image: none;
  border-radius: 10px;
}

/* A photograph nobody has looked at yet, covered whole.

   Not one of the styles above and deliberately not drawn like them: those
   cover a region somebody can point at, and this covers everything because
   nothing is known. No soft edge either -- the fade exists so a cover does not
   read as a censor's bar over the thing it sits on, and there is nothing for
   this one to sit on. It is the whole frame, so it ends where the frame does.

   Short-lived: the queue looks at a new picture within a turn or two, and the
   cover then shrinks to whatever was actually found. */
.cover.is-whole {
  mask-image: none; -webkit-mask-image: none;
  border-radius: 0;
  backdrop-filter: blur(var(--cover-blur)) saturate(.6) brightness(.92);
}

/* The handle on the curtain.

   On the photograph rather than in the corner of the window: a piece can hold
   several pictures and each is its own decision, and the window's own corner
   already has the close button in it. Placed by measurement, like the covers
   -- a figure is a photograph plus its caption, and the corner of the one is
   not the corner of the other. */
.cover-reveal {
  position: absolute; z-index: 2; right: 10px; bottom: 10px;
  display: grid; place-items: center; width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: color-mix(in srgb, var(--surface) 80%, transparent);
  backdrop-filter: blur(8px);
  color: var(--text); cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .3);
  transition: opacity .15s ease, color .15s ease;
}
.cover-reveal .ico { width: 19px; height: 19px; }
/* Once the photograph is showing, the way back stays there but stops
   competing with the thing it is sitting on. */
.cover-reveal.is-shown { opacity: .4; }
.cover-reveal.is-shown:hover, .cover-reveal:focus-visible { opacity: 1; }

@media (prefers-reduced-motion: reduce) { .cover-reveal { transition: none; } }

/* Laid over the photograph itself, placed by measurement — the box around a
   picture is not always the picture. Hidden until it has been measured, so a
   cover never appears anywhere but where it belongs, even for a frame. */
.cover-layer {
  position: absolute; inset: 0; visibility: hidden; pointer-events: none; z-index: 1;
  /* The element's rectangle is what crops the photograph, so it is what crops
     the covers: a card showing a band out of the middle of a portrait must not
     paint a cover for the part it is not showing. */
  overflow: hidden;
}
