/* The look of Are.na, made small for an app: Arial, regular weight, hierarchy by size and grey; white (or black) with
   navy buttons, 3px corners, their spacing (2 5 10 15 20 25 35 45 65). Each person keeps their colour as a name badge.
   Follows the system's light or dark. */
/* Arrows (→ ← ↔ ⇒, which the editor writes for -> and the rest) are drawn by the system's own typeface, as Notion
   draws them: a short arrow with an open head. Arial's is a long hairline with a speck for a head. (≠ ≤ ≥ stay
   Arial's: the system's ≠ comes out as an = with a stroke beside it.)
   A typeface can't be named for a few characters only, and the system's can't be named in a font file's source at all,
   so it's said the other way round: "Den Text" is Arial for every character but those, and those fall through to the
   next family, the system's. All four cuts are named, or bold and italic would be faked from the regular. Where Arial
   isn't on the device, everything is the system's face, which is what such a device showed before. */
@font-face { font-family: "Den Text"; font-weight: 400; font-style: normal; src: local("Arial"), local("ArialMT"); unicode-range: U+0000-218F, U+2200-10FFFF; }
@font-face { font-family: "Den Text"; font-weight: 700; font-style: normal; src: local("Arial Bold"), local("Arial-BoldMT"); unicode-range: U+0000-218F, U+2200-10FFFF; }
@font-face { font-family: "Den Text"; font-weight: 400; font-style: italic; src: local("Arial Italic"), local("Arial-ItalicMT"); unicode-range: U+0000-218F, U+2200-10FFFF; }
@font-face { font-family: "Den Text"; font-weight: 700; font-style: italic; src: local("Arial Bold Italic"), local("Arial-BoldItalicMT"); unicode-range: U+0000-218F, U+2200-10FFFF; }
:root {
  /* Colours: every one is light-dark(light, dark), so whichever color-scheme an element has picks its half: the
     system's everywhere, or the one a person chose (data-mode, set before paint by pages.ts THEME), dark inside the film
     at /about, the chat's inside a panel (show.ts). This block is the getden theme (getden's own app's palette: warm
     greys, charcoal, periwinkle), and it's what emails and panels use (look.ts). Classic, below, is the same tokens in
     white and pitch black. Nothing else names a colour; a picture's own ground (--media) and what's laid on it
     (--on-media) are the same everywhere. */
  color-scheme: light dark;
  --bg: light-dark(#fff, #1c1c1f); --fg: light-dark(#282a30, #e6e7eb); --fg2: light-dark(#6c6a63, #9a9ba3);
  --fg3: light-dark(#918e87, #7c7d85); --fg4: light-dark(#bdbab4, #4c4d53);
  --g1: light-dark(#f2f0ed, #2a2a2d); --g2: light-dark(#ebe9e6, #353538); --g3: light-dark(#dddbd8, #404043); --line: light-dark(#e0e1e5, #2d2d33);
  --brand: #6f79d9; --on-brand: #fff; --focus: light-dark(#6771ca, #8088d1); --mark: light-dark(#fdf4dd, #443c24); --link: light-dark(#2161dc, #6f97e6);
  --alert: light-dark(#e07425, #f59e0b); --green: light-dark(#3a903a, #5fb85f); --red: light-dark(#d93636, #c65d5d);
  --tip-bg: light-dark(#282a30, #e6e7eb); --tip-fg: light-dark(#fff, #1c1c1f);
  --shade: light-dark(rgba(0, 0, 0, .1), rgba(0, 0, 0, .45)); --shadow: 0 6px 24px var(--shade); --scrim: light-dark(rgba(153, 151, 148, .42), rgba(18, 20, 22, .7));
  --media: #000; --on-media: #fff;
  --hair: .5px;
  --font: "Den Text", system-ui, -apple-system, "Segoe UI", Roboto, Arial, Helvetica, sans-serif;
  --xs: .78125rem; --sm: .9rem; --md: 1rem; --lg: 1.2rem; --xl: 1.5rem;
  --max: 580px; --gutter: 15px; --edge: max(var(--gutter), env(safe-area-inset-left, 0px));
  --c: var(--g3);
  /* Corners: Are.na's square-ish boxes with the faintest squircle (continuous corners where the browser draws them). */
  --r: 4px;
}
:root[data-theme="classic"] {
  --bg: light-dark(#fff, #000); --fg: light-dark(rgba(0, 0, 0, .85), rgba(255, 255, 255, .85)); --fg2: light-dark(rgba(0, 0, 0, .6), rgba(255, 255, 255, .6));
  --fg3: light-dark(#999, #696969); --fg4: light-dark(#b2b2b2, #4f4f4f);
  --g1: light-dark(#f7f7f7, #1a1a1a); --g2: light-dark(#ededed, #222); --g3: light-dark(#dedede, #333); --line: light-dark(#e5e5e5, #262626);
  --brand: light-dark(#00075f, #3d46c2); --focus: light-dark(#3d46c2, #5e6dee); --mark: light-dark(#fff3cf, #3b3115); --link: #1d9bf0;
  --alert: light-dark(#e15100, #ff7a33); --green: light-dark(#238020, #4caf50); --red: light-dark(#e3164c, #ff285f);
  --tip-bg: light-dark(#333, #e5e5e5); --tip-fg: light-dark(#fff, #000);
  --shade: light-dark(rgba(0, 0, 0, .12), rgba(0, 0, 0, .6)); --scrim: light-dark(rgba(0, 0, 0, .28), rgba(0, 0, 0, .6));
}
/* Light or dark whatever the system says, when a person has chosen one (the foot of their page). */
:root[data-mode="light"] { color-scheme: light; }
:root[data-mode="dark"] { color-scheme: dark; }
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
html, body { background: var(--bg); color: var(--fg); font: 400 var(--md)/1.45 var(--font); margin: 0; -webkit-text-size-adjust: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; margin: 0; cursor: pointer; }
h1, h2 { font-weight: 400; }
.w { width: 100%; max-width: var(--max); margin: 0 auto; padding-left: max(var(--gutter), env(safe-area-inset-left)); padding-right: max(var(--gutter), env(safe-area-inset-right)); }
.mute { color: var(--fg2); }
.small { font-size: var(--xs); color: var(--fg3); }
.note { color: var(--alert); font-size: var(--sm); margin: 10px 0 0; }
.note.ok { color: var(--fg2); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--r); }
input:focus-visible, textarea:focus-visible { outline: none; }
::selection { background: color-mix(in srgb, var(--focus) 25%, transparent); }

/* Icons: one stroke weight, the text's colour. */
.i { width: 16px; height: 16px; flex: none; display: block; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }

/* The one arrow, after the words of anything that takes you somewhere. */
.i.after { display: inline-block; width: 14px; height: 14px; margin-left: 5px; vertical-align: -2px; }

/* Toasts (public/toast.js): small boxes dropped in at the top of any page. */
#toasts { position: fixed; z-index: 40; top: calc(10px + env(safe-area-inset-top, 0px)); left: var(--gutter); right: var(--gutter); display: grid; justify-items: center; gap: 5px; pointer-events: none; }
.toast { pointer-events: auto; display: flex; align-items: center; gap: 10px; max-width: min(100%, calc(var(--max) - 2 * var(--gutter))); min-height: 38px; padding: 0 12px; background: var(--bg); color: var(--fg); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--shadow); font-size: var(--sm); animation: toast-in .18s cubic-bezier(.22, 1, .36, 1) both; }
.toast:has(.ib) { padding-right: 4px; }
.toast a { color: var(--focus); white-space: nowrap; }
.toast a:hover { text-decoration: underline; text-underline-offset: 3px; }
@keyframes toast-in { from { opacity: 0; translate: 0 -10px; } }
@media (prefers-reduced-motion: reduce) { .toast { animation-name: fade; } }

/* Tooltips: a word or two in a small dark box, after a moment, above whatever has data-tip. Fingers don't hover. */
[data-tip] { position: relative; }
@media (hover: hover) {
  [data-tip]:hover::after, [data-tip]:focus-visible::after {
    content: attr(data-tip); position: absolute; z-index: 20; bottom: calc(100% + 6px); left: 50%; translate: -50% 0;
    background: var(--tip-bg); color: var(--tip-fg); font-size: var(--xs); line-height: 1.35; padding: 4px 7px; border-radius: var(--r);
    white-space: pre-line; width: max-content; max-width: 260px; pointer-events: none; animation: tip .15s .35s both;
  }
  [data-tip-end]:is(:hover, :focus-visible)::after { left: auto; right: 0; translate: 0 0; }
  [data-tip-start]:is(:hover, :focus-visible)::after { left: 0; translate: 0 0; }
  /* Along the top of the page there's no room above, and a tip there was cut off by the window's edge: it opens below. */
  :is(.top, .doc-top) [data-tip]:is(:hover, :focus-visible)::after { bottom: auto; top: calc(100% + 6px); }
  /* An empty tip is no tip, and an open menu is already saying what its button does. */
  [data-tip=""]::after, [aria-expanded="true"]::after { display: none !important; }
}
@keyframes tip { from { opacity: 0; } }

/* Buttons, as Are.na's: navy and full width for the one thing to do, grey for the other. */
.btn { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 45px; padding: 0 20px; border-radius: var(--r);
  background: var(--brand); color: var(--on-brand); font-size: var(--sm); text-align: center; transition: opacity .15s; }
.btn:hover { opacity: .92; }
.btn.secondary { background: var(--g1); color: var(--fg); }
.btn.secondary:hover { background: var(--g2); opacity: 1; }
.btn[aria-pressed="true"] { background: var(--g1); color: var(--fg); }
.btn[disabled] { opacity: .55; cursor: default; }
/* Busy: the button that was pressed turns into the wait, a plain ring where its label was. */
form[aria-busy] .btn[type="submit"], .btn[aria-busy] { position: relative; color: transparent; opacity: 1; pointer-events: none; }
form[aria-busy] .btn[type="submit"] > *, .btn[aria-busy] > * { visibility: hidden; }
form[aria-busy] .btn[type="submit"]::after, .btn[aria-busy]::after { content: ""; position: absolute; inset: 0; margin: auto; width: 18px; height: 18px; border: 2px solid color-mix(in srgb, var(--on-brand) 35%, transparent); border-top-color: var(--on-brand); border-radius: 50%; animation: spin .8s linear infinite; }
.btn.secondary[aria-busy]::after, form[aria-busy] .btn.secondary[type="submit"]::after { border-color: var(--g3); border-top-color: var(--fg2); }
.link { color: var(--fg2); text-decoration: underline; text-decoration-color: var(--g3); text-underline-offset: 3px; }
.link:hover { color: var(--fg); text-decoration-color: currentColor; }
code { font-family: ui-monospace, Menlo, monospace; font-size: .85em; color: var(--fg); word-break: break-all; cursor: copy; }

/* Fields: a grey box, 3px corners, a blue ring when focused. */
.box { display: flex; align-items: stretch; min-height: 45px; background: var(--g1); border: 1px solid var(--g1); border-radius: var(--r); transition: border-color .15s, background .15s; }
.box:focus-within { background: var(--bg); border-color: var(--focus); }
.box input { flex: 1; min-width: 0; padding: 0 12px; border: 0; background: transparent; color: var(--fg); font: inherit; font-size: var(--md); outline: 0; border-radius: var(--r); -webkit-appearance: none; appearance: none; }
.box input::placeholder { color: var(--fg3); }
.box[aria-invalid="true"] { border-color: var(--alert); }

/* The top bar: the brand mark and what the page picks between (All, You, groups), you on the right. */
.top { display: flex; align-items: center; gap: 2px; padding-top: max(8px, env(safe-area-inset-top)); padding-bottom: 6px; font-size: var(--sm); }
.top .brand { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; padding: 6px 0; color: var(--fg2); }
.top .brand svg { width: 15px; height: 15px; flex: none; }
.top .tabs { display: flex; gap: 2px; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.top .tabs::-webkit-scrollbar { display: none; }
.top .tabs a { flex: none; padding: 6px 10px; border-radius: var(--r); color: var(--fg2); white-space: nowrap; }
.top .tabs a:hover { color: var(--fg); background: var(--g1); }
.top .tabs a[aria-current="page"] { color: var(--fg); background: var(--g1); }
.top .right { margin-left: auto; flex: none; padding: 6px 0 6px 10px; color: var(--fg2); white-space: nowrap; }
.top .right:hover, .top .right[aria-current="page"] { color: var(--fg); }
/* Connect now, until Claude or ChatGPT is connected. */
#connect-banner { flex: none; padding-top: 2px; padding-bottom: 5px; }
.connect-banner { position: relative; display: flex; align-items: center; gap: 8px; min-height: 40px; padding-left: 10px; background: var(--g1); border-radius: var(--r); font-size: var(--sm); }
.connect-banner:hover { background: var(--g2); }
/* The whole bar is Connect now; the Claude logo and the x sit above it, each doing its own thing. */
.connect-banner .go::after { content: ""; position: absolute; inset: 0; }
.connect-banner svg:not(.i) { width: 16px; height: 16px; flex: none; }
.connect-banner .add { position: relative; z-index: 1; display: flex; align-items: center; align-self: stretch; padding: 0 6px; margin: 0 -6px 0 -4px; }
.connect-banner .ib { position: relative; z-index: 1; margin-left: auto; margin-right: 5px; }
.connect-banner .ib:hover { background: var(--g3); }
/* Not now: quiet words at the line's end, above the link that covers the rest of it. */
.connect-banner .later { position: relative; z-index: 1; margin-left: auto; align-self: stretch; padding: 0 12px; color: var(--fg3); }
.connect-banner .later:hover { color: var(--fg); }

/* Pages that scroll: invite, profile, getting started. */
div.page { padding-top: 25px; padding-bottom: 65px; }
div.page h1 { font-size: var(--lg); line-height: 1.35; margin: 0 0 20px; }
div.page h2 { font-size: var(--md); margin: 35px 0 10px; }
div.page p { margin: 0 0 10px; }
div.page .step { margin: 20px 0; display: grid; gap: 10px; }
div.page blockquote { margin: 0 0 15px; padding: 12px 15px; background: var(--g1); border-radius: var(--r); white-space: pre-wrap; overflow-wrap: anywhere; }
/* An invite (pages.ts invitePage): one sentence in grey with what you're joining in black and its picture small
   beside it, then what they said as a message from them; the one thing to do sits at the foot, by the thumb. */
/* The film at /about (pages.ts aboutPage, public/intro.js): the whole page, always black, whatever the theme. Top to
   bottom: the picture, his words a line at a time (all in one place, the one being said showing; a word is grey until
   he says it), Close. "fade" runs the picture off into black behind the words; "frame" keeps it in a box above them.
   One typeface, white and grey: what moves is the words arriving and the emoji landing. */
html:has(.intro) { overflow: hidden; }
.intro { position: fixed; inset: 0; z-index: 30; color-scheme: dark; background: var(--media); color: var(--on-media); overflow: hidden; transition: opacity .6s ease; }
.intro[data-done] { opacity: 0; pointer-events: none; }
.intro-in { position: relative; height: 100%; max-width: var(--max); margin: 0 auto; display: flex; flex-direction: column;
  padding: max(15px, env(safe-area-inset-top)) max(var(--gutter), env(safe-area-inset-right)) max(15px, env(safe-area-inset-bottom)) max(var(--gutter), env(safe-area-inset-left)); }
.intro-film { position: relative; flex: 1 1 0; min-height: 120px; cursor: pointer; }
.intro-film video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 25%; display: block; background: var(--media); }
.intro[data-look="fade"] .intro-film { margin: calc(-1 * max(15px, env(safe-area-inset-top))) calc(-1 * var(--gutter)) -70px; }
.intro[data-look="fade"] .intro-film::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(to bottom, transparent 52%, var(--media) 90%); }
.intro[data-look="frame"] .intro-film { border-radius: 8px; overflow: hidden; }   /* near the buttons' corners: sharp, as everything here is */
.intro[data-look="frame"] .intro-words { margin-top: 25px; }
.intro-sound { position: absolute; z-index: 1; right: 10px; bottom: 10px; display: flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 10px; border-radius: var(--r); background: color-mix(in srgb, var(--media) 60%, transparent); color: var(--on-media); font-size: var(--sm); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.intro[data-look="fade"] .intro-sound { right: var(--gutter); bottom: auto; top: max(15px, env(safe-area-inset-top)); }
.intro-words { position: relative; z-index: 1; display: grid; min-height: 2.4em; margin: 0 0 25px; font-size: clamp(1.1rem, 4.8vw, 1.35rem); line-height: 1.25; letter-spacing: -.01em; pointer-events: none; }
/* A line arrives a word at a time, each coming into focus out of a blur as it's said; the line before it fades away. */
.intro-words .line { grid-area: 1 / 1; align-self: end; margin: 0; opacity: 0; transition: opacity .25s ease; }
.intro[data-look="frame"] .intro-words .line { align-self: start; }
.intro-words .line[data-on] { opacity: 1; }
.intro-words .line [data-at] { display: inline-block; opacity: 0; filter: blur(8px); -webkit-filter: blur(8px); transition: opacity .4s ease, filter .4s ease, -webkit-filter .4s ease; will-change: opacity, filter; }
.intro-words .line [data-at][data-on] { opacity: 1; filter: none; -webkit-filter: none; }
.intro-words .line.welcome { filter: blur(8px); -webkit-filter: blur(8px); transition: opacity .5s ease, filter .5s ease, -webkit-filter .5s ease; }
.intro-words .line.welcome[data-on] { filter: none; -webkit-filter: none; }
.intro-words .line .nb { white-space: nowrap; }
.intro-foot { position: relative; z-index: 1; flex: none; display: flex; gap: 10px; }
.intro-back { flex: none; display: flex; align-items: center; justify-content: center; min-height: 45px; padding: 0 22px; border-radius: var(--r); color: var(--fg2); }
.intro-back:hover { color: var(--on-media); background: var(--g1); }
.intro-skip { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 45px; border-radius: var(--r); background: var(--g1); color: var(--on-media); }
.intro-skip:hover { background: var(--g2); }
.intro-credit { position: relative; z-index: 1; margin: 10px 0 0; font-size: .625rem; line-height: 1.3; color: var(--fg4); text-align: center; }
@media (prefers-reduced-motion: reduce) {
  .intro-words .line [data-at], .intro-words .line.welcome { filter: none; -webkit-filter: none; transition: none; }
}

.login main:has(> .invited) { padding-top: max(5vh, 32px); padding-bottom: max(20px, env(safe-area-inset-bottom)); }
.invited .says-who { margin: 0; font-size: var(--xl); line-height: 1.35; color: var(--fg3); }
.invited .says-who b { font-weight: 400; color: var(--fg); }
.invited .says-who .nb { white-space: nowrap; }
/* The button that opens the assistant ends in a chevron, so it reads as the way on. */
.invited .btn[data-ask] .i { width: 16px; height: 16px; flex: none; margin-right: -4px; }
.invited .pic { display: inline-block; width: 24px; height: 24px; margin-right: 2px; vertical-align: -3px; border-radius: var(--r); object-fit: cover; background: var(--g1); }
.invited .msg { margin-top: 25px; }
/* A photo picked for them: their face-to-be, plain, with who picked it under it. */
.invited .picked { margin: 25px 0 0; }
.invited .picked img { display: block; width: 96px; height: 96px; object-fit: cover; border-radius: var(--r); background: var(--g1); }
.invited .picked figcaption { margin-top: 8px; font-size: var(--sm); color: var(--fg2); }
.invited [data-photo] input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.invited [data-photo] label.btn { cursor: pointer; }
.invited .msg + .msg { margin-top: 12px; }
.invited .bub { display: inline-block; max-width: 100%; margin: 0; padding: 10px 14px; background: var(--g2); border-radius: 18px 18px 18px 4px; font-size: var(--lg); line-height: 1.3; white-space: pre-wrap; overflow-wrap: anywhere; }
/* Answer first: your answer sits under the question, on the right, as a message you're about to send. */
.invited .msg.mine { text-align: right; }
.invited .msg.mine .by { justify-content: flex-end; min-height: 0; margin: 0 0 4px; }
.invited .msg.mine .bub { border-radius: 18px 18px 4px 18px; text-align: left; }
.invited .box.answer textarea { flex: 1; min-width: 0; display: block; margin: 0; padding: 11px 12px; border: 0; background: transparent; color: var(--fg); font: inherit; font-size: var(--md); line-height: 1.4; outline: 0; resize: none; field-sizing: content; min-height: 4.5em; max-height: 40vh; }
.invited .box.answer textarea::placeholder { color: var(--fg3); }
.invited .bub .veiled { filter: blur(8px); user-select: none; pointer-events: none; }
.invited .foot { margin-top: 30px; display: grid; gap: 10px; }
/* On a phone what to do next sits at the foot of the screen, nearest the thumb, the message above it. */
@media (max-width: 700px) {
  .login main:has(> .invited) { display: flex; flex-direction: column; min-height: 100dvh; }
  .invited { flex: 1; display: flex; flex-direction: column; }
  .invited .foot { margin-top: auto; padding-top: 30px; }
}
.invited .foot p { margin: 0; }
.invited .foot .small { margin: 5px 0 0; }
.invited .tagline { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 5px; font-size: var(--xs); color: var(--fg2); }
.invited .tagline svg { width: 15px; height: 15px; flex: none; }
.invited .connect { display: grid; gap: 10px; margin: 0; }
.connect:not(:has([data-app])) .until-connected { display: none; }
/* Something you can say: the arrow, then the sentence. */
.says { display: grid; gap: 6px; margin-bottom: 10px; }
.say { display: flex; align-items: flex-start; gap: 10px; padding: 11px 14px; border-radius: var(--r); background: var(--g1); font-size: var(--sm); line-height: 1.4; color: var(--fg); }
.say:hover { background: var(--g2); }
.say .i { margin-top: 2px; color: var(--fg3); }
div.page .try { color: var(--fg2); margin: 5px 0; }
div.page p.done { margin-top: 35px; }
div.page p.done.centered { text-align: center; }

/* Sign-in: one question, its field, one navy button; a narrow column a third of the way down. */
.login main { width: 100%; max-width: var(--max); margin: 0 auto; padding: max(14vh, 45px) var(--gutter) 45px; }
.login h1 { font-size: var(--lg); line-height: 1.35; margin: 0 0 5px; }
.login .lede { color: var(--fg2); font-size: var(--sm); margin: 0 0 20px; }
.login form { display: grid; gap: 10px; }
/* On a phone the sign-in page is what Den is at the top and the number at the foot, under your thumb; the small print
   under the button. With the keyboard up the page is shorter (dvh), so the field rides just above it. */
@media (max-width: 700px) {
  .login main:has(> .quotes, > .headline) > form:not(.undo) { margin-top: auto; padding-top: 30px; }
  .login main:has(> .quotes, > .headline) > form:not(.undo) ~ .small { margin-top: 15px; padding-top: 0; }
  /* With both, what people say stays up top and what Den is sits on the number, the gap between them. */
  .login main:has(> .headline) > .headline { margin-top: auto; padding-top: 30px; }
  .login main:has(> .headline) > .headline ~ form:not(.undo) { margin-top: 0; padding-top: 0; }
}
.sub { display: flex; flex-wrap: wrap; gap: 5px 20px; margin: 15px 0 0; font-size: var(--sm); }
.sub button, .sub a { color: var(--fg2); text-decoration: underline; text-decoration-color: var(--g3); text-underline-offset: 3px; padding: 5px 0; }
.sub button:hover, .sub a:hover { color: var(--fg); text-decoration-color: currentColor; }
.login .small { margin-top: 25px; line-height: 1.5; }
/* Just logged out: one tap puts you back. */
.login form.undo { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 35px; padding: 5px 5px 5px 15px; border-radius: var(--r); background: var(--g1); font-size: var(--sm); }
.login form.undo p { margin: 0; }
.login form.undo .btn { width: auto; min-height: 40px; background: var(--bg); }
/* The code: big digits, spaced, one field. */
.box.code input { font-size: var(--xl); letter-spacing: .35em; padding-left: 15px; font-variant-numeric: tabular-nums; }
.box.code input::placeholder { letter-spacing: .35em; color: var(--fg4); }

/* The phone field: the country (flag, code) and the number, in one box. */
.phone { position: relative; }
.cc { flex: none; display: flex; align-items: center; gap: 5px; min-width: 106px; padding: 0 8px 0 12px; color: var(--fg); font-size: var(--md); font-variant-numeric: tabular-nums; white-space: nowrap; border-right: 1px solid var(--g3); border-radius: var(--r) 0 0 3px; }
.cc:hover, .cc[aria-expanded="true"] { background: var(--g2); }
.cc .chev { width: 12px; height: 12px; color: var(--fg3); margin-left: auto; }
.flag { font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; font-size: 1.15em; line-height: 1; }
.cc-pop { position: absolute; z-index: 10; left: 0; right: 0; top: calc(100% + 5px); background: var(--bg); border: 1px solid var(--g3); border-radius: var(--r); box-shadow: var(--shadow); overflow: hidden; }
.cc-search { display: flex; align-items: center; gap: 8px; padding: 0 12px; border-bottom: 1px solid var(--line); color: var(--fg3); }
.cc-search input { flex: 1; min-width: 0; height: 44px; border: 0; background: transparent; color: var(--fg); font: inherit; font-size: var(--md); outline: 0; -webkit-appearance: none; appearance: none; }
.cc-search input::placeholder { color: var(--fg3); }
.cc-list { list-style: none; margin: 0; padding: 5px 0; max-height: min(38vh, 300px); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
.cc-list li { display: flex; gap: 10px; align-items: center; padding: 8px 12px; cursor: pointer; }
.cc-list li[aria-selected="true"] { background: var(--g1); }
.cc-list li.sep { height: 1px; padding: 0; margin: 5px 0; background: var(--line); cursor: default; }
.cc-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-dial { color: var(--fg3); font-size: var(--sm); font-variant-numeric: tabular-nums; }
.cc-list li[data-current] .cc-dial::after { content: " ✓"; color: var(--focus); }
.cc-none { color: var(--fg3); cursor: default; }

/* A person's page (pages.ts profilePage), as X draws one but quieter: the picture, round, with what you can do on the
   right of it; the name, the @handle; the row of their accounts elsewhere as plain icons; how many friends. Plain text
   and placement say what each is: no boxes, no labels. */
.person { margin: 12px 0 28px; }
.person-top { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.person-acts { position: relative; display: flex; align-items: center; gap: 8px; }
.person-acts .dots { margin-right: -7px; color: var(--fg2); }   /* the dots' edge on the page's edge */
.person-acts .dots .i { width: 18px; height: 18px; }
.person-acts .menu { top: 38px; right: -7px; min-width: 220px; }
/* Someone else's page: talk to them in your own assistant. One under the other on a phone, side by side past that. */
.talk { display: grid; gap: 10px; margin-top: 20px; }
.talk .btn svg { width: 18px; height: 18px; flex: none; }
@media (min-width: 560px) { .talk { grid-template-columns: 1fr 1fr; } }
/* Your page seen as someone else: a quiet line over it saying so, and the way back. */
.seen-as { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 4px 16px; margin: 4px 0 20px; padding: 10px 0; border-bottom: var(--hair) solid var(--line); font-size: var(--sm); color: var(--fg2); }
.seen-as a { color: var(--fg); text-decoration: underline; text-decoration-color: var(--g3); text-underline-offset: 3px; }
.seen-as a:hover { text-decoration-color: currentColor; }
.person-acts .follow { margin: 0; }
.person-acts .btn { width: auto; min-height: 34px; padding: 0 16px; }
.person-acts .tag { font-size: var(--sm); color: var(--fg3); padding: 0 4px; }
div.page .person h1, .person h1 { margin: 0; font-size: var(--xl); font-weight: 700; line-height: 1.2; min-width: 0; overflow-wrap: anywhere; }
.person .handle { margin-top: 2px; color: var(--fg3); overflow-wrap: anywhere; }
.person h1 button, .person .handle button { text-align: left; font-weight: inherit; overflow-wrap: anywhere; }
.person h1 button:hover, .person .handle button:hover { text-decoration: underline; text-decoration-color: var(--g3); text-underline-offset: 4px; }
.person .note { margin: 8px 0 0; }
.who-edit { display: block; width: 100%; padding: 0; border: 0; border-bottom: 1px solid var(--focus); border-radius: 0; background: none; color: var(--fg); font: inherit; outline: 0; }
.person-meta { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 16px; margin-top: 12px; font-size: var(--sm); color: var(--fg2); }
.person-meta b { color: var(--fg); font-weight: 700; }
.person-meta a:hover { text-decoration: underline; text-underline-offset: 3px; }
.quiet { color: var(--fg3); font-size: var(--sm); }
.quiet:hover { color: var(--fg); }
.person .topics { margin: 16px 0 0; }

/* A face: a picture, round (the one round thing, as on X), or the first letter of a name in its colour on grey. Your
   own is a button: a camera shows over it on hover, and the wait is a ring around it while it saves. */
.face { position: relative; flex: none; display: inline-grid; place-items: center; width: var(--size); height: var(--size); border-radius: 50%; overflow: hidden;
  background: var(--g1); box-shadow: inset 0 0 0 var(--hair) var(--line); color: light-dark(color-mix(in srgb, var(--c) 78%, black), var(--c)); }
.face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.face-letter { font-size: calc(var(--size) * .42); font-weight: 700; line-height: 1; }
button.face { cursor: pointer; }
.face-edit { position: absolute; inset: 0; display: grid; place-items: center; background: var(--scrim); color: var(--on-media); opacity: 0; transition: opacity .15s; }
.face-edit .i { width: calc(var(--size) * .3); height: calc(var(--size) * .3); }
button.face:hover .face-edit, button.face:focus-visible .face-edit { opacity: 1; }
button.face.lettered .face-edit { opacity: 1; background: none; color: var(--fg3); }
button.face.lettered .face-letter { display: none; }
.face[aria-busy]::after { content: ""; position: absolute; inset: 0; border-radius: 50%; border: 3px solid transparent; border-top-color: var(--focus); animation: spin .8s linear infinite; }
.face[aria-busy] .face-edit { opacity: 0; }
.face-xs { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; flex: none; }
.top .right { display: flex; align-items: center; gap: 6px; }
.top .right[aria-busy] .face-xs { opacity: .5; }
/* Completing your profile: the one thing to do, in the bar where your name would be. */
.top .btn.cta { width: auto; min-height: 32px; margin-left: auto; padding: 0 14px; flex: none; white-space: nowrap; }

/* Accounts elsewhere: one row of plain icons, each a link out. Yours ends in a plus that shows, faint, what you could add. */
.sites { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; margin: 10px 0 0 -7px; }
.site { display: inline-grid; place-items: center; width: 36px; height: 36px; border-radius: var(--r); color: var(--fg2); }
.site .i { width: 20px; height: 20px; }
.site:hover, .site[data-picked] { color: var(--fg); background: var(--g1); }
.site.add { color: var(--fg4); }
.site.add:hover { color: var(--fg); }
.site.plus { color: var(--fg3); }
/* With nothing linked yet, the plus says what it's for. */
.site.plus.first { width: auto; gap: 6px; grid-auto-flow: column; padding: 0 8px; font-size: var(--sm); }
.sites.open .site.plus.first span { display: none; }
.sites.open .site.plus .i { rotate: 45deg; }
.sites.open a.site { box-shadow: inset 0 0 0 var(--hair) var(--g3); }
/* On a person's page their accounts elsewhere are words, each ending on the arrow up and to the right, in one quiet
   line with "Add" at its end. (Picking from the whole grid, on completing your profile, keeps the logos.) */
.person .sites { gap: 2px 18px; margin: 12px 0 0; }
.person .site { display: inline-flex; align-items: center; gap: 4px; width: auto; height: auto; padding: 3px 0; border-radius: 0; background: none; font-size: var(--sm); color: var(--fg2); }
.person .site .i { display: none; }
.person .site-name { display: inline; }
.person a.site::after { content: ""; width: 11px; height: 11px; background: currentColor; opacity: .55; transition: translate .2s cubic-bezier(.22, 1, .36, 1), opacity .2s ease;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25'/%3E%3C/svg%3E") center / contain no-repeat; }
.person .site:hover, .person .site[data-picked] { color: var(--fg); background: none; }
.person a.site:hover::after { translate: 1px -1px; opacity: 1; }
.person .site.add { color: var(--fg3); }
.person .sites.open a.site { box-shadow: none; text-decoration: underline; text-decoration-color: var(--g3); text-underline-offset: 4px; }
.person .sites.open a.site::after { display: none; }
.person .site.plus, .person .site.plus.first { width: auto; padding: 3px 0; color: var(--fg3); font-size: var(--sm); }
.person .site.plus:hover { color: var(--fg); }
.site.plus .short, .site.plus:not(.first) .long { display: none; }
.site.plus:not(.first) .short { display: inline; }
.sites.open .site.plus .long, .sites.open .site.plus .short { display: none; }
.sites.open .site.plus::after { content: attr(data-done); }
.person .sites.open .site.plus { color: var(--fg); }   /* Done stands apart from what you could add */
.site-form { margin: 10px 0 0; }
.site-form label { display: block; font-size: var(--xs); color: var(--fg3); margin-bottom: 5px; }
.site-form .row { display: flex; gap: 8px; align-items: center; }
.site-form .box { flex: 1; min-width: 0; }
.site-form .btn { width: auto; flex: none; }
.site-form .note { margin: 6px 0 0; }

/* A list of people (friends): picture, name, @handle and a line, the whole row a link, one under another. */
.list-head { display: flex; align-items: center; gap: 8px; margin: 6px 0 16px -7px; }
div.page .list-head h1 { margin: 0; font-size: var(--lg); font-weight: 700; }
.people { list-style: none; margin: 10px 0 0; padding: 0; }
.person-row { display: flex; gap: 12px; align-items: flex-start; padding: 10px 0; border-bottom: var(--hair) solid var(--line); }
.person-row:hover .name { text-decoration: underline; text-underline-offset: 3px; }
.person-row .names { display: grid; min-width: 0; line-height: 1.3; }
.person-row .name { font-weight: 700; overflow-wrap: anywhere; }
.person-row .handle { color: var(--fg3); font-size: var(--sm); }
.person-row .line { margin-top: 3px; color: var(--fg2); font-size: var(--sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Completing your profile: a step a screen, big and plain, the thing to do under the words, Skip under that. */
.complete { padding-top: 20px; }
div.page .complete h1 { margin: 6px 0 4px; font-size: var(--xl); font-weight: 700; }
.complete-face { display: flex; justify-content: center; margin: 28px 0; }
.complete-acts { display: grid; gap: 10px; margin-top: 24px; }
.complete .box.at { display: flex; align-items: center; gap: 2px; }
.complete .box.at span { color: var(--fg3); padding-left: 12px; }
.site-name { display: none; }
/* Completing your profile, the row is the whole screen: each site a tile with its name, in full ink. */
.site-grid .sites { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; margin: 24px 0 0; }
.site-grid .site { width: auto; height: 76px; gap: 6px; align-content: center; color: var(--fg); background: var(--g1); }
.site-grid .site:hover, .site-grid .site[data-picked] { background: var(--g2); }
.sites.big a.site { box-shadow: inset 0 0 0 1.5px var(--fg); }
.site-grid .site .i { width: 24px; height: 24px; }
.site-grid .site-name { display: block; font-size: var(--xs); color: var(--fg2); }
.site-grid .site.plus { display: none; }

/* Cropping a picture: the photo under a round window, the page dimmed behind. */
.cropper { width: min(420px, calc(100vw - 2 * var(--gutter))); padding: 16px; border: 0; border-radius: var(--r); background: var(--bg); color: var(--fg); box-shadow: var(--shadow); }
.cropper::backdrop { background: var(--scrim); }
.crop-view { position: relative; width: 100%; aspect-ratio: 1; overflow: hidden; touch-action: none; cursor: grab; background: var(--media); border-radius: var(--r); }
.crop-view:active { cursor: grabbing; }
.crop-view canvas { width: 100%; height: 100%; display: block; }
.crop-ring { position: absolute; inset: 0; pointer-events: none; border-radius: 50%; box-shadow: 0 0 0 999px color-mix(in srgb, var(--media) 55%, transparent); outline: 1px solid color-mix(in srgb, var(--on-media) 60%, transparent); }
.cropper .small { text-align: center; margin: 10px 0; }
.crop-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.rows { display: grid; gap: 10px; }
.label { color: var(--fg3); font-size: var(--xs); margin-bottom: 5px; }
.topics { margin-bottom: 35px; counter-reset: t; }
.topic { display: grid; grid-template-columns: 22px 1fr; text-align: left; padding: 3px 0; color: var(--fg); counter-increment: t; }
.topic::before { content: counter(t, lower-alpha) "."; color: var(--fg3); }
.topic:hover { color: var(--focus); }
/* The room a person's page opens on (pages.ts roomBlock): edge to edge on a phone, the column's width past that. Every
   room is shown on black (src/ground.ts), so the box is black in the light and in the dark alike: in the dark the room
   stands on the page itself, in the light on a black plate. It holds its shape before anything loads. */
.room { margin: 25px calc(-1 * max(var(--gutter), env(safe-area-inset-right))) 5px calc(-1 * max(var(--gutter), env(safe-area-inset-left))); }
.room-pic { background: var(--media); overflow: hidden; }
.room-pic img { display: block; width: 100%; height: 100%; object-fit: contain; }
.room figcaption { padding: 10px max(var(--gutter), env(safe-area-inset-right)) 0 max(var(--gutter), env(safe-area-inset-left)); font-size: var(--sm); color: var(--fg2); }
@media (min-width: 581px) {
  .room { margin: 25px 0 5px; }
  .room-pic { border-radius: var(--r); }
  .room figcaption { padding-inline: 0; }
}
/* A person's page is a head over their thoughts (the feed), and under the last of your own, a foot: small, one line. */
.page.head { padding-bottom: 0; }
.foot { display: flex; flex-wrap: wrap; gap: 5px 20px; padding-top: 35px; padding-bottom: 10px; color: var(--fg3); font-size: var(--sm); }
.foot a:hover, .foot button:hover { color: var(--fg); }
/* The theme this device shows: plain words, the one in use in the text's colour. */
.foot .theme { flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: baseline; gap: 5px 14px; }
.foot .theme [aria-pressed="true"] { color: var(--fg); }
.foot .theme .sep { color: var(--fg4); }
/* What Den is, in one line along the top of a person's page (pages.ts aboutBanner): plain grey words, the two
   assistants as their logos, and the arrow after them. All of it is the link. */
.about-banner { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 5px; width: fit-content; max-width: 100%; margin: 10px auto 20px; font-size: var(--sm); color: var(--fg2); }
.about-banner:hover { color: var(--fg); }
.about-banner svg:not(.i) { width: 15px; height: 15px; flex: none; }
.about-banner .i.after { margin-left: 2px; }

/* Add to Claude / Add to ChatGPT, each a grey button with its logo; a connected app is a quiet tick. */
.connect { display: grid; gap: 10px; margin: 0 0 25px; }
.app { display: flex; align-items: center; gap: 10px; min-height: 45px; padding: 0 15px; border-radius: var(--r); font-size: var(--sm); text-align: left; background: var(--g1); color: var(--fg); }
.app:hover { background: var(--g2); }
.app svg { flex: none; }
/* Every row's icon sits in the same 20px column, so every label starts on the same line. */
.app > .i { margin-inline: 2px; }
.connect .steps { list-style: none; margin: 5px 0 0; padding: 0; display: grid; gap: 2px; counter-reset: s; }
.connect .steps li { display: grid; grid-template-columns: 22px 1fr; align-items: baseline; counter-increment: s; }
.connect .steps li::before { content: counter(s, lower-alpha) "."; color: var(--fg3); }
.connect .steps li > :not(button) { grid-column: 2; }
.connect .steps li > span { padding: 4px 0; }
.connect .how { display: grid; gap: 8px; }
.connect .steps button { text-align: left; color: var(--fg); padding: 4px 0; transition: color .2s; }
.connect .steps button[aria-pressed="true"] { color: var(--fg3); text-decoration: line-through; }
.connect .steps li:has(button[aria-pressed="true"])::before { content: "✓"; color: var(--green); }
/* Always allow: an open box to tick in a grey row; tapped, the row grows to hold its steps. */
.allow { border-radius: var(--r); background: var(--g1); }
.todo { display: flex; align-items: stretch; min-height: 45px; font-size: var(--sm); }
.todo .open { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; padding: 0 0 0 15px; text-align: left; }
.todo .open svg { flex: none; }
.todo .tick { display: grid; place-items: center; padding: 0 15px 0 12px; }   /* the whole end of the row is the box */
.todo .tick:hover .tickbox { border-color: var(--fg2); }
.app .of { margin-left: auto; color: var(--fg3); font-variant-numeric: tabular-nums; }
.tickbox { flex: none; margin: 0 1px 0 auto; transition: border-color .15s; display: grid; place-items: center; width: 18px; height: 18px; border: 1.5px solid var(--fg3); border-radius: var(--r); }
.tickbox .i { width: 18px; height: 18px; stroke-width: 2.4; color: var(--green); opacity: 0; transition: opacity .15s; }
/* Done is the tick alone: the box goes. */
.allow[data-done] .tickbox, .tickbox[data-on] { border: 0; }
.allow[data-done] .tickbox .i, .tickbox[data-on] .i { opacity: 1; }
/* A row being waited on: its box is the ring, whether or not it was ticked. */
.app[aria-busy] .tickbox, .app[aria-busy] .tickbox[data-on] { border: 2px solid var(--g3); border-top-color: var(--fg2); border-radius: 50%; animation: spin .8s linear infinite; }
.app[aria-busy] .tickbox .i, .app[aria-busy] .tickbox[data-on] .i { opacity: 0; }
.todo-more { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .24s cubic-bezier(.22, 1, .36, 1); }
.todo-more > div { min-height: 0; overflow: hidden; visibility: hidden; transition: visibility .24s; }
.allow[data-open] .todo-more { grid-template-rows: 1fr; }
.allow[data-open] .todo-more > div { visibility: visible; }
/* Quotes that cycle on the sign-in page (sign_in_quotes.ts): stacked in one cell, so the page never changes height. */
.login main:has(> .quotes, > .headline) { display: flex; flex-direction: column; min-height: 100dvh; padding-top: max(10vh, 45px); padding-bottom: max(20px, env(safe-area-inset-bottom)); }
.login main:has(> .quotes, > .headline) > .small { margin-top: auto; padding-top: 25px; }
.quotes { display: grid; margin-bottom: 25px; }
/* One leaves, then the next arrives: two sentences never overlap. */
/* Something someone said, large, signed with their @handle (pages.ts said). Cycling on the sign-in page, one shows at a time. */
.quote { margin: 0; }
.quotes .quote { grid-area: 1 / 1; opacity: 0; translate: 0 8px; transition: opacity .3s ease, translate .3s ease; }
.quotes .quote[data-on] { opacity: 1; translate: 0 0; transition: opacity .5s ease .3s, translate .5s cubic-bezier(.22, 1, .36, 1) .3s; }
.quotes .quote[data-was] { translate: 0 -8px; }
.quote.by-first footer { margin-bottom: 8px; font-size: var(--sm); color: var(--fg2); }
/* What follows a subject and can't be read yet: a stand-in, blurred, wherever the quote is shown. */
.quote .veiled { filter: blur(10px); user-select: none; pointer-events: none; }
/* The invite that brought them, over a sign-in step: it sits where it sat on its own page. */
.login main:has(> .quote.by-first) { padding-top: 32px; }
.login main > .quote.by-first { margin-bottom: 20px; }
.quote.by-first p { margin-bottom: 14px; white-space: pre-wrap; overflow-wrap: anywhere; }
.quote p { margin: 0 0 8px; font-size: var(--xl); line-height: 1.25; }
.quote footer { font-size: var(--xs); }
@media (prefers-reduced-motion: reduce) { .quotes .quote { transition: none; } }
/* What Den is, over the number on the sign-in page (pages.ts headline): its lines share one cell, so the page never
   changes height, and take turns there. A line leaves a letter at a time, rising out of focus; the next arrives the
   same way from below, each letter a moment after the one before. */
.login .headline { display: grid; margin: 0 0 25px; font-size: var(--xl); line-height: 1.25; }
.headline .line { grid-area: 1 / 1; text-wrap: balance; }
.headline .word { display: inline-block; white-space: nowrap; }
.headline .word span { display: inline-block; opacity: 0; }
.headline .logo svg { display: block; width: .85em; height: .85em; }
.headline .word:has(.logo) { vertical-align: -.08em; }
.headline .line[data-on] .word span { opacity: 1; animation: letter-in .7s cubic-bezier(.22, 1, .36, 1) backwards; animation-delay: calc(var(--i) * 22ms); }
/* Taking a turn, it waits for the one before to go: two lines are never read at once. */
.headline[data-turning] .line[data-on] .word span { animation-delay: calc(.32s + var(--i) * 22ms); }
.headline .line[data-was] .word span { animation: letter-out .32s ease both; animation-delay: calc(var(--i) * 5ms); }
@keyframes letter-in { from { opacity: 0; filter: blur(8px); translate: 0 .4em; } }
@keyframes letter-out { from { opacity: 1; } to { opacity: 0; filter: blur(6px); translate: 0 -.3em; } }
/* Less motion: the lines still take turns, since each says something, but only by fading. */
@media (prefers-reduced-motion: reduce) {
  .headline .line[data-on] .word span, .headline[data-turning] .line[data-on] .word span { animation: fade .4s ease backwards; animation-delay: 0s; }
  .headline[data-turning] .line[data-on] .word span { animation-delay: .4s; }
  .headline .line[data-was] .word span { animation: fade-out .4s ease both; animation-delay: 0s; }
}
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
/* A sealed invite: nothing over it, and its two buttons at the foot of the screen, each with its app's mark. */
.login main:has(.sealed, .foot) { display: flex; flex-direction: column; min-height: 100dvh; padding-bottom: max(20px, env(safe-area-inset-bottom)); }
/* A page whose only step is a button or two (a sealed invite, a thought that isn't yours to read: .sealed, .foot).
   The message sits a little above the middle of the space over the buttons (two parts above it, three below), where
   the eye lands; the buttons stay at the foot, by the thumb. A tall one (a picture) just fills from the top. */
.letter:has(.sealed, .foot) { flex: 1; display: grid; grid-template-rows: 2fr auto 3fr auto; grid-template-columns: minmax(0, 1fr); }
.letter:has(.sealed, .foot) > .run.lead { grid-row: 2; margin: 0; }
.letter .sealed, .letter .foot { grid-row: 4; margin: 0; padding-top: 25px; }
.letter:has(.sealed, .foot) > .signed { grid-row: 5; margin-top: 14px; }
.spinner { width: 18px; height: 18px; border: 2px solid var(--g3); border-top-color: var(--fg2); border-radius: 50%; animation: spin .8s linear infinite; }
@keyframes spin { to { rotate: 360deg; } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.letter .sealed .btn svg { width: 18px; height: 18px; flex: none; }
.letter .quote p { font-size: var(--xl); line-height: 1.25; }

.allow .steps { margin: 0; padding: 0 15px; }
.allow .steps li { grid-template-columns: 30px 1fr; }   /* the letters under the box, the words under its label */
.connect .allow p { margin: 5px 0 0; padding: 0 15px 12px 45px; font-size: var(--xs); }
.connect .allow a { color: var(--fg2); text-decoration: underline; font-size: var(--sm); justify-self: start; }
/* The connector link: in a box, the whole box copies it, a copy icon on the right that turns to a tick. */
.connect .address { margin: 5px 0 0; display: grid; gap: 5px; }
.connect .address .label { margin: 0; }
.copybox { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 45px; padding: 0 12px 0 15px; border: 1px solid var(--g3); border-radius: var(--r); background: var(--bg); color: var(--fg); text-align: left; }
.copybox:hover { border-color: var(--fg3); }
.copybox .mono { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: var(--sm) ui-monospace, SFMono-Regular, Menlo, monospace; }
.copybox .i { color: var(--fg2); }
.copybox .did, .copybox[data-done] .do { display: none; }
.copybox[data-done] .did { display: block; color: var(--green); }

/* The feed: the page never scrolls, only #s does; #app is sized to the visible viewport (feed.js). */
html.thread, body.thread { height: 100%; overflow: hidden; touch-action: manipulation; }
#app { position: fixed; left: 0; right: 0; top: 0; height: 100%; display: flex; flex-direction: column; }
#app > .top { flex: none; }
#s { flex: 1; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; padding-bottom: max(25px, env(safe-area-inset-bottom)); }
#log { padding-top: 5px; }

/* The box: one grey field; Send turns navy once there's something to send. */
.composer { flex: none; display: flex; align-items: flex-end; gap: 5px; padding-top: 5px; padding-bottom: 10px; }
.composer .field { flex: 1; min-width: 0; display: flex; align-items: flex-end; gap: 2px; background: var(--g1); border: 1px solid var(--g1); border-radius: var(--r); padding: 0 5px; transition: border-color .15s, background .15s; }
.composer .field:focus-within { background: var(--bg); border-color: var(--focus); }
.composer textarea { flex: 1; display: block; min-width: 0; height: 43px; max-height: 40vh; margin: 0; padding: 11px 5px; background: transparent; border: 0; outline: 0; resize: none; overflow-y: auto;
  font: inherit; font-size: var(--md); line-height: 1.3; color: var(--fg); -webkit-appearance: none; appearance: none; scrollbar-width: thin; }
.composer textarea::placeholder { color: var(--fg3); }
.composer .ib { height: 43px; }
/* The + sits snug to its icon: the box is for words. */
.composer #add { min-width: 26px; padding: 0 5px; }
#go { flex: none; height: 45px; padding: 0 15px; border-radius: var(--r); background: var(--g1); color: var(--fg3); font-size: var(--sm); transition: background .15s, color .15s; }
#go.on { background: var(--brand); color: var(--on-brand); }
.replying { flex: none; display: flex; gap: 8px; align-items: center; color: var(--fg2); font-size: var(--sm); padding-top: 5px; }
.replying .i { width: 14px; height: 14px; color: var(--fg3); }
.replying span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.replying button { margin-left: auto; }
.drop #s { outline: 2px dashed var(--focus); outline-offset: -8px; }

/* An icon button: quiet grey, darker on hover, a 32px target (44px for fingers). */
.ib { flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 3px; min-width: 30px; height: 30px; padding: 0 5px; border-radius: var(--r); color: var(--fg3); font-size: var(--xs); font-variant-numeric: tabular-nums; }
.ib:hover, .ib[data-open] { color: var(--fg); background: var(--g1); }
.ib.danger:hover { color: var(--alert); }
.ib[data-done] { color: var(--green); }

/* A thread: the first thought, then its replies, each level indented on a thin rule. */
/* A conversation; the line under it runs out past the text to the edge of the screen. */
.thread-block { margin-inline: calc(-1 * var(--edge)); padding: 20px var(--edge); border-bottom: var(--hair) solid var(--line); }
.thread-block:last-of-type { border-bottom: 0; }
.kids { margin-left: 4px; margin-top: 4px; padding-left: 16px; border-left: var(--hair) solid var(--g3); }
/* Replies fold away: the line under a thought says how many and when the latest came, and opens them. */
.fold { display: inline-flex; align-items: center; gap: 5px; margin: 0 0 5px; padding: 2px 0; border: 0; background: none; color: var(--fg3); font: inherit; font-size: var(--xs); cursor: pointer; }
.fold:hover { color: var(--fg); }
.fold .i { width: 10px; height: 10px; transition: transform .15s; }
.node[data-folded] > .fold .i { transform: rotate(-90deg); }
.node[data-folded] > .kids { display: none; }
.kids .kids .kids .kids { margin-left: 0; padding-left: 0; border-left: 0; }
.post { position: relative; padding: 8px 0; overflow-wrap: anywhere; }
.post[data-rises] { animation: rise .5s cubic-bezier(.22, 1, .36, 1) both; }
/* The thought you opened by its link: a soft amber behind its words, like a marked passage. */
.post.focus > .body { background: var(--mark); box-shadow: -6px 0 0 var(--mark), 6px 0 0 var(--mark); border-radius: var(--r); }
/* The box to reply sits right under the thought you opened (feed.js placeBox). Words are one size everywhere. */
.composer.inline, .replying.inline { max-width: none; padding-left: 0; padding-right: 0; }
.composer.inline { padding: 6px 0 12px; }
/* The line above the words. It is one sentence of running text (feed.js sentence): "Justin to Linus in Den · 1d", so
   every gap is the type's own word space and a long one wraps as text does. The quiet icons sit at its end, level
   with its first line. */
.by { display: flex; align-items: flex-start; gap: 0 8px; min-height: 26px; margin-bottom: 2px; font-size: var(--xs); color: var(--fg3); }
.by .said { flex: 0 1 auto; min-width: 0; padding: 4px 0; line-height: 18px; }
/* A name: semibold in the person's colour, as Den's speakers wear theirs: as it is on black; on white, darkened just
   enough that yellow and lime still read. */
.badge { font-weight: 600; color: light-dark(color-mix(in srgb, var(--c) 78%, black), var(--c)); white-space: nowrap; }
.by .when { color: var(--fg3); }
.by .when:hover { color: var(--fg); }
.by .sep, .by .dot { color: var(--fg3); }
/* Someone not on Den yet: a name like any other, in grey. */
.by .not-on { font-weight: 600; color: var(--fg3); white-space: nowrap; }
.by .invite { margin-left: auto; padding: 4px 0; line-height: 18px; color: var(--focus); white-space: nowrap; }
.by .invite:hover { text-decoration: underline; }
.by .filed { color: var(--fg2); }
/* Held until a time, and private: more of the sentence, a small icon before the words. */
.by .held, .by .private { color: var(--fg2); }
.by .private { text-transform: lowercase; }
.by .held .i, .by .private .i { display: inline-block; width: 11px; height: 11px; margin-right: 3px; vertical-align: -1px; }
/* At the end of a thought's top line: the eye once it's been seen, and ⋯, small and grey, for its menu. */
.by .acts { display: inline-flex; align-items: center; gap: 2px; margin: 0 -6px 0 auto; }
.by .invite + .acts { margin-left: 0; }
.by .acts .ib { position: relative; min-width: 26px; height: 26px; color: var(--fg3); }
/* A finger's worth of target, no layout. On ::before: ::after is the tooltip, and sharing it drew an empty white box (14 Nov). */
.by .acts .ib::before { content: ""; position: absolute; inset: -7px -3px; }
.by .acts .dots .i { width: 16px; height: 16px; }
.post:not(.focus) { cursor: pointer; }
/* The menu ⋯ opens: words only, the editor's menu look (editor.css .doc-menu). It scales in from the dots. */
@keyframes menu-in { from { opacity: 0; scale: .94; } }
@keyframes menu-out { to { opacity: 0; scale: .97; } }
.menu { position: absolute; z-index: 10; right: -6px; top: 32px; min-width: 180px; max-width: min(280px, calc(100vw - 2 * var(--gutter))); padding: 5px; background: var(--bg); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--shadow); font-size: var(--sm); cursor: default; transform-origin: 100% 0; animation: menu-in .13s cubic-bezier(.2, .9, .3, 1.15) both; }
.menu[data-up] { top: auto; bottom: calc(100% - 6px); transform-origin: 100% 100%; }
.menu[data-out] { animation: menu-out .09s ease-in both; pointer-events: none; }
.menu .row { display: flex; align-items: center; width: 100%; min-height: 30px; padding: 0 10px; border-radius: var(--r); text-align: left; color: var(--fg); }
.menu .row .i { width: 16px; height: 16px; margin-right: 10px; flex: none; color: var(--fg3); stroke-width: 1.5; }
.menu .row:hover .i, .menu .row:focus-visible .i { color: var(--fg); }
.menu .row.danger:hover .i, .menu .row.danger:focus-visible .i { color: var(--alert); }
.menu .row:hover, .menu .row:focus-visible { background: var(--g1); outline: 0; }
.menu .row.danger:hover, .menu .row.danger:focus-visible { color: var(--alert); }
.menu .label { margin-top: 5px; padding: 8px 10px 5px; border-top: var(--hair) solid var(--line); font-size: var(--xs); line-height: 1.4; color: var(--fg3); }
@media (pointer: coarse) { .menu .row { min-height: 40px; } }
@media (prefers-reduced-motion: reduce) { .menu { animation: none; } }
.edit-bar { display: flex; gap: 10px; margin-top: 6px; }
.edit-bar .btn { width: auto; min-height: 32px; padding: 0 12px; }
.body { white-space: pre-wrap; font-size: var(--md); line-height: 1.5; margin: 0; -webkit-user-select: text; user-select: text; }
/* Cut to six lines until "See more" (public/web/clamp.js), unless that would only hide a line. */
.body[data-clamp]:not([data-short]) { display: -webkit-box; -webkit-line-clamp: var(--lines, 6); -webkit-box-orient: vertical; overflow: hidden; }
.body { overflow-wrap: anywhere; }
/* Words with blocks in them (web/doc.js draw): the shapes the editor shows (public/editor.css), at the feed's size.
   A mark (bullet, number, box, arrow) sits in a column of its own; what's under a block is indented one step. */
.body.rich { white-space: normal; }
.body.rich[data-clamp]:not([data-short]) { display: block; max-height: calc(var(--lines, 6) * 1.5em); overflow: hidden; -webkit-mask-image: linear-gradient(#000 calc(100% - 1.5em), transparent); mask-image: linear-gradient(#000 calc(100% - 1.5em), transparent); }
.body.rich .line { display: flex; align-items: flex-start; }
.body.rich .text { flex: 1; min-width: 0; white-space: pre-wrap; }
.body.rich .under { margin-left: 22px; }
.body.rich .mark { flex: none; min-width: 22px; }
.body.rich .blk[data-type="numberedListItem"] > .line > .mark { padding-right: 5px; font-variant-numeric: tabular-nums; }
.body.rich .check { min-width: 0; width: 14px; height: 14px; margin: calc((1.5em - 14px) / 2) 8px 0 0; border: 1px solid var(--fg2); border-radius: 3px; }
.body.rich .check[data-on] { border-color: var(--brand); background: var(--brand) center / 10px no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 12.75 6 6 9-13.5'/%3E%3C/svg%3E"); }
.body.rich .blk[data-checked] > .line > .text { color: var(--fg3); text-decoration: line-through; }
.body.rich .fold { display: grid; place-items: center; width: 22px; height: 1.5em; margin-left: -4px; margin-right: 4px; min-width: 0; border-radius: var(--r); color: var(--fg); }
.body.rich .fold::before { content: ""; width: 9px; height: 10px; background: currentColor; transition: transform .12s ease;
  /* The editor's arrow: a triangle with its corners a touch rounded (editor.css .bn-toggle-button). */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 10'%3E%3Cpath d='M1.6 1.4v7.2L7.6 5z' stroke='%23000' stroke-width='1.6' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 10'%3E%3Cpath d='M1.6 1.4v7.2L7.6 5z' stroke='%23000' stroke-width='1.6' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
.body.rich .blk[data-open] > .line > .fold::before { transform: rotate(90deg); }
.body.rich .blk[data-folds]:not([data-open]) > .under { display: none; }
@media (hover: hover) { .body.rich .fold:hover { background: var(--g2); } }
.body.rich .blk[data-type="heading"] > .line { font-size: var(--xl); line-height: 1.25; margin: .5em 0 .15em; }
.body.rich .blk[data-type="heading"][data-level="2"] > .line { font-size: 1.3rem; }
.body.rich .blk[data-type="heading"][data-level="3"] > .line { font-size: 1.15rem; }
.body.rich > .blk[data-type="heading"]:first-child > .line { margin-top: 0; }
.body.rich .blk[data-type="heading"] .fold { height: 1.25em; }
.body.rich .blk[data-type="quote"] > .line { border-left: 3px solid var(--fg); padding-left: 10px; margin: 2px 0; }
.body.rich .blk[data-type="file"] { margin: 6px 0; }
.body.rich .blk[data-type="file"] img, .body.rich .blk[data-type="file"] video { display: block; max-width: 100%; height: auto; border-radius: var(--r); }
.body.rich hr { border: 0; border-top: 1px solid var(--g3); margin: 10px 0; }
.body.rich pre { margin: 5px 0; padding: 10px 12px; background: var(--g1); border-radius: var(--r); font: var(--sm)/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; }
.body.rich .text code { word-break: normal; font: .85em/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--g2); border-radius: var(--r); padding: .15em .35em; }
.body.rich b { font-weight: 700; }
.body.rich u { text-underline-offset: 3px; }
/* Under the words, on their left edge, only as wide as it reads (the magazine look's column would stretch and centre it). */
.more { display: block; width: fit-content; align-self: flex-start; color: var(--fg2); font-size: var(--sm); padding: 3px 0; }
.more:hover { color: var(--fg); }
.post textarea { display: block; width: 100%; padding: 8px 10px; border: 1px solid var(--focus); border-radius: var(--r); background: var(--bg); color: var(--fg); font: inherit; outline: 0; resize: none; }
.status { color: var(--fg2); font-size: var(--xs); margin-top: 3px; }
.status button { color: var(--fg); text-decoration: underline; }
.tell-claude { display: block; width: fit-content; }
.tell-claude:hover { color: var(--fg); }
.seen-list { display: grid; gap: 2px; margin: 3px 0 0; padding: 8px 10px; background: var(--g1); border-radius: var(--r); color: var(--fg2); font-size: var(--xs); }

/* What a thought holds, under its words, in order: pictures and players at their own shape, pages in a frame, the rest by name. */
.files { display: grid; gap: 8px; margin-top: 8px; justify-items: start; }
.media { display: block; max-width: min(100%, 520px); }
.media img, img.media { display: block; max-width: 100%; max-height: 70vh; width: auto; height: auto; border-radius: var(--r); }
video.media { width: 100%; max-height: 70vh; border-radius: var(--r); background: var(--media); }
audio.media { width: min(100%, 420px); height: 40px; }
iframe.page { width: min(100%, 520px); aspect-ratio: 1; max-height: 70vh; border: 0; border-radius: var(--r); background: var(--g1); }
/* Two or more things to look at: one shelf (public/web/shelf.js). One height; each tile as wide as its shape, between
   half the height and 92% of the row, so the next always peeks in. Edge to edge on a phone, hairline gaps, snapped to
   the middle. A shape too extreme to crop is shown whole on its tile. */
.shelf { --h: min(66vw, 352px); position: relative; justify-self: stretch; min-width: 0; }
.shelf .track { display: flex; gap: 2px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none; }
.shelf .track::-webkit-scrollbar { display: none; }
.shelf .tile { flex: none; height: var(--h); width: clamp(calc(var(--h) * .5), calc(var(--h) * var(--a, 1)), 92%); scroll-snap-align: center; overflow: hidden; border-radius: var(--r); background: var(--g1); }
.shelf .tile > .media, .shelf .tile > .media img, .shelf .tile > iframe { display: block; width: 100%; height: 100%; max-width: none; max-height: none; border-radius: 0; aspect-ratio: auto; }
.shelf .tile .media img, .shelf .tile > video.media { object-fit: cover; }
.shelf .tile[data-whole] .media img { object-fit: contain; }

/* The arrows are den-fiction's strip arrows (ui-kit.css ui-strip-arrow): a bare chevron on each edge the row can
   still travel to, white with a soft shadow so it reads on any picture; faint while the row is hovered, clear under the
   pointer, never there on a touch screen. */
.shelf .nav { position: absolute; z-index: 1; top: 50%; translate: 0 -50%; display: grid; place-items: center; width: 27px; min-width: 0; height: 27px; padding: 0; border-radius: 0; background: transparent; color: var(--on-media); filter: drop-shadow(0 1px 3px color-mix(in srgb, var(--media) 58%, transparent)); opacity: 0; transition: opacity .16s ease; }
.shelf .nav .i { width: 18px; height: 18px; }
.shelf .nav[hidden] { display: none; }
.shelf .nav.prev { left: 0; } .shelf .nav.prev .i { rotate: 90deg; }
.shelf .nav.next { right: 0; } .shelf .nav.next .i { rotate: -90deg; }
.shelf:hover .nav, .shelf .nav:focus-visible { opacity: .55; }
.shelf:hover .nav:hover { opacity: .95; background: transparent; color: var(--on-media); }
@media not all and (hover: hover) and (pointer: fine) { .shelf .nav { display: none; } }
@media (prefers-reduced-motion: reduce) { .shelf .nav { transition: none; } }
/* One song or link card on its own: upright, and small, so it sits beside the words rather than filling the screen. */
iframe.page[data-upright] { width: min(100%, 300px); background: transparent; }
iframe.page[data-sized] { background: transparent; border-radius: 0; }
/* A web address in someone's words: X's blue, underlined only under the pointer, as X shows one. */
.body a { color: var(--link); text-decoration: none; }
.body a:hover { text-decoration: underline; }
.file { display: inline-flex; align-items: center; gap: 5px; color: var(--fg2); font-size: var(--sm); }
.file:hover { color: var(--fg); }
.file .size { color: var(--fg3); }

#t { color: var(--fg2); font-size: var(--sm); }
#t:not(:empty) { padding: 8px 0 0; }
#t button { color: var(--fg); text-decoration: underline; }
.empty { color: var(--fg2); margin-top: 25px; }
.empty a { color: var(--fg); }
.empty a:hover { text-decoration: underline; text-underline-offset: 3px; }
.earlier { color: var(--fg3); font-size: var(--sm); padding: 15px 0 25px; }
.earlier button { color: var(--fg2); text-decoration: underline; }
.up { margin-top: 25px; display: grid; gap: 15px; }
.up input[type="file"] { color: var(--fg2); font: inherit; font-size: var(--sm); max-width: 100%; }

@keyframes rise { from { opacity: 0; translate: 0 8px; } }
@keyframes fade { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .post[data-rises] { animation-name: fade; } }

/* Fingers: the same look, with targets you can hit, the space taken from padding, not layout. */
@media (pointer: coarse) {
  .top .tabs a { padding: 10px 12px; }
  .top .right { padding-top: 10px; padding-bottom: 10px; }
  .ib { min-width: 40px; height: 40px; }
  .composer #add { min-width: 34px; }
  .cc-list li { padding: 12px; }
  .topic { padding: 8px 0; }
  .sub button, .sub a { padding: 10px 0; }
}
/* Searching countries: the list has the focus, not the number. */
.box.phone:has(.cc-pop:focus-within) { background: var(--g1); border-color: var(--g1); }

/* What a sent link opens: the thought on top at every step, then one thing to do. */
.letter .next { margin-top: 36px; padding-top: 24px; border-top: 1px solid var(--g2); }
.letter .next h2 { margin: 0 0 6px; font-size: 19px; }
.login main:has(.letter) { padding-top: 32px; }
.letter .run { margin: 0 0 22px; }
.letter .run p { margin: 0 0 12px; font-size: 17px; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
.letter .run p:last-child { margin-bottom: 0; }
.letter .run .who { margin: 0 0 6px; font-size: 14px; color: var(--fg3); }
.letter .run.mine { padding-left: 12px; border-left: 1px solid var(--g2); }
.letter .hook { margin: 0 0 14px; font-size: 24px; line-height: 1.25; font-weight: 600; letter-spacing: -0.01em; white-space: pre-wrap; overflow-wrap: anywhere; }
.letter .media { display: block; width: 100%; margin: 0 0 14px; }
.letter .veiled { filter: blur(10px); user-select: none; pointer-events: none; }
/* Locked, the picture is a fixed band and shrinks while typing, so the thought, the blur and the field all stay on screen above the keyboard. */
.letter .frame { overflow: hidden; margin: 0 0 14px; height: min(26vh, 240px); background: var(--g1); transition: height .2s ease; }
.letter:has(.gate input:focus) .frame { height: 84px; }
.letter .frame .media { margin: 0; width: 100%; height: 100%; object-fit: cover; filter: blur(26px); transform: scale(1.25); }
.letter .frames { display: flex; gap: 2px; margin: 0 0 14px; }
.letter .frames .frame { flex: 1; min-width: 0; margin: 0; }
/* What can't be seen yet, a few lines of it however much there is, fading out so the field under it stays on screen. */
.letter .veil { max-height: 8.2em; overflow: hidden; margin: 0 0 4px; padding: 6px 10px 0; margin-inline: -10px; -webkit-mask-image: linear-gradient(#000 72%, transparent); mask-image: linear-gradient(#000 72%, transparent); transition: max-height .2s ease; }
.letter:has(.gate input:focus) .veil { max-height: 4.4em; }
.letter .veil .held { display: block; width: fit-content; max-width: 100%; padding: 9px 12px; background: var(--g3); border-radius: var(--r); white-space: nowrap; overflow: hidden; }
.letter div.gate > p { margin: 0 0 14px; }
.letter .reveal { position: relative; overflow: hidden; margin: 0 0 14px; background: var(--g1); }
.letter .reveal img { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; object-fit: cover; }
.letter .reveal .smear { filter: blur(26px); transform: scale(1.25); }
.letter .reveal .media { opacity: 0; filter: blur(24px); transition: opacity .25s ease, filter .8s cubic-bezier(.22, 1, .36, 1); }
.letter .reveal.in .media { opacity: 1; filter: blur(0); }
.letter .gate .small { margin-top: 12px; }
.letter .sent { margin: 0 0 14px; }
.letter .signed { margin-top: 28px; }
.letter .signed a { color: var(--fg3); }
/* The same box as the feed's composer. */
.letter .reply { display: flex; align-items: flex-end; gap: 2px; background: var(--g1); border: 1px solid var(--g1); border-radius: var(--r); padding: 0 10px; }
.letter .reply:focus-within { background: var(--bg); border-color: var(--focus); }
.letter .reply textarea { flex: 1; min-width: 0; display: block; margin: 0; padding: 11px 0; font: inherit; font-size: 17px; line-height: 1.4; border: 0; outline: 0; background: transparent; color: inherit; resize: none; field-sizing: content; max-height: 40vh; }
.letter .reply textarea::placeholder { color: var(--fg3); }
.letter .reply button { flex: none; padding: 11px 0 11px 10px; font: inherit; font-size: 15px; background: none; border: 0; color: inherit; cursor: pointer; }
.letter .assistant { margin-top: 28px; }
.letter .assistant p { margin: 0 0 10px; }
.letter .latest { margin-top: 32px; }
.letter .latest h2 { margin: 0 0 10px; font-size: 14px; font-weight: 400; color: var(--fg3); }
.letter .latest a { display: block; padding: 12px 0; border-top: 1px solid var(--g1); color: inherit; text-decoration: none; }
.letter .latest span { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden; font-size: 16px; line-height: 1.4; white-space: pre-wrap; overflow-wrap: anywhere; }
.claim { margin: 0 0 22px; }
.claim p { margin: 0 0 10px; }

/* The magazine look (feed.js `mag`, ?look=magazine): the same feed set like a magazine. No rules between thoughts,
   only space; a picture leads its thought edge to edge. Words stay one size, as everywhere. */
.mag .thread-block { border-bottom: 0; padding: 25px var(--edge); }
.mag .thread-block > .node > .post { display: flex; flex-direction: column; }
.mag .thread-block > .node > .post[data-pic] > .files { order: -1; justify-items: stretch; margin: 0 calc(-1 * var(--edge)) 10px; }
.mag .thread-block > .node > .post[data-pic] > .files > .media, .mag .thread-block > .node > .post[data-pic] > .files > .media img { width: 100%; max-width: none; max-height: none; border-radius: 0; }
.mag .badge[data-handle] { cursor: pointer; }
/* A run of one person's thoughts as one entry: the first three, each greyer than the last. */
.burst { display: block; width: 100%; padding: 25px 0; border: 0; background: none; color: var(--fg); font: inherit; text-align: left; cursor: pointer; }
.burst .by { display: block; min-height: 0; }
.burst .by span:last-child { color: var(--fg3); }
.burst .line { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-top: 8px; }
.burst .line:nth-of-type(3) { color: var(--fg2); }
.burst .line:nth-of-type(4) { color: var(--fg3); }
.burst .rest { display: block; margin-top: 10px; font-size: var(--xs); color: var(--fg3); }
/* Someone's page, pulled up over the feed as a phone's sheet is. A hairline gives it an edge where the shadow can't
   (black on a dimmed black feed). */
.sheet-dim { position: fixed; inset: 0; z-index: 40; background: var(--scrim); opacity: 0; transition: opacity .3s ease; }
.sheet-dim[data-open] { opacity: 1; }
.sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 41; display: flex; flex-direction: column; width: 100%; max-width: 640px; height: min(92dvh, 760px); margin-inline: auto; overflow: hidden; background: var(--bg); border: 1px solid var(--line); border-bottom: 0; border-radius: 12px 12px 0 0; box-shadow: 0 -8px 40px var(--shade); transform: translateY(100%); transition: transform .42s cubic-bezier(.32, .72, 0, 1); }
.sheet[data-open] { transform: none; }
.sheet .grab { flex: none; width: 36px; height: 5px; margin: 6px auto 0; border-radius: 3px; background: var(--g3); }
.sheet-done { flex: none; align-self: flex-end; min-height: 44px; padding: 0 var(--edge); border: 0; background: none; color: var(--fg2); font: inherit; font-size: var(--sm); cursor: pointer; }
.sheet iframe { flex: 1; width: 100%; border: 0; opacity: 0; transition: opacity .2s ease; }
.sheet iframe[data-ready] { opacity: 1; }
.framed .top, .framed .connect-banner { display: none; }
/* The thread beside the editor (public/editor.js): the thought being written is the page beside it, so here it's only
   its line (who, when, who it's to), for the replies to hang under; its words and files aren't said twice. */
.panel .post.focus > .body, .panel .post.focus > .files, .panel .post.focus > .more { display: none; }
.panel #replying { display: none !important; }
@media (prefers-reduced-motion: reduce) { .sheet, .sheet-dim { transition: none; } }

/* Search (public/web/search.js): the magnifier on the top bar's right; tapped, the tabs slide away left and the field
   slides in from the right over the same line. */
.top { position: relative; }
.top .search-open { flex: none; display: grid; place-items: center; width: 30px; height: 30px; margin-left: auto; color: var(--fg2); border-radius: var(--r); }
.top .search-open:hover { color: var(--fg); background: var(--g1); }
.top .search-open + .right { margin-left: 0; padding-left: 4px; }
/* Writing a page (/new), beside search: the same quiet square. */
.top .tabs, .top .search-open, .top .right { transition: transform .28s cubic-bezier(.2, .8, .2, 1), opacity .2s ease; }
.top .search { position: absolute; inset: auto var(--edge) 6px; display: flex; align-items: center; gap: 5px; height: 35px; padding: 0 0 0 10px;
  background: var(--g1); border-radius: var(--r); transform: translateX(24px); opacity: 0; pointer-events: none;
  transition: transform .28s cubic-bezier(.2, .8, .2, 1), opacity .2s ease; }
.top .search .glass { flex: none; color: var(--fg3); width: 17px; height: 17px; }
.top .search input { flex: 1; min-width: 0; height: 100%; border: 0; background: none; color: var(--fg); font: inherit; font-size: var(--md); outline: 0; -webkit-appearance: none; }
.top .search input::-webkit-search-cancel-button { display: none; }
.top[data-searching] .search { transform: none; opacity: 1; pointer-events: auto; }
.top[data-searching] .tabs, .top[data-searching] .search-open, .top[data-searching] .right { transform: translateX(-24px); opacity: 0; pointer-events: none; }
.searching #f, .searching #replying, .searching #connect-banner { display: none; }
.results .found { color: var(--fg3); font-size: var(--xs); margin: 5px 0 10px; }
.results .person { display: block; padding: 6px 0; color: var(--fg); text-decoration: none; }
.results .person span { color: var(--fg3); }
.results .person + .found { margin-top: 12px; }
@media (prefers-reduced-motion: reduce) { .top .search, .top .tabs, .top .search-open, .top .right { transition: none; } }

/* The rail on your own page (public/web/rail.js): your thoughts by who can read them, set like the tabs along the top:
   words in grey, the one you're on in a grey box, how many beside each in the lightest grey. */
.rail { display: flex; gap: 2px; margin: 20px 0 0; overflow-x: auto; scrollbar-width: none; }
.rail::-webkit-scrollbar { display: none; }
.rail button { flex: none; display: inline-flex; align-items: baseline; gap: 5px; padding: 6px 10px; border-radius: var(--r); color: var(--fg2); font-size: var(--sm); white-space: nowrap; }
.rail button:hover { color: var(--fg); background: var(--g1); }
.rail button[aria-selected="true"] { color: var(--fg); background: var(--g1); }
.rail .n { color: var(--fg3); font-size: var(--xs); font-variant-numeric: tabular-nums; }
.rail-who { margin: 5px 0 10px; color: var(--fg3); font-size: var(--xs); }

/* A thread's line (feed.js threadEl): who added how many, to what, when. Then the first thought, "N more", the latest reply. */
.summary { display: flex; align-items: baseline; gap: 5px; width: 100%; margin: 0 0 10px; color: var(--fg2); font-size: var(--sm); text-align: left; }
.summary:hover .what { color: var(--fg); }
.summary .what { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.summary .sep, .summary time { flex: none; color: var(--fg3); }
.answers { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; color: var(--fg3); font-size: var(--xs); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.answers:hover { color: var(--fg2); }

/* A nudge (public/nudge.js): the tooltip's small dark box, kept open over the one thing to do, its point aimed at it.
   It rises in, bobs twice, then stays still. */
.nudge { position: fixed; z-index: 30; padding: 4px 7px; border-radius: var(--r); background: var(--tip-bg); color: var(--tip-fg);
  font-size: var(--xs); line-height: 1.35; white-space: nowrap; pointer-events: none; translate: -50% -100%;
  animation: nudge-in .2s ease-out both, nudge-bob .9s .3s ease-in-out 2; }
.nudge::after { content: ""; position: absolute; top: 100%; left: calc(50% + var(--shift, 0px)); margin-left: -5px; border: 5px solid transparent; border-top-color: var(--tip-bg); }
.nudge[data-gone] { animation: nudge-out .15s ease-in both; }
@keyframes nudge-in { from { opacity: 0; margin-top: 4px; } }
@keyframes nudge-out { to { opacity: 0; } }
@keyframes nudge-bob { 50% { margin-top: -3px; } }
@media (prefers-reduced-motion: reduce) { .nudge { animation: nudge-in .01s both; } }

/* A game's pages (src/prompts): its link's page, a friend's part, and the owner's page with what it opened. */
.prompt header { margin-bottom: 25px; }
.prompt .from { margin: 0 0 8px; font-size: var(--sm); color: var(--fg2); }
.prompt h1 { margin: 0 0 8px; font-size: var(--xl); line-height: 1.25; font-weight: 600; letter-spacing: -0.01em; overflow-wrap: anywhere; }
.prompt .lede { margin: 0; font-size: var(--lg); line-height: 1.35; color: var(--fg2); }
.prompt .gate { display: grid; gap: 10px; margin-top: 20px; }
.prompt .gate > * { margin: 0; }
.prompt .gate .btn svg { width: 18px; height: 18px; flex: none; }
/* The three things to tap in the assistant, once they've gone there: numbered, large enough to read at a glance back. */
.prompt .three { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 4px; counter-reset: s; font-size: var(--lg); }
.prompt .three li { display: grid; grid-template-columns: 26px 1fr; counter-increment: s; }
.prompt .three li::before { content: counter(s); color: var(--fg3); }
.prompt .sub { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; margin: 5px 0 0; font-size: var(--sm); color: var(--fg3); }
.prompt .sub.foot { margin-top: 30px; }
/* Under the play buttons: copy the prompt, centred, its icon beside it. */
.prompt .sub.under { justify-content: center; align-items: center; gap: 4px 20px; margin-top: 8px; }
.prompt .sub.under button { display: inline-flex; align-items: center; gap: 6px; }
.prompt .sub.under .i { width: 13px; height: 13px; }
/* On a phone the buttons that open the game sit at the foot of the screen, nearest the thumb. */
@media (max-width: 700px) {
  .login main:has(> .prompt.thumb) { display: flex; flex-direction: column; min-height: 100dvh; padding-bottom: max(16px, env(safe-area-inset-bottom)); }
  .prompt.thumb { flex: 1; display: flex; flex-direction: column; }
  article.prompt.thumb > .gate:last-child { margin-top: auto; padding-top: 30px; }
}
.prompt .gate.on { margin-top: 30px; padding-top: 25px; border-top: 1px solid var(--line); }
.prompt .made { margin-top: 30px; }
.prompt [hidden] { display: none; }
.prompt .card { margin: 15px 0 0; padding: 20px; border: 1px solid var(--g3); border-radius: var(--r); }
.prompt .card:first-child { margin-top: 0; }
.prompt .gate + .card { margin-top: 25px; }
.prompt .card .label { margin: 0 0 6px; font-size: var(--xs); color: var(--fg3); }
.prompt .card h2 { margin: 0; font-size: var(--lg); line-height: 1.3; font-weight: 600; overflow-wrap: anywhere; }
.prompt .card .lede { margin-top: 6px; font-size: var(--md); color: var(--fg); }
.prompt .card .part { margin-top: 16px; }
.prompt .card .part p:last-child { margin: 0; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
.prompt .card .yes { margin-top: 18px; display: grid; }
.prompt .said { margin: 16px 0 0; font-size: var(--sm); color: var(--fg2); }
.prompt .veiled { filter: blur(7px); user-select: none; pointer-events: none; }
.prompt .box.area textarea { flex: 1; min-width: 0; min-height: 110px; padding: 11px 12px; border: 0; outline: 0; background: transparent; color: var(--fg); font: var(--sm)/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; resize: vertical; overflow-wrap: anywhere; }
.prompt .box.area textarea::placeholder { color: var(--fg3); font-family: var(--font); }
.prompt .saving-line { display: none; margin: 0; color: var(--fg2); }
html.saving .prompt > * { display: none; }
html.saving .prompt > .saving-line { display: block; }
/* Places in a pool: three pictures, shut ones blurred. While they're being found they breathe. */
.prompt .places h1 { margin-bottom: 15px; }
.prompt .places .row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.prompt .place { margin: 0; }
.prompt .place .pic { aspect-ratio: 4 / 5; overflow: hidden; border-radius: var(--r); background: var(--g1); }
.prompt .place img { display: block; width: 100%; height: 100%; object-fit: cover; }
.prompt .place.shut img { filter: blur(14px); transform: scale(1.3); }
.prompt .place figcaption { margin-top: 6px; font-size: var(--xs); color: var(--fg3); }
.prompt .place.open figcaption { color: var(--fg); }
.prompt .places .mute { margin: 12px 0 0; }
.prompt .place.empty .pic { animation: finding 1.6s ease-in-out infinite; }
.prompt .place.empty:nth-child(2) .pic { animation-delay: .2s; }
.prompt .place.empty:nth-child(3) .pic { animation-delay: .4s; }
.prompt .places[data-finding] h1::after { content: "…"; }
.prompt .places:not([data-finding]) .place { animation: found .5s ease both; }
.prompt .places:not([data-finding]) .place:nth-child(2) { animation-delay: .12s; }
.prompt .places:not([data-finding]) .place:nth-child(3) { animation-delay: .24s; }
@keyframes finding { 50% { opacity: .35; } }
@keyframes found { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .prompt .place, .prompt .place .pic { animation: none !important; } }
/* A result's one line as a picture (a game's poster): first on its page, to save and send. Plain words under it. */
.prompt .poster { margin: 0 0 25px; }
.prompt .poster img { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; border: 1px solid var(--g3); border-radius: var(--r); background: #fff; }
.prompt .poster figcaption { margin-top: 8px; gap: 4px 20px; }
.prompt .poster figcaption a, .prompt .poster figcaption button { color: var(--fg2); text-decoration: none; }
.prompt .poster figcaption [data-done] { color: var(--fg); }
.prompt header + .poster { margin-top: 20px; }
/* A game's funnel, for whoever runs it: plain tables, numbers to the right, the first column the step. */
.prompt .answer + .came, .prompt .scroll + .came { margin-top: 35px; }
.prompt .answer { margin: 15px 0 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.prompt .scroll { overflow-x: auto; margin-top: 10px; }
.prompt table.funnel { border-collapse: collapse; font-size: var(--sm); width: 100%; }
.prompt table.funnel th, .prompt table.funnel td { padding: 6px 10px 6px 0; text-align: right; white-space: nowrap; border-bottom: 1px solid var(--line); font-weight: 400; font-variant-numeric: tabular-nums; }
.prompt table.funnel th:first-child { text-align: left; color: var(--fg2); }
.prompt table.funnel thead th { color: var(--fg3); }
.prompt .sub.foot a[aria-current] { color: var(--fg); }
/* A picture: the button is the picker. */
.prompt [data-photo] input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.prompt [data-photo] label.btn { cursor: pointer; }
/* Their own result, folded under what it opened. */
.prompt .yours { margin-top: 25px; }
.prompt .yours summary { cursor: pointer; font-size: var(--sm); color: var(--fg2); list-style: none; }
.prompt .yours summary::-webkit-details-marker { display: none; }
.prompt .yours summary::after { content: " +"; color: var(--fg3); }
.prompt .yours[open] summary::after { content: " −"; }
.prompt .yours .card { margin-top: 12px; }
/* What friends said, a board, and where you stand on it. */
.prompt .came h1 { margin-bottom: 0; }
.prompt .came .mute { margin: 6px 0 0; }
.prompt .standing { display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline; margin: 15px 0 0; font-size: var(--sm); color: var(--fg2); }
.prompt .standing strong { font-size: var(--lg); color: var(--fg); font-weight: 600; }
.prompt .board { list-style: none; margin: 0; padding: 0; }
.prompt .board li { display: flex; gap: 12px; align-items: baseline; padding: 12px 0; border-top: 1px solid var(--line); }
.prompt .board li div { flex: 1; min-width: 0; }
.prompt .board p { margin: 2px 0 0; font-size: var(--sm); color: var(--fg2); }
.prompt .board .rank { width: 1.5em; color: var(--fg3); font-variant-numeric: tabular-nums; }
.prompt .board .score { font-weight: 600; font-variant-numeric: tabular-nums; }
.prompt .board .me strong::after { content: " (you)"; font-weight: 400; color: var(--fg3); }
/* Answering on the page: each question's answers are rows to tap. */
.prompt .quiz fieldset { margin: 8px 0 0; padding: 0; border: 0; display: grid; gap: 6px; min-width: 0; }
.prompt .quiz legend { padding: 0; margin-bottom: 8px; font-weight: 600; line-height: 1.35; }
.prompt .quiz label { display: block; cursor: pointer; }
.prompt .quiz label input { position: absolute; opacity: 0; pointer-events: none; }
.prompt .quiz label span { display: block; padding: 11px 12px; border: 1px solid var(--g3); border-radius: var(--r); line-height: 1.3; }
.prompt .quiz label input:checked + span { border-color: var(--fg); box-shadow: inset 0 0 0 1px var(--fg); }
.prompt .quiz label input:focus-visible + span { border-color: var(--focus); }
/* Every game, at /p. */
/* A week's board by the name its winner gave it. */
.prompt .week { margin: 10px 0 0; font-size: var(--sm); color: var(--fg2); }
.prompt .standing + .week { margin-top: 6px; }
/* A result that is the page: where it came from, small, at its foot, so a screenshot carries the way in. */
.prompt .card .where { margin: 18px 0 0; font-size: var(--xs); color: var(--fg3); }
.prompt .card + .came, .prompt .gate + .came, .prompt .quote + .came, .prompt .list + .came, .prompt .sub + .came { margin-top: 35px; padding-top: 25px; border-top: 1px solid var(--line); }
.prompt .came + .card { margin-top: 15px; }
.prompt .card ~ .came h1, .prompt .quote ~ .came h1, .prompt .gate ~ .came h1, .prompt header ~ .came h1, .prompt .list ~ .came h1, .prompt .board ~ .came h1 { font-size: var(--lg); }
.prompt header + .quote { margin-top: 0; }
/* An answer to an ask: what was asked, their words, whose. Plain, like a thought. */
.prompt .quote { margin: 15px 0 0; padding: 20px; border: 1px solid var(--g3); border-radius: var(--r); }
.prompt .quote .label { margin: 0 0 8px; font-size: var(--xs); color: var(--fg3); }
.prompt .quote blockquote { margin: 0; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
.prompt .quote figcaption { margin-top: 12px; font-size: var(--sm); color: var(--fg2); }
.prompt .quote:target { border-color: var(--fg); }
.prompt .quote .yes { margin-top: 15px; display: grid; gap: 10px; }
/* A list of theirs: each thing's name, their words under it, and the box that adds one. */
.prompt .list { list-style: none; margin: 10px 0 0; padding: 0; }
.prompt .list li { padding: 12px 0; border-top: 1px solid var(--line); overflow-wrap: anywhere; }
.prompt .list li:first-child { border-top: 0; }
.prompt .list p { margin: 2px 0 0; font-size: var(--sm); color: var(--fg2); white-space: pre-wrap; }
.prompt .gate.add { grid-template-columns: 1fr auto; margin-top: 10px; }
.prompt .board li div p .link, .prompt .board li div p button { font-size: var(--sm); }

.prompt .games { display: grid; }
.prompt .games a { display: grid; gap: 2px; padding: 14px 0; border-top: 1px solid var(--line); color: inherit; text-decoration: none; }
.prompt .games span { font-size: var(--sm); color: var(--fg2); }
/* A photo picked for them on joining: the photo is the button that keeps it. */
.invited form.picked { margin: 25px 0 0; }
.invited form.picked button { display: block; padding: 0; border-radius: var(--r); cursor: pointer; }
.invited form.picked button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.invited form.picked .caption { margin: 8px 0 0; font-size: var(--sm); color: var(--fg2); }
.invited .small.centered { text-align: center; }
/* A world someone's asked into, with no Den in their assistant yet: its own chat buttons wait for the numbered taps. */
.game.needs-den .show [data-say] { display: none; }
