/* ===========================================================================
   Mind Universe — an observatory for your own head.

   The whole design rests on one idea: an entry is a star. Thoughts burn amber
   (daylight), dreams burn periwinkle (night), ideas burn cyan (first light).
   Colour is never decorative here — it always tells you which of the three
   you are looking at. The feed hangs off a meridian line with a star at every
   entry, and the Universe view is that same sky, drawn properly.
   =========================================================================== */

:root {
  /* the sky */
  --void:      #0A0B14;
  --night:     #12141F;
  --panel:     #171A28;
  --raised:    #1E2233;
  --haze:      #2A2F49;
  --hairline:  #232741;

  /* light */
  --starlight: #E9E7F2;
  --dust:      #9195AF;
  --faint:     #62667F;

  /* the three kinds */
  --ember:     #F2A25C;   /* thought — daylight */
  --violet:    #9B8CE8;   /* dream   — night */
  --dawn:      #5FD8C4;   /* idea    — first light */

  --ember-dim: color-mix(in srgb, var(--ember) 14%, transparent);
  --violet-dim:color-mix(in srgb, var(--violet) 14%, transparent);
  --dawn-dim:  color-mix(in srgb, var(--dawn) 14%, transparent);

  --danger:        #F2718C;
  --danger-dim:    #4A2A38;
  --danger-bright: #F5A8B8;

  /* recessed surfaces — inputs, empty thumbnails, anything sitting a level
     below the panel it is on */
  --deep:     #0D0F19;
  --deep-2:   #0A0C15;
  /* floating surfaces — modals, menus, toasts, a level above the panel */
  --elevated: #191C2B;
  /* reading text dimmer than --starlight, brighter than --dust */
  --secondary: #CFCDDF;
  /* what the countless subtle overlay tints below are mixed from */
  --wash:      #FFFFFF;
  --haze-bright: #3A4066;

  --rail-1: #10121C;
  --rail-2: #0C0E17;

  --primary-1:       #E9E7F2;
  --primary-2:       #C8C5DC;
  --primary-text:    #10121C;
  --primary-1-hover: #FFFFFF;
  --primary-2-hover: #D8D5E8;

  --display: 'Bodoni Moda', 'Didot', Georgia, serif;
  --body:    'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono:    'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  --rail: 232px;
  --aside: 320px;
  --column: 640px;

  --r-sm: 6px;
  --r-md: 12px;
  --r-lg: 20px;

  --lift: 0 1px 0 #FFFFFF08 inset, 0 12px 32px -18px #000000CC;
}

/* kind-scoped accent, set once per card and inherited by everything inside */
[data-kind="thought"] { --accent: var(--ember);  --accent-dim: var(--ember-dim); }
[data-kind="dream"]   { --accent: var(--violet); --accent-dim: var(--violet-dim); }
[data-kind="idea"]    { --accent: var(--dawn);   --accent-dim: var(--dawn-dim); }

/* ===========================================================================
   Appearance — dark by default, with a daylight room to write in too.

   Not every surface flips. `.universe`, `.landing__sky` and `.crossfig` are
   windows onto the sky itself — the real chart, the made preview on the
   front door, the drawn crossing — and a window stays dark regardless of the
   room it hangs in, the same reasoning that keeps `.nightmode` dark on
   purpose no matter the hour. Photo overlays (`.card__stamp`, the lightbox)
   stay dark too, for the ordinary reason every photo viewer's chrome does:
   it has to sit on top of a photograph of unknown brightness, not a room.
   Everything else — the page, the cards, the rail, the chrome — is the room,
   and the room is what this switches.

   Chosen in Settings, kept in this browser, defaulting to the system's own
   preference — see public/js/scheme.js. An explicit choice always wins:
   `[data-scheme]` on `<html>` overrides the media query in both directions,
   which is why the light palette below is written out twice rather than
   once — a selector cannot be both "this media query" and "this attribute,
   regardless of media query" at the same time.
   =========================================================================== */

:root[data-scheme="light"] {
  --void:      #F3F1F8;
  --night:     #F9F8FC;
  --panel:     #FFFFFF;
  --raised:    #FFFFFF;
  --haze:      #D6D2E6;
  --hairline:  #E9E7F1;

  --starlight: #201D2E;
  --dust:      #635F7C;
  --faint:     #85809C;
  --secondary: #423E58;

  --ember:  #A15A11;
  --violet: #6552CC;
  --dawn:   #0E7F6B;

  --ember-dim:  color-mix(in srgb, var(--ember) 14%, transparent);
  --violet-dim: color-mix(in srgb, var(--violet) 14%, transparent);
  --dawn-dim:   color-mix(in srgb, var(--dawn) 14%, transparent);

  --danger:        #B7304E;
  --danger-dim:    #F2D3DA;
  --danger-bright: #9C2A44;

  --deep:     #EDEAF5;
  --deep-2:   #E3DFEF;
  --elevated: #FFFFFF;
  --wash:     #000000;
  --haze-bright: #B7B0D1;

  --rail-1: #FBFAFE;
  --rail-2: #F2F0F8;

  --primary-1:       #26222F;
  --primary-2:       #17131E;
  --primary-text:    #F8F7FB;
  --primary-1-hover: #3A3549;
  --primary-2-hover: #221E2C;

  --lift: 0 1px 0 #FFFFFF inset, 0 14px 28px -20px #16131E26;
}

@media (prefers-color-scheme: light) {
  /* Applies only when nothing has been chosen explicitly — see is-dark below. */
  :root:not([data-scheme="dark"]) {
    --void:      #F3F1F8;
    --night:     #F9F8FC;
    --panel:     #FFFFFF;
    --raised:    #FFFFFF;
    --haze:      #D6D2E6;
    --hairline:  #E9E7F1;

    --starlight: #201D2E;
    --dust:      #635F7C;
    --faint:     #85809C;
    --secondary: #423E58;

    --ember:  #A15A11;
    --violet: #6552CC;
    --dawn:   #0E7F6B;

    --ember-dim:  color-mix(in srgb, var(--ember) 14%, transparent);
    --violet-dim: color-mix(in srgb, var(--violet) 14%, transparent);
    --dawn-dim:   color-mix(in srgb, var(--dawn) 14%, transparent);

    --danger:        #B7304E;
    --danger-dim:    #F2D3DA;
    --danger-bright: #9C2A44;

    --deep:     #EDEAF5;
    --deep-2:   #E3DFEF;
    --elevated: #FFFFFF;
    --wash:     #000000;
    --haze-bright: #B7B0D1;

    --rail-1: #FBFAFE;
    --rail-2: #F2F0F8;

    --primary-1:       #26222F;
    --primary-2:       #17131E;
    --primary-text:    #F8F7FB;
    --primary-1-hover: #3A3549;
    --primary-2-hover: #221E2C;

    --lift: 0 1px 0 #FFFFFF inset, 0 14px 28px -20px #16131E26;
  }
}

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

/*
 * `hidden` has to actually hide.
 *
 * The browser's own rule is `[hidden] { display: none }`, and any class
 * carrying a display beats it — `.field { display: block }` does, and an
 * inline `display: flex` beats it twice over. So `element.hidden = true` was
 * quietly doing nothing in the write box: "Where it has got to" showed on
 * thoughts and dreams, which have nowhere to have got to, and "Write it
 * unsigned" showed while editing and on private entries, where ticking it
 * has never done anything at all. Both looked like offers and neither was.
 *
 * Found while building the inner space, where the second one mattered most:
 * a room whose whole promise is that nobody sees what is in it is no place
 * for a checkbox about how your name appears in public.
 *
 * The one place !important earns its keep, because the thing it has to beat
 * is an inline style.
 */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--void);
  color: var(--starlight);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-weight: 500; line-height: 1.15; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button, input, textarea, select { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--accent, var(--violet));
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* --- the sky behind everything ------------------------------------------ */

.starfield {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(1100px 700px at 78% -8%, #1D2244 0%, transparent 62%),
    radial-gradient(900px 620px at 8% 102%, #191B36 0%, transparent 60%),
    var(--void);
}
/* The stars here are the chart's stars: the same three kind colours, each a
   small core inside a halo at the chart's own proportions (radius x3.4, faint),
   with a little colourless dust between. They read the kind tokens, so the
   light theme's darker kinds come with them and no second set is needed. */
.starfield::after {
  content: '';
  position: absolute;
  inset: 0;
  opacity: .85;
  background-image:
    radial-gradient(10.4px 10.4px at 12% 22%, color-mix(in srgb, var(--violet) 16%, transparent), transparent),
    radial-gradient(3.1px 3.1px at 12% 22%, var(--violet) 75%, transparent),
    radial-gradient(8.7px 8.7px at 76% 14%, color-mix(in srgb, var(--ember) 16%, transparent), transparent),
    radial-gradient(2.5px 2.5px at 76% 14%, var(--ember) 75%, transparent),
    radial-gradient(9.9px 9.9px at 42% 68%, color-mix(in srgb, var(--dawn) 16%, transparent), transparent),
    radial-gradient(2.9px 2.9px at 42% 68%, var(--dawn) 75%, transparent),
    radial-gradient(7.5px 7.5px at 88% 56%, color-mix(in srgb, var(--violet) 16%, transparent), transparent),
    radial-gradient(2.2px 2.2px at 88% 56%, var(--violet) 75%, transparent),
    radial-gradient(11.6px 11.6px at 28% 88%, color-mix(in srgb, var(--ember) 16%, transparent), transparent),
    radial-gradient(3.4px 3.4px at 28% 88%, var(--ember) 75%, transparent),
    radial-gradient(7.5px 7.5px at 62% 40%, color-mix(in srgb, var(--dawn) 16%, transparent), transparent),
    radial-gradient(2.2px 2.2px at 62% 40%, var(--dawn) 75%, transparent),
    radial-gradient(7.0px 7.0px at 6% 62%, color-mix(in srgb, var(--violet) 16%, transparent), transparent),
    radial-gradient(2.0px 2.0px at 6% 62%, var(--violet) 75%, transparent),
    radial-gradient(8.2px 8.2px at 94% 84%, color-mix(in srgb, var(--ember) 16%, transparent), transparent),
    radial-gradient(2.4px 2.4px at 94% 84%, var(--ember) 75%, transparent),
    radial-gradient(1.7px 1.7px at 20% 48%, var(--faint), transparent),
    radial-gradient(1.7px 1.7px at 54% 10%, var(--faint), transparent),
    radial-gradient(1.7px 1.7px at 70% 78%, var(--faint), transparent),
    radial-gradient(1.7px 1.7px at 34% 30%, var(--faint), transparent),
    radial-gradient(1.7px 1.7px at 84% 32%, var(--faint), transparent),
    radial-gradient(1.7px 1.7px at 50% 92%, var(--faint), transparent);
}

/* The one surface that cannot be reached by swapping tokens: its shape, not
   just its colour, is the night sky. A daylight answer keeps the same two
   glows in the same two corners — this is still the sky the app is named
   for — but traded for warmth instead of depth, and the stars are the same
   kind-coloured ones, darkened by the tokens, not inverted into black
   specks on white, which reads as grime rather than as a sky. */
:root[data-scheme="light"] .starfield {
  background:
    radial-gradient(1100px 700px at 78% -8%, #F2A25C1F 0%, transparent 62%),
    radial-gradient(900px 620px at 8% 102%, #5FD8C41F 0%, transparent 60%),
    var(--void);
}
@media (prefers-color-scheme: light) {
  :root:not([data-scheme="dark"]) .starfield {
    background:
      radial-gradient(1100px 700px at 78% -8%, #F2A25C1F 0%, transparent 62%),
      radial-gradient(900px 620px at 8% 102%, #5FD8C41F 0%, transparent 60%),
      var(--void);
  }
}

/* A handful of surfaces are deliberately always dark, whatever the room's own
   scheme: a window onto the sky (`.universe`, `.landing__sky`, `.crossfig`),
   a chip or a viewer sitting over a photograph of unknown brightness
   (`.card__figure`, `.lightbox`), and the middle of the night (`.nightmode`,
   `.night`) is dark on principle, at 3am or at noon — see the comment above
   `body.nightmode` further down. Their own hardcoded backgrounds already say
   so; what this re-pins is everything *inside* them that reads a token —
   `.universe__hint`, `.lightbox__close`, a kind colour on a node in the
   chart — so light-theme text does not go looking for contrast against a
   pale room and land on a dark window instead. */
.universe,
.landing__sky,
.crossfig,
.card__figure,
.lightbox,
.nightmode,
.night {
  --void:      #0A0B14;
  --night:     #12141F;
  --panel:     #171A28;
  --raised:    #1E2233;
  --haze:      #2A2F49;
  --hairline:  #232741;
  --starlight: #E9E7F2;
  --dust:      #9195AF;
  --faint:     #62667F;
  --secondary: #CFCDDF;
  --ember:  #F2A25C;
  --violet: #9B8CE8;
  --dawn:   #5FD8C4;
  --wash:   #FFFFFF;
}

/* --- boot ---------------------------------------------------------------- */

/* The container grows past 100dvh rather than being held at it, so the pitch
   inside can be taller than a short phone without its top being cut off by the
   centring. */
.app-loading { display: grid; place-items: center; min-height: 100dvh; padding: 40px 22px; }
.boot { position: relative; z-index: 1; text-align: center; }
.boot__mark {
  width: 46px; height: 46px; margin: 0 auto 18px;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, var(--dawn), var(--violet) 60%, transparent 72%);
  animation: pulse 2.4s ease-in-out infinite;
}
.boot__text { font-family: var(--mono); font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--faint); }
@keyframes pulse { 0%, 100% { opacity: .45; transform: scale(.94); } 50% { opacity: 1; transform: scale(1); } }

/* --- first light: the pitch, in front of the boot screen ------------------ */

/*
 * What is in public/index.html itself, so it is read before any JavaScript has
 * run and by anything that never runs JavaScript at all. It is thrown away the
 * moment the app renders, so nothing here is ever seen beside the real page.
 *
 * position/z-index for the reason tests/stacking.test.js exists: the starfield
 * is opaque and positioned, so anything that forgets this is painted over
 * perfectly and invisibly.
 */
.firstlight {
  position: relative; z-index: 1;
  width: 100%; max-width: 44ch;
  text-align: center;
}
.firstlight__brand { justify-content: center; margin-bottom: 26px; }

.firstlight__title {
  font-family: var(--display);
  font-size: clamp(32px, 7.4vw, 46px);
  line-height: 1.06;
  letter-spacing: -.01em;
  margin: 0 0 16px;
  text-wrap: balance;
}
.firstlight__title em { font-style: italic; }

.firstlight__lede {
  font-size: 16.5px; line-height: 1.6; color: var(--dust);
  margin: 0 auto 28px; max-width: 40ch;
  text-wrap: pretty;
}

.firstlight__three {
  list-style: none; margin: 0 0 40px; padding: 0;
  display: grid; gap: 10px;
}
.firstlight__three li {
  display: flex; gap: 11px; align-items: baseline;
  padding: 11px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  text-align: left;
}
.firstlight__three i {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  transform: translateY(1px);
}
.firstlight__three strong { font-size: 14px; font-weight: 600; flex: none; }
.firstlight__three span { font-size: 13px; color: var(--faint); }

@media (min-width: 700px) {
  .firstlight { max-width: 52ch; }
  .firstlight__three { grid-template-columns: repeat(3, 1fr); }
  .firstlight__three li { flex-direction: column; gap: 7px; align-items: flex-start; }
}

/* --- shell --------------------------------------------------------------- */

.shell {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  min-height: 100dvh;
}

.rail {
  position: sticky;
  top: 0;
  height: 100dvh;
  padding: 26px 18px 22px;
  border-right: 1px solid var(--hairline);
  background: linear-gradient(180deg, var(--rail-1) 0%, var(--rail-2) 100%);
  display: flex;
  flex-direction: column;
  gap: 22px;
  /* Someone who writes about a lot of things can fill this past the height of
     the screen — "What you write about" alone can run to eight tags. Without
     this the overflow does not scroll, it just keeps drawing past the bottom
     of the rail and over whatever the page has put there instead. */
  overflow-y: auto;
  overscroll-behavior: contain;
}

.brand { display: flex; align-items: center; gap: 11px; padding: 0 6px; }
.brand__glyph { width: 30px; height: 30px; flex: none; }
.brand__name {
  font-family: var(--display);
  font-size: 21px;
  letter-spacing: .01em;
}
.brand__name em { font-style: italic; }

.nav { display: flex; flex-direction: column; gap: 2px; }
.nav__item {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px;
  border-radius: var(--r-md);
  color: var(--dust);
  font-size: 14.5px;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid transparent;
  background: none;
  width: 100%;
  text-align: left;
  transition: color .16s, background .16s;
}
.nav__item:hover { color: var(--starlight); background: color-mix(in srgb, var(--wash) 3%, transparent); }
.nav__item[aria-current="page"] {
  color: var(--starlight);
  background: color-mix(in srgb, var(--wash) 4%, transparent);
  border-color: var(--hairline);
}
.nav__item svg { width: 17px; height: 17px; flex: none; opacity: .85; }

.rail__spacer { flex: 1; }

/* Offered only when the browser says it can be installed, so it is usually
   not there at all. */
.rail__install { margin-bottom: 10px; }

/* Reachable, without taking a place in the main run of nav items. */
.nav__item--quiet { font-size: 13.5px; color: var(--faint); margin-bottom: 8px; }
.nav__item--quiet svg { width: 15px; height: 15px; }
.install { justify-content: center; }

.rail__section {
  border-top: 1px solid var(--hairline);
  padding-top: 16px;
}
.rail__label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 10px;
  padding: 0 6px;
}

.themelist { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 4px; }

.me {
  display: flex; align-items: center; gap: 10px;
  padding: 8px; border-radius: var(--r-md);
  cursor: pointer; border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--wash) 2%, transparent); width: 100%; text-align: left;
}
.me:hover { background: color-mix(in srgb, var(--wash) 5%, transparent); }
.me__name { font-size: 14px; font-weight: 600; line-height: 1.2; }
.me__handle { font-family: var(--mono); font-size: 11px; color: var(--faint); }

/* --- main ---------------------------------------------------------------- */

.main {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  padding: 0 28px 120px;
}
.main--split {
  grid-template-columns: minmax(0, var(--column)) var(--aside);
  gap: 40px;
  justify-items: stretch;
  justify-content: center;
}
.column { width: 100%; max-width: var(--column); }

.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 16px;
  padding: 18px 0 14px;
  margin-bottom: 6px;
  background: linear-gradient(180deg, var(--void) 62%, transparent);
}
.topbar > :first-child { min-width: 0; }
.topbar h1 {
  font-family: var(--display);
  font-size: 30px;
  letter-spacing: .005em;
  overflow-wrap: anywhere;
  /* A search term or a theme name can be any length; the bar is sticky, so it
     is not allowed to eat the screen. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.topbar__sub {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 4px;
}
.topbar__actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }

/* --- controls ------------------------------------------------------------ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 15px;
  border-radius: 999px;
  border: 1px solid var(--haze);
  background: color-mix(in srgb, var(--wash) 3%, transparent);
  color: var(--starlight);
  font-size: 13.5px; font-weight: 600;
  cursor: pointer;
  transition: background .16s, border-color .16s, transform .16s, opacity .16s;
  white-space: nowrap;
}
.btn:hover:not(:disabled) { background: color-mix(in srgb, var(--wash) 8%, transparent); border-color: var(--haze-bright); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn svg { width: 15px; height: 15px; }

.btn--primary {
  background: linear-gradient(180deg, var(--primary-1), var(--primary-2));
  color: var(--primary-text);
  border-color: transparent;
}
.btn--primary:hover:not(:disabled) { background: linear-gradient(180deg, var(--primary-1-hover), var(--primary-2-hover)); }

.btn--ghost { background: none; border-color: transparent; color: var(--dust); }
.btn--ghost:hover:not(:disabled) { color: var(--starlight); background: color-mix(in srgb, var(--wash) 4%, transparent); }

.btn--danger { color: var(--danger); border-color: var(--danger-dim); }
.btn--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 8%, transparent); }

.btn--sm { padding: 6px 12px; font-size: 12.5px; }
.btn--block { width: 100%; }

.field { display: block; margin-bottom: 14px; }
.field__label {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint); margin-bottom: 7px;
}
.input, .textarea, .select {
  width: 100%;
  /* Date and time inputs have a wide intrinsic size; without this they refuse
     to shrink and push the whole dialog past the edge of a phone screen. */
  min-width: 0;
  padding: 11px 13px;
  background: var(--deep);
  border: 1px solid var(--haze);
  border-radius: var(--r-md);
  color: var(--starlight);
  transition: border-color .16s, background .16s;
}
.input:focus, .textarea:focus, .select:focus {
  outline: none;
  border-color: var(--accent, var(--violet));
  background: var(--deep-2);
}
.textarea { resize: vertical; min-height: 108px; line-height: 1.65; }
.input::placeholder, .textarea::placeholder { color: var(--faint); }
.field__hint { font-size: 12.5px; color: var(--faint); margin-top: 6px; }

/* Two fields side by side, stacking when there is no room for both. */
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 540px) { .pair { grid-template-columns: 1fr; } }

.search {
  position: relative;
  flex: 1;
  max-width: 260px;
}
.search .input { padding-left: 34px; border-radius: 999px; }
.search svg {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--faint); pointer-events: none;
}

/* the preview under the search box: a few people, a few entries, a way out */
.searchdrop {
  /* Wider than the box it hangs from: an entry needs more room to be
     recognised than a name does. Anchored right, since the box sits right. */
  position: absolute;
  top: 100%; left: auto; right: 0;
  width: min(380px, calc(100vw - 32px));
  z-index: 30;
  margin-top: 6px;
  padding: 6px;
  display: none;
  max-height: min(60vh, 460px);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--haze);
  border-radius: var(--r-md);
  box-shadow: 0 20px 50px -20px #000;
}
.searchdrop__label {
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--faint);
  padding: 9px 8px 5px;
}
.searchdrop__label:first-child { padding-top: 4px; }
.searchdrop__row {
  width: 100%;
  background: none;
  border: none;
  border-radius: var(--r-sm);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  padding: 7px 8px;
}
.searchdrop__row:hover { background: color-mix(in srgb, var(--wash) 5%, transparent); }
.searchdrop .person + .person { border-top: none; }

.searchdrop__entry { display: flex; align-items: center; gap: 10px; }
.searchdrop__entry svg { flex: none; }
.searchdrop__text { min-width: 0; flex: 1; }
.searchdrop__line {
  display: block;
  font-size: 13.5px;
  line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.searchdrop__by {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--faint);
  margin-top: 2px;
}
.searchdrop__none { color: var(--faint); font-size: 13px; padding: 10px; }
.searchdrop__all {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%;
  margin-top: 6px;
  padding: 10px 9px;
  border: none;
  border-top: 1px solid var(--hairline);
  background: none;
  color: var(--starlight);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.searchdrop__all:hover { color: var(--violet); }
.searchdrop__all svg { transform: rotate(180deg); flex: none; opacity: .7; }

/* --- kind chooser -------------------------------------------------------- */

.kinds { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; margin-bottom: 16px; }
.kind {
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 13px 10px 12px;
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: var(--deep);
  cursor: pointer;
  text-align: center;
  transition: border-color .16s, background .16s;
}
.kind:hover { border-color: var(--haze); }
.kind[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-dim);
}
.kind__star { display: block; width: 15px; height: 15px; margin: 0 auto 7px; }
.kind__name { font-size: 13.5px; font-weight: 600; }
.kind__note { font-size: 11.5px; color: var(--faint); margin-top: 2px; }

/* --- the meridian: a hairline down the feed, one star per entry ---------- */

.feed { position: relative; padding-left: 34px; }
.feed::before {
  content: '';
  position: absolute;
  left: 7px; top: 12px; bottom: 40px;
  width: 1px;
  background: linear-gradient(180deg, transparent, var(--hairline) 8%, var(--hairline) 92%, transparent);
}

.entry { position: relative; margin-bottom: 30px; }

.entry__node {
  position: absolute;
  left: -34px; top: 15px;
  width: 15px; height: 15px;
  display: grid; place-items: center;
}
.entry__node::before {
  content: '';
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--void), 0 0 14px 1px var(--accent);
}
.entry__node--bright::before { width: 10px; height: 10px; box-shadow: 0 0 0 4px var(--void), 0 0 22px 3px var(--accent); }

.card {
  background: linear-gradient(180deg, var(--panel), var(--night));
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--lift);
}

.card__head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px 12px;
}
.card__who { min-width: 0; }
.card__name { font-size: 14px; font-weight: 600; line-height: 1.25; }
.card__name:hover { text-decoration: underline; }
.card__meta {
  font-family: var(--mono);
  font-size: 11px; color: var(--faint);
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
}
.card__kind { color: var(--accent); text-transform: uppercase; letter-spacing: .1em; }
.card__tools { margin-left: auto; display: flex; gap: 2px; }

.card__title {
  font-family: var(--display);
  font-size: 24px;
  line-height: 1.2;
  padding: 0 16px 8px;
}
.card__body {
  padding: 0 16px 14px;
  color: var(--secondary);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.card__body--clamped {
  display: -webkit-box;
  -webkit-line-clamp: 8;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card__figure { position: relative; background: var(--deep-2); border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); }
.card__figure img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; }
.card__stamp {
  position: absolute; left: 10px; bottom: 10px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  padding: 4px 8px; border-radius: 999px;
  background: #0A0C15CC; color: var(--dust);
  backdrop-filter: blur(6px);
  border: 1px solid #FFFFFF14;
}

.tags { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 16px 12px; }
.tag {
  font-family: var(--mono);
  font-size: 11px;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  color: var(--dust);
  cursor: pointer;
}
.tag:hover { color: var(--starlight); border-color: var(--haze); }
.tag--accent { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-dim); }

.card__foot {
  display: flex; align-items: center; gap: 4px;
  padding: 10px 12px;
  border-top: 1px solid var(--hairline);
}

.act {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px;
  border-radius: 999px;
  border: none; background: none;
  color: var(--dust);
  font-size: 12.5px; font-weight: 500;
  cursor: pointer;
  transition: color .16s, background .16s;
}
.act:hover { color: var(--starlight); background: color-mix(in srgb, var(--wash) 4%, transparent); }
.act svg { width: 15px; height: 15px; }
.act__label { white-space: nowrap; }
.act[aria-pressed="true"] { color: var(--accent); }
.act[aria-pressed="true"] svg { fill: var(--accent); }

/* --- what the AI can do right now ---------------------------------------- */

.aistate {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 11px;
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  font-size: 12px;
  line-height: 1.45;
  color: var(--dust);
  margin-bottom: 18px;
}
.aistate__dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-top: 5px;
  background: var(--faint);
}
.aistate--ok .aistate__dot { background: var(--dawn); box-shadow: 0 0 8px color-mix(in srgb, var(--dawn) 67%, transparent); }
.aistate--warn .aistate__dot { background: var(--ember); box-shadow: 0 0 8px color-mix(in srgb, var(--ember) 67%, transparent); }
.aistate--warn { border-color: color-mix(in srgb, var(--ember) 20%, transparent); background: color-mix(in srgb, var(--ember) 5%, transparent); }

.rail .aistate { margin-bottom: 0; }

/* --- comments: they open under the entry, never on a page of their own --- */

.comments {
  border-top: 1px solid var(--hairline);
  background: var(--night);
}

.comments__list { padding: 4px 0; }

.comments__empty,
.comments__signin {
  padding: 13px 16px;
  font-size: 13px;
  color: var(--faint);
}

.comment {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 16px;
}
.comment + .comment { border-top: 1px solid color-mix(in srgb, var(--wash) 3%, transparent); }
.comment__avatar { flex: none; margin-top: 1px; }
.comment__body { flex: 1; min-width: 0; }

.comment__text {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--secondary);
  overflow-wrap: anywhere;
}
.comment__name {
  font-weight: 600;
  color: var(--starlight);
  margin-right: 2px;
}
.comment__name:hover { text-decoration: underline; }

.comment__meta {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--faint);
  margin-top: 3px;
}

.comment__remove {
  flex: none;
  background: none;
  border: none;
  color: var(--faint);
  cursor: pointer;
  padding: 4px;
  border-radius: var(--r-sm);
  opacity: 0;
  transition: opacity .16s, color .16s;
}
.comment:hover .comment__remove,
.comment__remove:focus-visible { opacity: 1; }
.comment__remove:hover { color: var(--danger); }

/* The write row sits at the bottom of the card, like a chat composer. */
.comments__write {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 11px 16px 13px;
  border-top: 1px solid var(--hairline);
}
.comments__write .avatar { flex: none; margin-bottom: 3px; }

.comments__input {
  flex: 1;
  min-width: 0;
  resize: none;
  overflow-y: auto;
  max-height: 130px;
  padding: 8px 0;
  background: none;
  border: none;
  color: var(--starlight);
  font-size: 13.5px;
  line-height: 1.5;
}
.comments__input:focus { outline: none; }
.comments__input::placeholder { color: var(--faint); }

.comments__send {
  flex: none;
  background: none;
  border: none;
  color: var(--accent, var(--violet));
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  padding: 8px 4px;
  transition: opacity .16s;
}
.comments__send:disabled { opacity: .35; cursor: default; }
.comments__send:not(:disabled):hover { text-decoration: underline; }

/* Touch screens have no hover, so the delete control is always visible. */
@media (hover: none) {
  .comment__remove { opacity: .6; }
}

/* --- the reading: what the app noticed about one entry ------------------- */

.reading {
  margin: 0 16px 14px;
  padding: 13px 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--accent-dim);
  background:
    linear-gradient(180deg, var(--accent-dim), transparent),
    var(--deep);
}
.reading__label {
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent);
  display: flex; align-items: center; gap: 7px;
  margin-bottom: 8px;
}
.reading__text { font-size: 14px; color: var(--secondary); }
.reading__questions { margin: 10px 0 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.reading__questions li {
  font-size: 13.5px; color: var(--dust);
  padding-left: 15px; position: relative;
}
.reading__questions li::before {
  content: '';
  position: absolute; left: 3px; top: 9px;
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--accent); opacity: .8;
}
.reading__source {
  font-family: var(--mono); font-size: 10px; color: var(--faint);
  margin-top: 10px; letter-spacing: .06em;
}

/* --- aside --------------------------------------------------------------- */

.aside { display: flex; flex-direction: column; gap: 18px; padding-top: 76px; }
.aside__panel {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--panel), var(--night));
  padding: 16px;
}
.aside__title {
  font-family: var(--display);
  font-size: 19px;
  margin-bottom: 3px;
}
.aside__note { font-size: 13px; color: var(--faint); margin-bottom: 13px; }

.person { display: flex; align-items: center; gap: 10px; padding: 8px 0; }
.person + .person { border-top: 1px solid var(--hairline); }
.person__who { min-width: 0; flex: 1; }
.person__name { font-size: 13.5px; font-weight: 600; line-height: 1.25; }
.person__why { font-family: var(--mono); font-size: 10.5px; color: var(--faint); }

/* --- the bell -----------------------------------------------------------
   A count on the rail, and one row per thing that happened. Unread rows are
   marked twice over — a dot and a lit background — because a colour alone is
   not something everyone can read. */

.nav__badge, .tabbar__badge {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1;
  color: var(--void);
  background: var(--violet);
  border-radius: 999px;
  min-width: 18px;
  padding: 4px 5px;
  text-align: center;
}
.nav__badge { margin-left: auto; }

/* --- the switch between two views of one thing ------------------------------
   Sits under the title on My universe / Inner space and on Star chart /
   Threads. The current half is marked by fill and by aria-current, not by
   colour alone. */
.viewswitch {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  margin: 0 0 16px;
  background: var(--deep);
  border: 1px solid var(--hairline);
  border-radius: 999px;
}
.viewswitch__item {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: none; border-radius: 999px;
  color: var(--faint);
  font: inherit; font-size: 13px;
  padding: 6px 14px; cursor: pointer;
}
.viewswitch__item:hover { color: var(--starlight); }
.viewswitch__item[aria-current="page"] {
  background: var(--haze);
  color: var(--starlight);
}
.viewswitch__item:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }

.tabbar__item { position: relative; }
.tabbar__badge {
  position: absolute;
  top: -1px;
  left: calc(50% + 4px);
  box-shadow: 0 0 0 3px var(--deep);
}

.notif {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 14px 12px;
  border: 1px solid transparent;
  border-bottom-color: var(--hairline);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background .16s;
}
.notif:hover { background: color-mix(in srgb, var(--wash) 3%, transparent); }
.notif--unread {
  background: linear-gradient(90deg, color-mix(in srgb, var(--violet) 7%, transparent), transparent 70%);
  border-radius: var(--r-md) var(--r-md) 0 0;
}
.notif__mark {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--violet);
  margin-top: 15px;
  flex: none;
}
.notif--read .notif__mark { background: transparent; }
.notif__who { min-width: 0; flex: 1; }
.notif__line { font-size: 14px; line-height: 1.45; }
.notif__line strong { font-weight: 600; }
.notif__line span { color: var(--dust); }
.notif__quote {
  margin-top: 6px;
  font-size: 13.5px;
  color: var(--secondary);
  border-left: 2px solid var(--haze);
  padding-left: 10px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.notif__when {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .06em;
  color: var(--faint);
  margin-top: 6px;
  display: block;
}
/* Following back from the bell: one tap from "they followed you" to being able
   to message each other. */
.notif__follow { flex: none; align-self: center; }
.notif__followed {
  flex: none; align-self: center;
  font-family: var(--mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--dawn);
}

.notif__thumb {
  width: 46px; height: 46px; flex: none;
  border-radius: var(--r-sm);
  object-fit: cover;
  border: 1px solid var(--hairline);
}
.notif__star { display: grid; place-items: center; width: 46px; flex: none; padding-top: 4px; }

/* The mood somebody typed when they wrote it, in their own words. */
.card__mood { color: var(--accent); font-style: italic; }

/* Picked up where you left off. */
.draftnote {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 14px;
  padding: 9px 12px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  font-size: 12.5px;
  color: var(--dust);
}
.draftnote button {
  margin-left: auto;
  background: none; border: none; padding: 0;
  color: var(--faint); font: inherit; font-size: 12px;
  cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.draftnote button:hover { color: var(--starlight); }

/* --- the month, as squares ------------------------------------------------
   Empty days are drawn rather than left out: the gaps are as much of the
   picture as the marks, and a grid with holes in it says something a list
   never can. */

.cal {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--panel), var(--night));
  padding: 14px 16px 12px;
  margin-bottom: 16px;
}
.cal__head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.cal__toggle {
  display: flex; align-items: center; gap: 8px;
  background: none; border: none; padding: 0;
  color: var(--dust);
  font: inherit; font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer;
}
.cal__toggle:hover { color: var(--starlight); }
.cal__toggle svg { opacity: .7; }
.cal__toggle::after { content: '+'; font-family: var(--body); font-size: 14px; margin-left: 2px; }
.cal.is-open .cal__toggle::after { content: '−'; }

.cal__nav { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.cal__month {
  font-size: 13px; color: var(--starlight);
  min-width: 9.5em; text-align: center;
}
.cal__arrow {
  display: grid; place-items: center;
  width: 26px; height: 26px;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: none; color: var(--faint); cursor: pointer;
}
.cal__arrow:hover:not(:disabled) { color: var(--starlight); border-color: var(--hairline); }
.cal__arrow:disabled { opacity: .25; cursor: default; }
.cal__arrow--next svg { transform: rotate(180deg); }

.cal__grid,
.cal__summary { display: none; }
.cal.is-open .cal__grid { display: grid; }
.cal.is-open .cal__summary { display: block; }

.cal__grid {
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  margin-bottom: 10px;
}
.cal__dayname {
  text-align: center;
  font-family: var(--mono); font-size: 9.5px; color: var(--faint);
  padding-bottom: 4px;
}
.cal__pad { aspect-ratio: 1; }
.cal__day {
  position: relative;
  aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  color: var(--faint);
  font-family: var(--mono); font-size: 11px;
  padding: 0;
}
.cal__day.is-today { border-color: var(--haze); color: var(--starlight); }
.cal__day.has-entries {
  background: color-mix(in srgb, var(--wash) 5%, transparent);
  color: var(--starlight);
  cursor: pointer;
}
.cal__day.has-entries:hover { background: color-mix(in srgb, var(--wash) 9%, transparent); }
.cal__marks {
  position: absolute; left: 0; right: 0; bottom: 4px;
  display: flex; justify-content: center; gap: 2px;
}
.cal__mark { width: 4px; height: 4px; border-radius: 50%; display: block; }

.cal__summary {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  color: var(--faint);
}

/* --- how you have been ----------------------------------------------------
   Bars above and below a line: above is lighter than neutral, below heavier.
   Two directions rather than five colours, so the picture can be read without
   first learning a key — and so it does not fight the three colours the kinds
   already own. */

.mood {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--panel), var(--night));
  padding: 14px 16px 12px;
  margin-bottom: 20px;
}
.mood__head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.mood__toggle {
  display: flex; align-items: center; gap: 8px;
  background: none; border: none; padding: 0;
  color: var(--dust);
  font: inherit; font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer;
}
.mood__toggle:hover { color: var(--starlight); }
.mood__toggle svg { opacity: .7; }
.mood__toggle::after { content: '+'; font-family: var(--body); font-size: 14px; margin-left: 2px; }
.mood.is-open .mood__toggle::after { content: '−'; }

.mood__ranges { display: flex; gap: 4px; margin-left: auto; }
.mood__range {
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 3px 9px;
  color: var(--faint);
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  cursor: pointer;
}
.mood__range:hover { color: var(--starlight); }
.mood__range[aria-pressed="true"] {
  color: var(--starlight);
  border-color: var(--hairline);
  background: color-mix(in srgb, var(--wash) 3%, transparent);
}

.mood__ribbon {
  display: flex; align-items: center; gap: 3px;
  height: 54px;
  margin-bottom: 10px;
  /* the line entries sit above or below */
  background: linear-gradient(var(--hairline), var(--hairline)) center / 100% 1px no-repeat;
}
/* The fill is positioned from the centre line outwards. Percentage padding
   would have resolved against the ribbon's WIDTH, not its height, which made
   every bar hundreds of pixels of padding inside a 54px strip. */
.mood__bar {
  flex: 1; min-width: 3px;
  height: 100%;
  position: relative;
}
.mood__fill {
  position: absolute;
  left: 0; right: 0;
  bottom: 50%;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(180deg, #F2D9A8, #C9A46A);
}
.mood__bar.is-down .mood__fill {
  bottom: auto;
  top: 50%;
  border-radius: 0 0 2px 2px;
  background: linear-gradient(180deg, #6E74A0, #4A4F7A);
}

.mood__summary { font-size: 14px; color: var(--dust); }
.mood__summary strong { color: var(--starlight); font-weight: 600; }

.mood__more { display: none; }
.mood.is-open .mood__more { display: block; }
.mood__section { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--hairline); }
.mood__label {
  font-family: var(--mono); font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 9px;
}
.mood__words { display: flex; flex-wrap: wrap; gap: 6px; }
.mood__word {
  padding: 4px 10px;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  font-size: 12.5px;
  color: var(--secondary);
}
.mood__leanings { display: flex; flex-direction: column; gap: 6px; }
.mood__lean {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 8px 11px;
  border: 1px solid var(--hairline);
  border-left-width: 3px;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  color: inherit; font: inherit; text-align: left;
  cursor: pointer;
}
.mood__lean:hover { background: color-mix(in srgb, var(--wash) 5%, transparent); }
.mood__lean[data-lean="lighter"] { border-left-color: #C9A46A; }
.mood__lean[data-lean="heavier"] { border-left-color: #6E74A0; }
.mood__leanname { font-size: 13.5px; flex: 1; min-width: 0; }
.mood__leanword { font-size: 12px; color: var(--faint); flex: none; }
.mood__leancount {
  font-family: var(--mono); font-size: 10px; color: var(--faint);
  flex: none; min-width: 18px; text-align: right;
}
.mood__caveat {
  margin-top: 14px;
  font-size: 11.5px; line-height: 1.5; color: var(--faint);
}

/* --- getting started -----------------------------------------------------
   Three things, ticking themselves off. It leaves for good once they are all
   done, so it never becomes furniture. */

.started {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--panel), var(--night));
  padding: 16px 18px 8px;
  margin-bottom: 18px;
  box-shadow: var(--lift);
}
.started__head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 12px; }
.started__title { font-family: var(--display); font-size: 20px; }
.started__note {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint); margin-top: 3px;
}
.started__close {
  margin-left: auto; flex: none;
  background: none; border: none; color: var(--faint);
  cursor: pointer; padding: 4px; border-radius: var(--r-sm);
}
.started__close:hover { color: var(--starlight); }

.started__list { list-style: none; margin: 0; padding: 0; }
.started__step {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0;
  border-top: 1px solid var(--hairline);
}
.started__tick {
  width: 20px; height: 20px; flex: none;
  border-radius: 50%;
  border: 1px solid var(--haze);
  display: grid; place-items: center;
  color: var(--void);
}
.started__step.is-done .started__tick { background: var(--dawn); border-color: var(--dawn); }
.started__body { flex: 1; min-width: 0; }
.started__body strong { display: block; font-size: 14px; }
.started__body span { font-size: 12.5px; color: var(--faint); }
.started__step.is-done .started__body strong { color: var(--faint); text-decoration: line-through; }
.started__step.is-done .started__body span { display: none; }
.started__doneword {
  font-family: var(--mono); font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dawn); flex: none;
}

/* --- the empty room -------------------------------------------------------
   Explore with nothing on it is a quiet page, and quiet is all it should be.
   The panel that used to stand here announced how new the place was; it is
   gone for good, and its styles with it. The three ways in stay: they are an
   invitation rather than a status report, and they go the moment there is a
   feed to show instead. */

/* The three kinds, offered as three ways to start. Laid out as tiles rather
   than a row of buttons because each one carries its own sentence, and the
   sentence is the half that says what this app is for. */

.waysin { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.waysin__row {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px;
  width: 100%;
}
.waysin__one {
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
  text-align: left;
  padding: 12px 13px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--raised);
  color: inherit;
  font: inherit;
}
button.waysin__one { cursor: pointer; transition: border-color .15s, background .15s; }
button.waysin__one:hover { border-color: var(--haze); background: var(--panel); }
button.waysin__one[data-kind="thought"]:hover { border-color: var(--ember); }
button.waysin__one[data-kind="dream"]:hover   { border-color: var(--violet); }
button.waysin__one[data-kind="idea"]:hover    { border-color: var(--dawn); }
.waysin__star { display: block; line-height: 0; }
.waysin__name { font-size: 14px; font-weight: 500; }
.waysin__note { font-size: 12px; color: var(--faint); line-height: 1.45; }

/* Inside an empty feed, which is centred and much wider than this wants to be. */
.empty .waysin { max-width: 520px; margin: 0 auto; }

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

/* --- the moment after the first entry -------------------------------------
   Shown once, in a dialog, and every line of it describes something that has
   already happened rather than something that will. */

.firstmoment__lede { font-size: 14px; color: var(--dust); line-height: 1.6; margin-bottom: 16px; }
.firstmoment__list { list-style: none; margin: 0; padding: 0; }
.firstmoment__step {
  display: flex; gap: 13px; align-items: flex-start;
  padding: 14px 0;
  border-top: 1px solid var(--hairline);
}
.firstmoment__mark {
  width: 26px; height: 26px; flex: none;
  display: grid; place-items: center;
  color: var(--dust);
}
.firstmoment__body {
  min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
}
.firstmoment__body strong { font-size: 14px; }
.firstmoment__body > span { font-size: 13px; color: var(--dust); line-height: 1.6; }
.firstmoment__body > span.firstmoment__aside { font-size: 12px; color: var(--faint); }

/* --- today: the run of days, the question, the look back ------------------
   Three small things at the top of Home. They have to earn their space above
   the feed, so each is one line tall and none of them shouts. */

.today { display: flex; flex-direction: column; gap: 10px; margin-bottom: 22px; }

.today__line {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 0 2px;
}
.today__streak { display: flex; align-items: center; gap: 7px; font-size: 14px; }
.today__flame { color: var(--ember); font-size: 15px; line-height: 1; }
.today__note { font-size: 13px; color: var(--faint); }

.today__ask {
  display: flex; align-items: center; gap: 14px;
  width: 100%;
  padding: 15px 16px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--panel), var(--night));
  cursor: pointer;
  text-align: left;
  transition: border-color .16s, transform .12s;
}
.today__ask:hover { border-color: var(--haze); }
.today__ask:active { transform: scale(.995); }
.today__question {
  flex: 1; min-width: 0;
  font-family: var(--display);
  font-size: 19px;
  line-height: 1.3;
}
.today__go {
  display: flex; align-items: center; gap: 5px;
  flex: none;
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint);
}
.today__go svg { transform: rotate(180deg); }
.today__ask:hover .today__go { color: var(--starlight); }

.lookback {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 10px 14px 10px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  cursor: pointer;
  text-align: left;
  color: inherit;
  transition: border-color .16s, background .16s;
}
.lookback:hover { border-color: var(--accent, var(--haze)); background: color-mix(in srgb, var(--wash) 4%, transparent); }
.lookback__thumb {
  width: 42px; height: 42px; flex: none;
  border-radius: var(--r-sm);
  object-fit: cover;
  border: 1px solid var(--hairline);
}
.lookback__thumb--star { display: grid; place-items: center; background: var(--deep); }
.lookback__body { flex: 1; min-width: 0; }
.lookback__label {
  display: block;
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent, var(--dust));
  margin-bottom: 3px;
}
.lookback__text {
  display: block;
  font-size: 13.5px;
  color: var(--secondary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lookback svg { flex: none; color: var(--faint); transform: rotate(180deg); }

/* While the app is reading back through the entries. Quieter than a skeleton,
   because it is doing something rather than waiting for something. */
.threadsearch {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 40px 20px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  color: var(--faint);
  font-size: 14px;
}

/* --- connections --------------------------------------------------------- */

.thread {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--panel), var(--night));
  padding: 16px;
  margin-bottom: 16px;
  box-shadow: var(--lift);
}
.thread__theme {
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--dust);
  margin-bottom: 10px;
}
.thread__note {
  font-family: var(--display);
  font-size: 20px; line-height: 1.3;
  margin-bottom: 15px;
}
.thread__pair { display: grid; grid-template-columns: 1fr auto 1fr; gap: 12px; align-items: stretch; }
.thread__end {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 11px 12px;
  background: var(--deep);
  cursor: pointer;
  text-align: left;
  min-width: 0;
}
.thread__end:hover { border-color: var(--accent); }
.thread__kind {
  font-family: var(--mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 5px;
}
.thread__snippet {
  font-size: 13px; color: var(--secondary);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.thread__arc { display: grid; place-items: center; width: 34px; color: var(--faint); }
.thread__arc svg { width: 34px; height: 34px; }

/* --- universe (the constellation chart) ---------------------------------- */

.universe {
  position: relative;
  height: min(72vh, 680px);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background:
    radial-gradient(700px 500px at 30% 20%, #1A1E3C 0%, transparent 60%),
    radial-gradient(600px 400px at 80% 80%, #171932 0%, transparent 60%),
    #090A12;
  overflow: hidden;
  touch-action: none;
}
.universe svg {
  display: block; width: 100%; height: 100%; cursor: grab;
  /* A finger on the chart moves the chart, the way it would on a map. The page
     is still scrolled by starting anywhere else. */
  touch-action: none;
}
.universe svg:active { cursor: grabbing; }

.universe__controls {
  position: absolute; right: 12px; bottom: 12px;
  display: flex; flex-direction: column; gap: 6px;
  z-index: 2;
}
.universe__btn {
  display: grid; place-items: center;
  width: 32px; height: 32px;
  border-radius: 9px;
  border: 1px solid var(--hairline);
  background: #0D0F19CC;
  backdrop-filter: blur(6px);
  color: var(--dust);
  cursor: pointer;
  transition: color .16s, border-color .16s, opacity .16s;
}
.universe__btn:hover:not(:disabled) { color: var(--starlight); border-color: var(--haze); }
.universe__btn:disabled { opacity: .3; cursor: default; }
.universe__hint {
  position: absolute; left: 14px; bottom: 12px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  color: var(--faint); pointer-events: none;
}
.universe__legend {
  position: absolute; right: 14px; top: 12px;
  display: flex; gap: 12px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  pointer-events: none;
}
.universe__legend span { display: flex; align-items: center; gap: 5px; color: var(--dust); }
.universe__legend i { width: 7px; height: 7px; border-radius: 50%; display: block; }

/* --- resonating ----------------------------------------------------------
   The star fills in the instant it is pressed, and blooms out of the entry on
   a double-tap. Nothing here waits for the server. */

/* A quiet way to ask who, beside the count rather than on top of it. */
.act--who {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 5px 6px;
  margin-left: -6px;
}
.act--who:hover { color: var(--starlight); }
.act--who[hidden] { display: none; }

/* Kept, for yourself. Nobody is told, so there is no count beside it. */
.act--keep.is-on { color: var(--dawn); }
.act--keep.is-on svg { fill: var(--dawn); stroke: var(--dawn); }

.act--resonate.is-on { color: var(--ember); }
.act--resonate.is-on svg { fill: var(--ember); stroke: var(--ember); }
.act--resonate svg { transition: transform .18s cubic-bezier(.2,.8,.3,1.4); }
.act--resonate.is-on svg { transform: scale(1.12); }

.burst {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  color: var(--ember);
  z-index: 3;
  animation: burst .78s cubic-bezier(.2,.8,.3,1) forwards;
}
@keyframes burst {
  0%   { opacity: 0; transform: scale(.5); }
  22%  { opacity: .92; transform: scale(1.06); }
  52%  { opacity: .82; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.18); }
}

.u-node { cursor: pointer; }
.u-node circle { transition: r .2s ease, opacity .2s ease; }
.u-node:hover .u-halo { opacity: .55; }
.u-label {
  font-family: var(--mono); font-size: 9px; fill: var(--dust);
  pointer-events: none;
  transition: opacity .2s ease, fill .2s ease;
}
.u-link { stroke: var(--thread, var(--haze)); fill: none; transition: stroke .2s ease, opacity .2s ease; }

/* Holding on one star: everything unrelated steps back, so what that entry is
   joined to is the only thing left lit. */
.universe svg.is-focused .u-node.is-dim { opacity: .18; }
.universe svg.is-focused .u-node.is-lit .u-label { fill: var(--starlight); }
.universe svg.is-focused .u-link { opacity: .12; }
.universe svg.is-focused .u-link.is-lit { opacity: 1; stroke: var(--thread, var(--dust)); }
.u-link--tinted { stroke-opacity: .6; }
.universe svg.is-focused .u-link--tinted.is-lit { stroke-opacity: 1; }

/* While the sky is being dragged, nothing should look clickable. */
.universe svg.is-panning { cursor: grabbing; }
.universe svg.is-panning .u-node { pointer-events: none; }

.universe__count {
  position: absolute; right: 56px; bottom: 18px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  color: var(--faint); pointer-events: none;
}

/* --- what the chart is ---------------------------------------------------- */

.chartkey {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  padding: 6px 8px 8px;
  margin-bottom: 16px;
}
.chartkey__toggle {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  background: none; border: none;
  color: var(--dust);
  font: inherit;
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  padding: 8px 8px 6px;
  cursor: pointer;
  text-align: left;
}
.chartkey__toggle:hover { color: var(--starlight); }
.chartkey__toggle svg { opacity: .7; flex: none; }
.chartkey__toggle::after {
  content: '−';
  margin-left: auto;
  font-family: var(--body);
  font-size: 15px;
  line-height: 1;
}
.chartkey--closed .chartkey__toggle::after { content: '+'; }

.chartkey__body {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 20px;
  padding: 6px 8px 4px;
}
.chartkey--closed .chartkey__body { display: none; }
.chartkey__row strong {
  display: block;
  font-size: 13.5px;
  margin-bottom: 2px;
}
.chartkey__row span {
  font-size: 12.5px;
  color: var(--faint);
  line-height: 1.5;
}

.chartnote {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 14px;
  padding: 12px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  font-size: 13px;
}
.chartnote strong { font-weight: 600; }
.chartnote span { color: var(--faint); flex: 1; min-width: 200px; }
.u-link--growth { stroke: var(--dawn); stroke-opacity: .5; stroke-dasharray: none; }

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

.profile {
  display: flex; gap: 22px; align-items: flex-start;
  padding: 8px 0 26px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 26px;
}
.profile__body { flex: 1; min-width: 0; }
.profile__name { font-family: var(--display); font-size: 32px; line-height: 1.1; }
.profile__handle { font-family: var(--mono); font-size: 12.5px; color: var(--faint); margin-bottom: 10px; }
.profile__bio { color: var(--secondary); max-width: 46ch; margin-bottom: 14px; }
.profile__stats { display: flex; gap: 22px; }
.stat { text-align: left; }
/* A count you can open looks like one: it underlines on hover, and keeps a
   visible focus ring for anyone arriving by keyboard. */
.stat--link {
  background: none; border: none; padding: 0; color: inherit; font: inherit;
  cursor: pointer; border-radius: var(--r-sm);
}
.stat--link:hover .stat__value { text-decoration: underline; text-underline-offset: 3px; }
.stat--link:hover .stat__label { color: var(--dust); }
.stat__value { font-family: var(--display); font-size: 22px; }
.stat__label { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--faint); }
.profile__actions { display: flex; gap: 8px; margin-top: 16px; }

/* display:block matters — as a bare inline span its width would be ignored
   anywhere it is not a direct flex child, such as inside a link. */
.avatar {
  display: block;
  border-radius: 50%;
  flex: none;
  background: var(--deep);
  border: 1px solid var(--hairline);
  overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }

/* --- filters ------------------------------------------------------------- */

.filters { display: flex; gap: 7px; margin-bottom: 22px; flex-wrap: wrap; }
.filter {
  padding: 6px 13px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  color: var(--dust);
  font-size: 13px; font-weight: 500;
  cursor: pointer;
}
.filter:hover { color: var(--starlight); }
.filter[aria-pressed="true"] { color: var(--void); background: var(--starlight); border-color: transparent; font-weight: 600; }
.filter[aria-pressed="true"][data-kind="thought"] { background: var(--ember); }
.filter[aria-pressed="true"][data-kind="dream"] { background: var(--violet); }
.filter[aria-pressed="true"][data-kind="idea"] { background: var(--dawn); }

/* --- empty & states ------------------------------------------------------ */

.empty {
  text-align: center;
  padding: 64px 24px;
  border: 1px dashed var(--hairline);
  border-radius: var(--r-lg);
}
.empty__mark { width: 40px; height: 40px; margin: 0 auto 16px; opacity: .8; }
.empty__title { font-family: var(--display); font-size: 24px; margin-bottom: 7px; }
.empty__text { color: var(--faint); max-width: 40ch; margin: 0 auto 18px; }

.skeleton {
  border-radius: var(--r-lg);
  background: linear-gradient(90deg, var(--deep) 25%, color-mix(in srgb, var(--deep), var(--panel) 55%) 50%, var(--deep) 75%);
  background-size: 200% 100%;
  animation: sweep 1.4s linear infinite;
  height: 190px;
  margin-bottom: 30px;
}
@keyframes sweep { from { background-position: 200% 0; } to { background-position: -200% 0; } }

.spinner {
  width: 15px; height: 15px;
  border: 2px solid color-mix(in srgb, var(--wash) 16%, transparent);
  border-top-color: var(--starlight);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* --- compose ------------------------------------------------------------- */

.compose-launch {
  position: fixed;
  right: 32px; bottom: 32px;
  z-index: 30;
  width: 56px; height: 56px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--wash) 12%, transparent);
  background: linear-gradient(150deg, var(--dawn), var(--violet));
  color: var(--void);
  display: grid; place-items: center;
  cursor: pointer;
  box-shadow: 0 14px 40px -12px color-mix(in srgb, var(--dawn) 40%, transparent), 0 0 0 1px var(--void);
  transition: transform .18s;
}
.compose-launch:hover { transform: scale(1.06) rotate(90deg); }
.compose-launch svg { width: 22px; height: 22px; }

.preview {
  position: relative;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--deep-2);
  margin-bottom: 14px;
}
.preview img { width: 100%; aspect-ratio: 1/1; object-fit: cover; }
.preview__remove { position: absolute; right: 8px; top: 8px; }
.preview--busy { display: grid; place-items: center; aspect-ratio: 1/1; gap: 12px; }

/* Three choices when writing, four when editing — the extra one being "keep
   the picture it already has" — so the row counts its own children. */
/* One column per choice, and there are two, three or four of them: two when
   AI pictures are not offered, one more while editing an entry that already
   has a picture, one more again when they are. Each count needs its own rule
   and every one of them has to be repeated in the phone rule below — a bare
   class cannot override a `:has()`, whatever the media query. */
.imagepick { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 14px; }
.imagepick:has(> :nth-child(2):last-child) { grid-template-columns: repeat(2, 1fr); }
.imagepick:has(> :nth-child(4)) { grid-template-columns: repeat(4, 1fr); }
.imagepick .btn {
  flex-direction: column; gap: 6px; padding: 13px 8px;
  font-size: 12px; border-radius: var(--r-md);
  text-align: center;
  /* A .btn never wraps, which is right for a button sitting on its own line
     and wrong for four sharing a narrow one: the label paints outside its own
     column, across its neighbour, and the width it claims is enough to make
     the dialog draggable sideways — .modal__body scrolls on both axes the
     moment it scrolls on either. */
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
}
.imagepick .btn small { font-weight: 400; color: var(--faint); font-size: 10.5px; }

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

.backdrop {
  position: fixed; inset: 0; z-index: 50;
  background: #05060BD9;
  backdrop-filter: blur(6px);
  display: grid; place-items: center;
  padding: 24px;
  overflow-y: auto;
  animation: fade .18s ease;
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.modal {
  width: 100%;
  max-width: min(560px, 100%);
  min-width: 0;
  background: linear-gradient(180deg, var(--elevated), var(--night));
  border: 1px solid var(--haze);
  border-radius: var(--r-lg);
  box-shadow: 0 40px 90px -30px #000;
  animation: rise .22s cubic-bezier(.2,.7,.3,1);
  margin: auto;
  /* Taller than the screen, the middle scrolls and the two ends stay put, so
     Cancel and Save are always where you left them. A long dialog on a phone
     otherwise pushes its own buttons below the fold. */
  max-height: calc(100dvh - 48px);
  display: flex;
  flex-direction: column;
}
@keyframes rise { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }

.modal__head {
  display: flex; align-items: center; gap: 12px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--hairline);
  flex: none;
}
.modal__title { font-family: var(--display); font-size: 22px; }
.modal__body {
  padding: 18px 20px;
  overflow-y: auto;
  min-height: 0;
}
.modal__foot {
  display: flex; gap: 9px; justify-content: flex-end;
  padding: 14px 20px 18px;
  border-top: 1px solid var(--hairline);
  flex: none;
}
.modal__close { margin-left: auto; }

/* --- the landing page ---------------------------------------------------- */

/*
 * The front door, in front of the sign-up form rather than instead of it.
 *
 * position/z-index for the reason tests/stacking.test.js exists: the starfield
 * is fixed, opaque and positioned, so a full-height view that forgets this
 * renders perfectly and shows nothing at all. `.landing` is in that test's
 * PAGE_ROOTS for the same reason `.gate` and `.solo` are.
 */
.landing {
  position: relative; z-index: 1;
  min-height: 100dvh;
  width: 100%;
  max-width: 860px;
  margin: 0 auto;
  padding: 0 22px 80px;
}

.landing__bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  padding: 20px 0 8px;
}
.landing__barlinks { display: flex; align-items: center; gap: 8px; }

/* The mark on the gate, which is the way back to this page. A button, so it
   is reachable by keyboard, wearing the brand's own clothes. */
.gate__home {
  background: none; border: none; color: inherit; cursor: pointer;
  text-align: left;
}

.landing__hero { padding: 7vh 0 6vh; text-align: center; }

.landing__title {
  font-family: var(--display);
  font-size: clamp(36px, 7vw, 62px);
  line-height: 1.03;
  letter-spacing: -.01em;
  margin: 0 0 18px;
  text-wrap: balance;
}
.landing__title em { font-style: italic; }

.landing__lede {
  font-size: 17.5px; line-height: 1.6; color: var(--dust);
  margin: 0 auto 34px; max-width: 46ch;
  text-wrap: pretty;
}

.landing__kinds {
  list-style: none; margin: 0 0 34px; padding: 0;
  display: grid; gap: 10px;
}
.landing__kind {
  display: flex; gap: 12px; align-items: center;
  padding: 13px 16px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  text-align: left;
}
.landing__kindstar { flex: none; display: flex; }
.landing__kind strong { font-size: 14.5px; font-weight: 600; flex: none; }
.landing__kind span { font-size: 13.5px; color: var(--faint); }

.landing__act { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.landing__actnote { font-size: 13px; color: var(--faint); margin: 0; }

/* Wrapping rather than shrinking, so neither of the two is ever a stub — the
   lesson tests/imagepick.test.js was written for. */
.landing__before {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: 8px;
  margin: 26px 0 0;
}

.landing__part {
  padding: 46px 0;
  border-top: 1px solid var(--hairline);
  max-width: 62ch;
  /* Centred like the hero above it — a left-hugging column under a centred
     opening reads as the page losing its nerve halfway down. */
  margin: 0 auto;
  text-align: center;
}
.landing__h {
  font-family: var(--display);
  font-size: clamp(25px, 4vw, 34px);
  line-height: 1.12;
  margin: 0 0 16px;
  text-wrap: balance;
}
.landing__p {
  font-size: 16px; line-height: 1.68; color: var(--dust);
  margin: 0 0 15px;
  text-wrap: pretty;
}
.landing__p:last-child { margin-bottom: 0; }

.landing__sky {
  margin: 24px 0 10px;
  /* The shape of the frame the sky is drawn into, so it fills the box at every
     width instead of sitting letterboxed in the middle of it. BAND in
     landing.js is this number and has to stay this number. */
  aspect-ratio: 8 / 5;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, #12141F, #0A0B14);
  overflow: hidden;
}
.landing__sky svg { display: block; width: 100%; height: 100%; }
.landing__caption { font-size: 12.5px; color: var(--faint); margin: 0 0 4px; }

/*
 * The made sky on the landing page: it fills in, and then it breathes.
 *
 * Every part of the movement is a CSS animation rather than a loop in
 * JavaScript, for one reason. The router throws this page away and rebuilds
 * it, and a requestAnimationFrame loop started here would go on running
 * against a picture nobody can see, for the rest of the session. An animation
 * belongs to its element and stops when the element does.
 *
 * **The finished sky is what these rules describe** — stars at full strength,
 * threads solid. The animations only ever run it backwards from nothing, so
 * anything that switches them off is left looking at the whole picture rather
 * than an empty box: the reduce block below, a browser that never reaches the
 * `is-drawing` class, an old browser that ignores the keyframes. That is also
 * why each of them fills `backwards` — it holds a star at nothing through its
 * delay, instead of showing it and then taking it away to bring it back.
 *
 * The colours are the chart's own, with one change: a thread here is drawn a
 * shade above var(--haze), because the chart draws its lines across a panel
 * the height of a screen and these are a pixel wide across a picture the size
 * of a postcard. At that width haze is a line only somebody who already knows
 * it is there can see — and the paragraph above this picture is about the
 * lines. One entry growing out of another keeps var(--dawn), as in the chart.
 */
.landing__sky--made .madesky__dust circle { fill: var(--faint); opacity: .55; }
.landing__sky--made .madesky__halo { opacity: .14; }
.landing__sky--made .madesky__thread {
  stroke: var(--faint); stroke-opacity: .5; fill: none; stroke-linecap: round;
}
.landing__sky--made .madesky__thread--tinted { stroke: var(--thread, var(--faint)); stroke-opacity: .7; }
.landing__sky--made .madesky__thread--growth { stroke: var(--dawn); stroke-opacity: .55; }

.landing__sky--made.is-drawing .madesky__star {
  animation: madesky-arrive 1.6s ease-out var(--in) backwards;
}
.landing__sky--made.is-drawing .madesky__drift {
  animation: madesky-drift var(--dur) ease-in-out var(--in) infinite alternate;
}
.landing__sky--made.is-drawing .madesky__thread {
  stroke-dasharray: var(--len);
  animation: madesky-draw 2.2s ease-in-out var(--in) backwards;
}
.landing__sky--made.is-drawing .madesky__dust circle {
  animation: madesky-twinkle var(--dur) ease-in-out var(--in) infinite alternate;
}

@keyframes madesky-arrive {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes madesky-drift {
  from { transform: translate(0, 0); }
  to   { transform: translate(var(--dx), var(--dy)); }
}
@keyframes madesky-draw {
  from { stroke-dashoffset: var(--len); }
  to   { stroke-dashoffset: 0; }
}
@keyframes madesky-twinkle {
  from { opacity: .22; }
  to   { opacity: .75; }
}

/* Asked for less movement: the sky, still and whole. Nothing is lost by it,
   because the picture the animations arrive at is the picture described above
   — switching them off shows the end rather than the beginning. */
@media (prefers-reduced-motion: reduce) {
  .landing__sky--made.is-drawing .madesky__star,
  .landing__sky--made.is-drawing .madesky__drift,
  .landing__sky--made.is-drawing .madesky__thread,
  .landing__sky--made.is-drawing .madesky__dust circle { animation: none; }
}

/* --- the crossing, drawn ------------------------------------------------ */

/*
 * Two entries, one thread, and the two endings under them.
 *
 * The grid is the whole argument: three columns, with each ending sitting in
 * the same column as the entry it belongs to, so the lit panel is under yours
 * and the dark one is under theirs and nobody has to be told which is which.
 * Dropping to one column keeps it — the order down the page is yours, the
 * thread, theirs, told, not told, which is the same story read aloud.
 *
 * The frame and the two ends are the app's own: `.cross__pair` in the
 * notifications is the same shape down to the kind colouring the top of each
 * end, so signing up shows the picture again rather than a second design of
 * it. What is missing here is the writing, and that is deliberate — see
 * drawnCrossing() in landing.js.
 *
 * **The finished picture is what these rules describe**, exactly as with the
 * sky above: every animation runs it backwards from nothing, so reduced
 * motion, an old browser, or one that never reaches `is-drawing` is left
 * looking at the whole thing rather than an empty frame. Nothing outside
 * `is-drawing` may hide a part of it, and tests/landing.test.js walks these
 * rules to make sure nothing starts to.
 */
.crossfig {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 18px 14px;
  align-items: start;
  margin: 24px 0 10px;
  padding: 18px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, #12141F, #0A0B14);
}

.crossfig__end {
  min-width: 0;
  display: grid; gap: 3px; align-content: start;
  border-top: 2px solid var(--haze);
  padding-top: 11px;
}
.crossfig__end[data-kind="thought"] { border-top-color: var(--ember); }
.crossfig__end[data-kind="dream"]   { border-top-color: var(--violet); }
.crossfig__end[data-kind="idea"]    { border-top-color: var(--dawn); }

.crossfig__star { display: flex; margin-bottom: 3px; }
.crossfig__whose { font-size: 15px; font-weight: 600; }
.crossfig__when {
  font-family: var(--mono, monospace); font-size: 10px;
  letter-spacing: .13em; text-transform: uppercase; color: var(--faint);
}
.crossfig__aud {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 5px; font-size: 13px; color: var(--dust);
}
.crossfig__aud svg { flex: none; }

/* The thread: two short rules and the theme between them, the way the app
   draws it when the two entries are stacked. It is a phrase rather than a
   word, and it never wraps — a theme broken across two lines stops reading
   as one thing, which is the one thing it has to read as. */
.crossfig__join {
  display: flex; align-items: center; justify-content: center;
  gap: 8px;
  padding-top: 34px;
}
.crossfig__rule { width: 18px; height: 1px; background: var(--haze); flex: none; }
.crossfig__rule:first-child { transform-origin: right; }
.crossfig__rule:last-child  { transform-origin: left; }
.crossfig__thread {
  font-family: var(--mono, monospace); font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--dawn); white-space: nowrap;
}

/* What each of them is told. One is lit and one is not, and the difference is
   the point — so the dark one is drawn as a panel that is empty on purpose
   (a dashed edge, a bell with a line through it) rather than one that looks
   unfinished. */
.crossfig__end2 {
  display: grid; gap: 4px; align-content: start;
  /* Both the same height, whichever of the two has more to say. One panel
     ending short of the other reads as a ragged edge rather than a pair. */
  align-self: stretch;
  padding: 12px 14px;
  border-radius: var(--r-md);
  font-size: 13px;
}
.crossfig__end2 strong { font-size: 14px; font-weight: 600; }
.crossfig__end2 span:last-child { color: var(--dust); line-height: 1.45; }

.crossfig__end2--told {
  grid-column: 1;
  border: 1px solid #5FD8C440;
  background: #5FD8C40E;
}
.crossfig__end2--told .crossfig__bell { color: var(--dawn); }

.crossfig__end2--untold {
  grid-column: 3;
  border: 1px dashed var(--haze);
}
.crossfig__end2--untold strong { color: var(--dust); }
.crossfig__end2--untold span:last-child { color: var(--faint); }
.crossfig__end2--untold .crossfig__bell { color: var(--faint); }

.crossfig__bell { position: relative; display: inline-flex; width: 15px; height: 15px; }
/* A bell that never rings. Drawn rather than a second icon, so the two
   endings wear the same mark and only one of them is crossed out. */
.crossfig__end2--untold .crossfig__bell::after {
  content: ''; position: absolute; inset: -1px;
  background: linear-gradient(to top right,
    transparent calc(50% - .7px), currentColor calc(50% - .7px),
    currentColor calc(50% + .7px), transparent calc(50% + .7px));
}

/* The sky's caption is the last thing in its section; this one has the
   argument for the whole picture under it, and needs the room a paragraph has. */
.crossfig + .landing__caption { margin-bottom: 16px; }

.crossfig.is-drawing .crossfig__end,
.crossfig.is-drawing .crossfig__join,
.crossfig.is-drawing .crossfig__end2 {
  animation: crossfig-arrive .7s ease-out var(--in) backwards;
}
/* The thread draws itself outward from the theme, once both ends are there.
   --in is the join's own delay, inherited. */
.crossfig.is-drawing .crossfig__rule {
  animation: crossfig-draw .55s ease-out calc(var(--in) + .3s) backwards;
}

@keyframes crossfig-arrive {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
@keyframes crossfig-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Asked for less movement: the crossing, whole and still. As with the sky,
   nothing is lost — what the animations arrive at is what is described above. */
@media (prefers-reduced-motion: reduce) {
  .crossfig.is-drawing .crossfig__end,
  .crossfig.is-drawing .crossfig__join,
  .crossfig.is-drawing .crossfig__end2,
  .crossfig.is-drawing .crossfig__rule { animation: none; }
}

/* One column, and the story runs down the page instead of across it. The same
   width the notification's own pair gives up at, for the same reason. */
@media (max-width: 560px) {
  .crossfig { grid-template-columns: 1fr; gap: 14px; }
  .crossfig__join { padding-top: 0; justify-content: flex-start; }
  .crossfig__end2--told, .crossfig__end2--untold { grid-column: 1; }
}

.landing__audiences { list-style: none; margin: 0 0 18px; padding: 0; display: grid; gap: 10px; }
.landing__audiences li {
  display: flex; gap: 12px; align-items: baseline;
  padding: 12px 15px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  text-align: left;
}
.landing__aud { flex: none; color: var(--dust); transform: translateY(2px); }
.landing__audiences strong { font-size: 14px; font-weight: 600; flex: none; }
.landing__audiences span { font-size: 13.5px; color: var(--faint); }

/* The install, and the way to read about it. Wrapping rather than shrinking,
   and the one-tap button is absent rather than disabled when no browser has
   offered one — a control that explains why it does nothing is worse than no
   control. */
.landing__install {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 10px;
  margin: 22px 0 18px;
}
.landing__installslot { display: flex; }
.landing__installslot[hidden] { display: none; }

.landing__end {
  padding: 52px 0 40px;
  border-top: 1px solid var(--hairline);
  text-align: center;
}
.landing__endh {
  font-family: var(--display);
  font-size: clamp(25px, 4.4vw, 36px);
  line-height: 1.1;
  margin: 0 0 12px;
  text-wrap: balance;
}
.landing__end .landing__p { margin-bottom: 24px; }
.landing__switch { margin: 16px 0 0; font-size: 13.5px; color: var(--faint); }
.landing__switch button {
  background: none; border: none; padding: 0;
  color: var(--starlight); font-weight: 600; cursor: pointer;
  text-decoration: underline; font-family: inherit; font-size: inherit;
}

.landing__foot {
  padding-top: 26px;
  border-top: 1px solid var(--hairline);
  text-align: center;
  font-size: 12.5px; color: var(--faint);
}
.landing__foot a { color: var(--dust); text-decoration: underline; text-underline-offset: 2px; }
.landing__foot a:hover { color: var(--starlight); }

@media (min-width: 700px) {
  .landing__kinds { grid-template-columns: repeat(3, 1fr); }
  .landing__kind { flex-direction: column; align-items: flex-start; gap: 8px; }
  .landing__audiences li { align-items: center; }
}

@media (max-width: 420px) {
  /* The bar carries a mark and two controls. Centring the lot beats shrinking
     "Start your universe" into something unreadable. */
  .landing__bar { flex-direction: column; align-items: stretch; gap: 12px; }
  .landing__barlinks { justify-content: center; }
}

/* --- auth ---------------------------------------------------------------- */

.gate {
  position: relative; z-index: 1;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.gate__pitch {
  padding: 8vh 5vw;
  display: flex; flex-direction: column; justify-content: center;
  border-right: 1px solid var(--hairline);
}
.gate__title {
  font-family: var(--display);
  font-size: clamp(38px, 5.2vw, 70px);
  line-height: 1.02;
  letter-spacing: -.01em;
  margin-bottom: 18px;
}
.gate__title em { font-style: italic; }
.gate__lede { font-size: 17px; color: var(--dust); max-width: 42ch; margin-bottom: 30px; }
.gate__three { display: grid; gap: 12px; max-width: 400px; }
.gate__three div {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
}
.gate__three strong { font-size: 14px; display: block; }
.gate__three span { font-size: 13px; color: var(--faint); }
.gate__three i { width: 9px; height: 9px; border-radius: 50%; margin-top: 6px; flex: none; }

.gate__form { padding: 8vh 5vw; display: flex; flex-direction: column; justify-content: center; }
.gate__card { width: 100%; max-width: 380px; margin: 0 auto; }
.gate__switch { text-align: center; margin-top: 16px; font-size: 13.5px; color: var(--faint); }
.gate__switch button { background: none; border: none; color: var(--starlight); font-weight: 600; cursor: pointer; text-decoration: underline; padding: 0; }

/* The privacy and terms links under the card. Quiet on purpose — they matter
   to the person who goes looking for them and to nobody else. */
.gate__legal {
  text-align: center;
  margin-top: 18px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--faint);
}
.gate__legal a { color: var(--dust); text-decoration: underline; text-underline-offset: 2px; }
.gate__legal a:hover { color: var(--starlight); }

/* Links inside a hint line, which is otherwise plain text. */
.field__hint a { color: var(--dust); text-decoration: underline; text-underline-offset: 2px; }
.field__hint a:hover { color: var(--starlight); }

/* --- the other way in ---------------------------------------------------
   "Continue with Google", under the form rather than above it. Deliberately
   the quieter of the two: an account here is an email address and a password
   first, and this is an alternative rather than the way in. */

.orway { margin-top: 18px; }
.orway__rule {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--faint);
  font-size: 12px;
  margin-bottom: 14px;
}
.orway__rule::before,
.orway__rule::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--hairline);
}
.orway__note {
  color: var(--faint);
  font-size: 12px;
  line-height: 1.5;
  margin-top: 10px;
  text-align: center;
}
.btn--google {
  border-color: var(--hairline);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-decoration: none;
}
.gmark { display: inline-flex; flex: none; }

/* --- a page that stands on its own --------------------------------------
   Used by the reset link, which is followed by somebody who cannot sign in.
   No rail, no tab bar — one card and nothing to get lost in. */

.solo {
  /* Above the starfield, which is fixed and so paints over anything that is
     not positioned — see .shell and .gate, which sit above it the same way.
     Without this the whole page renders and stays invisible. */
  position: relative;
  z-index: 1;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 6vh 22px;
}
.solo .brand { display: flex; align-items: center; gap: 10px; }
.solo__card { width: 100%; max-width: 380px; }
.solo__title { font-family: var(--display); font-size: 27px; margin-bottom: 6px; }
.solo__note { color: var(--faint); font-size: 14px; margin-bottom: 22px; }
.solo__foot {
  color: var(--faint);
  font-size: 12.5px;
  text-align: center;
  margin-top: 16px;
}

/* A statement of fact, not a success and not a failure: the reply to "I forgot
   my password" is deliberately the same either way, so it must not look like
   confirmation that the account exists. */
.notice {
  padding: 13px 15px;
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--wash) 3%, transparent);
  color: var(--secondary);
  font-size: 13.5px;
  line-height: 1.5;
}

/* A button that reads as a link. */
.linkish {
  background: none;
  border: none;
  padding: 0;
  color: var(--faint);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.linkish:hover { color: var(--starlight); }

.alert {
  padding: 10px 13px;
  border-radius: var(--r-md);
  border: 1px solid var(--danger-dim);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
  color: var(--danger-bright);
  font-size: 13.5px;
  margin-bottom: 14px;
}

/* A bar across the top when there is nothing to talk to. Deliberately plain:
   it is a statement of fact, not an error. */
.offlinebar {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 7px 14px;
  background: var(--haze);
  border-bottom: 1px solid var(--haze);
  color: var(--dust);
  font-family: var(--mono);
  font-size: 11px; letter-spacing: .06em;
}
.offlinebar svg { opacity: .8; }

/* --- the "…" menu on your own entries ------------------------------------ */

.ownermenu {
  z-index: 70;
  min-width: 200px;
  padding: 5px;
  border: 1px solid var(--haze);
  border-radius: var(--r-md);
  background: var(--elevated);
  box-shadow: 0 24px 50px -20px #000;
  animation: rise .14s ease;
}
.ownermenu__item {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  color: var(--starlight);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
.ownermenu__item:hover { background: color-mix(in srgb, var(--wash) 5%, transparent); }
.ownermenu__item svg { flex: none; opacity: .8; }
.ownermenu__item.is-danger { color: var(--danger); }
.ownermenu__item.is-danger:hover { background: color-mix(in srgb, var(--danger) 8%, transparent); }

/* --- the picture, on its own ---------------------------------------------- */

.lightbox {
  position: fixed; inset: 0; z-index: 80;
  margin: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px;
  padding: 5vh 5vw;
  background: #05060BF2;
  backdrop-filter: blur(10px);
  animation: fade .16s ease;
  cursor: zoom-out;
}
.lightbox img {
  max-width: 100%;
  max-height: 82vh;
  border-radius: var(--r-md);
  box-shadow: 0 40px 90px -30px #000;
  animation: rise .22s cubic-bezier(.2,.7,.3,1);
}
.lightbox figcaption {
  font-family: var(--display);
  font-size: 18px;
  color: var(--starlight);
  text-align: center;
  max-width: 60ch;
}
.lightbox__close {
  position: absolute; top: 16px; right: 16px;
  display: grid; place-items: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  border: 1px solid var(--hairline);
  background: #0D0F19CC;
  color: var(--dust);
  cursor: pointer;
}
.lightbox__close:hover { color: var(--starlight); }

/* Nothing behind it should scroll while it is open. */
body.has-lightbox { overflow: hidden; }

/* The picture in a card says it can be opened. */
.card__figure img { cursor: zoom-in; }

/* Picking who to send an entry to. */
.sendto { max-height: 46vh; overflow-y: auto; margin: 0 -4px; }
.sendto__person {
  width: 100%; background: none; border: none; cursor: pointer;
  text-align: left; padding: 8px 4px; border-radius: var(--r-sm);
}
.sendto__person:hover { background: color-mix(in srgb, var(--wash) 5%, transparent); }
.sendto__person:disabled { opacity: .5; cursor: default; }
.sendto__person svg { margin-left: auto; color: var(--faint); flex: none; }

/* --- messages -------------------------------------------------------------
   Two screens: the list of people, and one conversation. The conversation is
   the only place in the app that scrolls inside itself rather than with the
   page, because a message box that wanders off the bottom is useless. */

.chat {
  display: flex; align-items: center; gap: 13px;
  width: 100%;
  padding: 12px 10px;
  border: none;
  border-bottom: 1px solid var(--hairline);
  border-radius: var(--r-md) var(--r-md) 0 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.chat:hover { background: color-mix(in srgb, var(--wash) 3%, transparent); }
.chat.is-unread { background: linear-gradient(90deg, color-mix(in srgb, var(--violet) 7%, transparent), transparent 70%); }
.chat__body { flex: 1; min-width: 0; }
.chat__top { display: flex; align-items: baseline; gap: 8px; }
.chat__name { font-size: 14.5px; font-weight: 600; }
.chat.is-unread .chat__name { color: var(--starlight); }
.chat__when {
  margin-left: auto; flex: none;
  font-family: var(--mono); font-size: 10px; color: var(--faint);
}
.chat__last {
  display: block;
  font-size: 13px; color: var(--faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-top: 2px;
}
.chat.is-unread .chat__last { color: var(--dust); }
.chat__dot {
  width: 9px; height: 9px; flex: none;
  border-radius: 50%; background: var(--violet);
}

/* --- one conversation --- */

.column--convo { display: flex; flex-direction: column; height: 100dvh; }

.topbar--convo { gap: 12px; padding-bottom: 12px; }
.convo__who {
  font-family: var(--mono); font-size: 13px; color: var(--dust);
  text-decoration: none;
}
.convo__who:hover { color: var(--starlight); }

.convo { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.convo__messages {
  flex: 1;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 8px;
  padding: 4px 2px 16px;
  min-height: 0;
}
.convo__start {
  margin: auto;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 40px 20px;
  text-align: center;
}
.convo__start h3 { font-family: var(--display); font-size: 22px; }
.convo__start p { color: var(--faint); font-size: 13.5px; max-width: 34ch; }

.msg {
  align-self: flex-start;
  max-width: min(78%, 480px);
  padding: 9px 13px;
  border-radius: 16px 16px 16px 5px;
  background: var(--panel);
  border: 1px solid var(--hairline);
}
.msg--mine {
  align-self: flex-end;
  border-radius: 16px 16px 5px 16px;
  background: color-mix(in srgb, var(--violet) 22%, var(--panel));
  border-color: color-mix(in srgb, var(--violet) 40%, transparent);
}
.msg__text { font-size: 14.5px; line-height: 1.45; overflow-wrap: anywhere; white-space: pre-wrap; }
.msg__when {
  font-family: var(--mono); font-size: 9.5px; color: var(--faint);
  margin-top: 4px;
}

/*
 * Sent, Received, Seen — under the last thing you said.
 *
 * Sits outside the bubble's right edge rather than inside it, so it reads as a
 * note about the message instead of part of what you wrote. Quiet by default,
 * and only "Seen" is allowed to brighten: it is the one that answers the
 * question somebody is actually asking.
 */
.msg__status {
  position: absolute; right: 2px; top: calc(100% + 3px);
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .04em;
  color: var(--faint); white-space: nowrap;
}
.msg--mine[data-status="seen"] .msg__status { color: var(--dust); }
.msg--mine[data-status="sending"] .msg__status { font-style: italic; }

/* Room underneath for the line, on the one message that shows it. */
.msg--mine { position: relative; }
.msg--mine:has(.msg__status:not([hidden])) { margin-bottom: 13px; }

.msg.is-sending { opacity: .55; }
.msg.is-failed { border-color: var(--danger-dim); }
.msg__retry {
  display: block; margin-top: 6px;
  background: none; border: none; padding: 0;
  color: var(--danger); font: inherit; font-size: 11.5px;
  cursor: pointer; text-decoration: underline;
}

/* an entry sent into the conversation */
.msg__entry {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  margin-bottom: 7px;
  padding: 8px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--deep);
  color: inherit; font: inherit; text-align: left;
  cursor: pointer;
}
.msg__entry:hover { border-color: var(--accent, var(--haze)); }
.msg__entry--gone {
  cursor: default;
  color: var(--faint);
  font-size: 12.5px;
  font-style: italic;
  justify-content: center;
}
.msg__thumb {
  width: 38px; height: 38px; flex: none;
  border-radius: var(--r-sm); object-fit: cover;
  border: 1px solid var(--hairline);
}
.msg__thumb--star { display: grid; place-items: center; background: var(--night); }
.msg__entrybody { min-width: 0; flex: 1; }
.msg__entrykind {
  display: block;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent, var(--faint));
}
.msg__entrytext {
  display: block; font-size: 12.5px; color: var(--secondary); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Three dots while the other person is writing. Sits where their message will,
   so the thread does not jump when it arrives. */
.msg--typing { padding: 12px 14px; }
.msg--typing[hidden] { display: none; }
.typing { display: flex; align-items: center; gap: 4px; }
.typing__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--dust);
  animation: typing 1.2s ease-in-out infinite;
}
.typing__dot:nth-child(2) { animation-delay: .18s; }
.typing__dot:nth-child(3) { animation-delay: .36s; }
@keyframes typing {
  0%, 60%, 100% { opacity: .3; transform: translateY(0); }
  30%           { opacity: 1;  transform: translateY(-3px); }
}

/* Said to a screen reader, shown to nobody. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- the box --- */

.convo__foot { flex: none; padding: 8px 0 16px; }
.convo__box {
  display: flex; align-items: flex-end; gap: 8px;
  padding: 7px 7px 7px 14px;
  border: 1px solid var(--haze);
  border-radius: 22px;
  background: var(--panel);
}
.convo__input {
  flex: 1;
  background: none; border: none; resize: none;
  color: var(--starlight);
  font: inherit; font-size: 14.5px; line-height: 1.4;
  padding: 6px 0;
  max-height: 140px;
}
.convo__input:focus { outline: none; }
.convo__input::placeholder { color: var(--faint); }
.convo__send {
  flex: none;
  display: grid; place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%; border: none;
  background: var(--starlight); color: var(--void);
  cursor: pointer;
}
.convo__send:hover { filter: brightness(1.15); }
.convo__send svg { transform: rotate(180deg); margin-left: 2px; }
.convo__closed {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  color: var(--faint);
  font-size: 13px;
  text-align: center;
}

/* --- toasts -------------------------------------------------------------- */

.toasts {
  position: fixed; z-index: 90;
  left: 50%; bottom: 26px; transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 8px;
  pointer-events: none;
}
.toast {
  padding: 11px 17px;
  border-radius: 999px;
  background: var(--elevated);
  border: 1px solid var(--haze);
  box-shadow: 0 18px 40px -16px #000;
  font-size: 13.5px;
  animation: rise .2s ease;
  max-width: 90vw;
}
.toast--bad { border-color: var(--danger-dim); color: var(--danger-bright); }

/* --- mobile -------------------------------------------------------------- */

.tabbar { display: none; }

@media (max-width: 1180px) {
  .main--split { grid-template-columns: minmax(0, var(--column)); justify-items: center; }
  .aside { display: none; }
}

@media (max-width: 900px) {
  .gate { grid-template-columns: 1fr; }
  .gate__pitch { border-right: none; border-bottom: 1px solid var(--hairline); padding: 7vh 7vw 5vh; }
  .gate__form { padding: 5vh 7vw 9vh; }
}

@media (max-width: 820px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .rail { display: none; }
  .main { padding: 0 14px 96px; }
  .topbar h1 { font-size: 25px; }

  /* A title and a search box will not share one line on a phone. */
  .topbar { flex-wrap: wrap; gap: 10px; }
  .topbar__actions { margin-left: 0; width: 100%; }
  .search { max-width: none; }

  .tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    background: color-mix(in srgb, var(--deep) 95%, transparent);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--hairline);
    padding: 8px 4px calc(8px + env(safe-area-inset-bottom));
  }
  .tabbar__item {
    display: grid; place-items: center; gap: 3px;
    background: none; border: none; color: var(--faint);
    font-size: 10px; cursor: pointer; padding: 4px 0;
  }
  .tabbar__item svg { width: 19px; height: 19px; }
  .tabbar__item[aria-current="page"] { color: var(--starlight); }

  /* The owner's actions live behind the "…" menu now, so the bar is short
     enough to fit without hiding anything. */
  .act { padding: 5px 7px; }

  /* On somebody else's entry the bar holds four actions and the "…" menu, which
     is wider than a phone. Keep and Share drop their words and stay icons (both
     still carry an aria-label), so the menu is never pushed off the edge. */
  .card__foot { min-width: 0; }
  .act--keep .act__label, .act--share .act__label { display: none; }

  .compose-launch { right: 18px; bottom: 84px; width: 50px; height: 50px; }
  .feed { padding-left: 26px; }
  .entry__node { left: -26px; }
  .feed::before { left: 5px; }
  .card__title { font-size: 21px; }
  .profile { flex-direction: column; gap: 16px; }
  .profile__name { font-size: 27px; }
  /* Every selector, because ":has" outweighs a bare class and a media query
     adds no weight of its own — naming only .imagepick here left the four
     choices on the edit dialog in four columns on a phone. Any new count added
     above has to be repeated here or it keeps its columns on a phone, which is
     silent on a desktop and unreadable on a phone. */
  .imagepick,
  .imagepick:has(> :nth-child(2):last-child),
  .imagepick:has(> :nth-child(4)) { grid-template-columns: 1fr; }

  /* A conversation owns the screen: it scrolls inside itself and the page
     behind it does not move, so the box stays where a thumb expects it. The
     main column's bottom padding, which normally clears the tab bar, would
     make the document taller than the screen and undo that. */
  .column--convo { height: calc(100dvh - 72px); }
  .main:has(.column--convo) { padding-bottom: 0; }
  .chartkey__body { grid-template-columns: minmax(0, 1fr); }
  .today__question { font-size: 17px; }
  .today__go span { display: none; }
  .universe__count { display: none; }
  /* Short enough that the chart, its buttons and the hint all land on one
     screen with the key panel still above them. */
  .universe { height: min(56vh, 420px); }
  .imagepick .btn { flex-direction: row; justify-content: flex-start; text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .compose-launch:hover { transform: none; }
}

/* ------------------------------------------------------------------
   Speaking an entry
   ------------------------------------------------------------------ */

.dictate {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  flex-wrap: wrap;
}

.dictate__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

/* Listening is a state worth being obvious about: a microphone that is on and
   does not look on is the kind of thing that loses somebody's trust for good. */
.dictate__btn.is-listening {
  color: var(--void);
  background: var(--ember);
  border-color: var(--ember);
}

.dictate__btn.is-listening svg {
  animation: dictate-pulse 1.4s ease-in-out infinite;
}

@keyframes dictate-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* The running guess. Dimmer than written text, because it is not yet. */
.dictate__heard {
  flex: 1 1 140px;
  min-width: 0;
  font-size: 13px;
  color: var(--dust);
  font-style: italic;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dictate__note {
  font-size: 11px;
  color: var(--faint);
  letter-spacing: 0.02em;
  margin-left: auto;
}

/* On a narrow screen the reassurance goes under, rather than squeezing the
   guess down to nothing. */
@media (max-width: 420px) {
  .dictate__note { margin-left: 0; flex-basis: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .dictate__btn.is-listening svg { animation: none; }
}

/* The line that appears when the hour you chose has gone by and the day is
   still empty. A reminder, not a telling-off — hence the colour of a low
   lamp rather than a warning. */
.today__line--nudge {
  border-left: 2px solid var(--ember);
  padding-left: 10px;
}

.today__line--nudge .today__note {
  color: var(--ember);
}

/* ------------------------------------------------------------------
   A crossing: two entries and the thread between them
   ------------------------------------------------------------------ */

.cross {
  display: flex;
  align-items: stretch;
  gap: 10px;
  text-align: left;
  cursor: default;
}

.cross__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.cross__head {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The two entries, with the thread named between them. */
.cross__pair {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 10px;
  align-items: stretch;
  background: var(--night);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  padding: 14px;
}

.cross__end {
  min-width: 0;
  text-align: left;
  background: none;
  border: 0;
  border-top: 2px solid var(--haze);
  padding: 10px 0 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  display: grid;
  gap: 4px;
  align-content: start;
  border-radius: 0;
}

.cross__end[data-kind="thought"] { border-top-color: var(--ember); }
.cross__end[data-kind="dream"]   { border-top-color: var(--violet); }
.cross__end[data-kind="idea"]    { border-top-color: var(--dawn); }

.cross__end:hover .cross__text { color: var(--starlight); }
.cross__end:focus-visible { outline: 2px solid var(--ember); outline-offset: 3px; }

.cross__label {
  font-family: var(--mono, monospace);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.cross__text {
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--dust);
  /* Two lines each, so neither side can push the other out of shape. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.cross__join {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 5px;
  padding-top: 10px;
}

.cross__rule {
  width: 1px;
  height: 10px;
  background: var(--haze);
}

.cross__thread {
  font-family: var(--mono, monospace);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dawn);
  white-space: nowrap;
  max-width: 92px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cross__more {
  align-self: flex-start;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 12.5px;
  color: var(--faint);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.cross__more:hover { color: var(--dawn); }
.cross__more:focus-visible { outline: 2px solid var(--ember); outline-offset: 3px; }

/* Side by side needs width. Narrower than this and the two entries stack, with
   the thread turned to run across rather than down. */
@media (max-width: 560px) {
  .cross__pair { grid-template-columns: 1fr; gap: 12px; }
  .cross__join { grid-auto-flow: column; justify-content: start; padding-top: 0; }
  .cross__rule { width: 14px; height: 1px; }
  .cross__thread { max-width: none; }
}

/* ------------------------------------------------------------------
   Ideas, and where they have got to
   ------------------------------------------------------------------ */

/* The badge on a card. Quiet by default; the two endings get colour, because
   "made it" is worth seeing across a page and so, in its way, is "let go". */
.state {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  letter-spacing: 0.04em;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  color: var(--faint);
  white-space: nowrap;
}

.state[data-state="building"] {
  color: var(--dawn);
  border-color: color-mix(in srgb, var(--dawn) 40%, transparent);
}

.state[data-state="shipped"] {
  color: var(--void);
  background: var(--dawn);
  border-color: var(--dawn);
}

.state[data-state="shelved"] {
  color: var(--faint);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/* The question at the top of Home: "still alive?" */
.stillalive {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  background: var(--night);
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--dawn);
  border-radius: 0 14px 14px 0;
}

.stillalive__head { display: flex; flex-direction: column; gap: 6px; }

.stillalive__label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.stillalive__text {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 15px;
  line-height: 1.45;
  color: var(--starlight);
  text-align: left;
  cursor: pointer;
}

.stillalive__text:hover { color: var(--dawn); }
.stillalive__text:focus-visible { outline: 2px solid var(--dawn); outline-offset: 3px; }

.stillalive__ask {
  font-family: var(--display, Georgia, serif);
  font-size: 19px;
  color: var(--dawn);
}

.stillalive__answers {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}

.stillalive__later { margin-left: auto; font-size: 12.5px; }

/* What is left after answering: one line, then it is gone on the next load. */
.stillalive--done {
  border-left-color: var(--haze);
  padding: 12px 16px;
}

.stillalive__note { font-size: 14px; color: var(--dust); }

@media (max-width: 460px) {
  .stillalive__later { margin-left: 0; }
}

/* ------------------------------------------------------------------
   A dream that keeps coming back
   ------------------------------------------------------------------ */

.recur {
  background: var(--night);
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--violet);
  border-radius: 0 16px 16px 0;
  padding: 20px;
  margin-bottom: 18px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.recur__head { display: flex; flex-direction: column; gap: 6px; }

.recur__label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet);
}

.recur__count {
  font-family: var(--display, Georgia, serif);
  font-weight: 400;
  font-size: 26px;
  line-height: 1.15;
  margin: 0;
  color: var(--starlight);
}

.recur__span { margin: 0; font-size: 14px; color: var(--dust); }
/* One known thing, said quietly under the finding — never louder than the finding. */
.recur__note, .cross__note { margin: 10px 0 0; font-size: 14px; line-height: 1.5; color: var(--dust); }

/* The run of dreams, as a numbered line down the page — the count is the
   finding, so the numbers are doing real work rather than decorating. */
.recur__run {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: relative;
}

/* The thread itself, drawn behind the dots. */
.recur__run::before {
  content: '';
  position: absolute;
  left: 10px;
  top: 14px;
  bottom: 14px;
  width: 1px;
  background: var(--haze);
}

.recur__step {
  display: grid;
  grid-template-columns: 21px 1fr;
  gap: 12px;
  align-items: start;
  padding: 6px 0;
}

.recur__dot {
  width: 21px;
  height: 21px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  color: var(--void);
  background: var(--violet);
  position: relative;
  z-index: 1;
}

.recur__dream {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  display: grid;
  gap: 2px;
  min-width: 0;
}

.recur__when {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}

.recur__private {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  letter-spacing: 0.06em;
}

.recur__text {
  font-size: 14px;
  line-height: 1.45;
  color: var(--dust);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.recur__dream:hover .recur__text { color: var(--starlight); }
.recur__dream:focus-visible { outline: 2px solid var(--violet); outline-offset: 3px; }

.recur__more {
  align-self: flex-start;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 12.5px;
  color: var(--faint);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.recur__more:hover { color: var(--violet); }
.recur__more:focus-visible { outline: 2px solid var(--violet); outline-offset: 3px; }

/* ------------------------------------------------------------------
   Three in the morning
   ------------------------------------------------------------------ */

/* Darker than the rest of the app, deliberately: a screen at full brightness
   at 3am wakes you up properly, which defeats the whole point. */
body.nightmode { background: #05060B; }

.night {
  position: fixed;
  inset: 0;
  background: #05060B;
  color: var(--dust);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 50;
}

.night__leave {
  position: absolute;
  top: max(14px, env(safe-area-inset-top));
  right: max(14px, env(safe-area-inset-right));
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  border-radius: 50%;
  /* Faint on purpose. It has to be findable but must not invite a tap. */
  color: #2A2F49;
  cursor: pointer;
}

.night__leave:hover { color: var(--faint); }
.night__leave:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }

.night__middle {
  width: 100%;
  max-width: 460px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  text-align: center;
}

.night__mic {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  width: 100%;
}

/* The size of a fist, so it can be hit with one eye open. */
.night__button {
  width: 148px;
  height: 148px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: #0E1020;
  border: 1px solid #1B1F35;
  color: var(--violet);
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, color 160ms ease;
}

.night__button:hover { border-color: #2A2F49; }
.night__button:focus-visible { outline: 2px solid var(--violet); outline-offset: 6px; }

.night__button.is-listening {
  background: var(--violet);
  border-color: var(--violet);
  color: #05060B;
  animation: night-breathe 2.6s ease-in-out infinite;
}

/* Slow, so it reads as listening rather than as an alarm. */
@keyframes night-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(155, 140, 232, 0.24); }
  50%      { box-shadow: 0 0 0 22px rgba(155, 140, 232, 0); }
}

.night__status {
  margin: 0;
  font-size: 14px;
  color: #4A4E66;
  min-height: 1.4em;
}

.night__heard {
  margin: 0;
  font-size: 15px;
  font-style: italic;
  color: #3C4058;
  min-height: 1.4em;
}

.night__said {
  margin: 0;
  font-size: 17px;
  line-height: 1.6;
  color: var(--starlight);
  text-align: left;
  width: 100%;
  max-height: 34vh;
  overflow-y: auto;
}

.night__save { margin-top: 4px; }

.night__done { display: flex; flex-direction: column; align-items: center; gap: 10px; }

.night__kept {
  margin: 0;
  font-family: var(--display, Georgia, serif);
  font-size: 27px;
  color: var(--violet);
}

.night__back { margin: 0; font-size: 14px; color: #4A4E66; }

.night__after { display: flex; gap: 8px; margin-top: 10px; }

/* The moon, directly above the writing button and sharing its right edge, so
   the two read as one pair rather than two stray circles. */
.night-launch {
  position: fixed;
  right: 38px;
  bottom: 98px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--panel);
  border: 1px solid var(--hairline);
  color: var(--violet);
  cursor: pointer;
  z-index: 20;
}

.night-launch:hover { border-color: var(--violet); }
.night-launch:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .night__button.is-listening { animation: none; }
}

/* On a phone the writing button moves up to clear the tab bar, so the moon
   moves with it — the two stay a pair at every width. */
@media (max-width: 900px) {
  /* right = 18 (the writing button's) + 25 (its half) - 20 (this one's half),
     so the two circles share a centre line at this width too. */
  .night-launch { right: 23px; bottom: 142px; width: 40px; height: 40px; }
}

/* A question drawn from the person's own threads, rather than the one
   everybody gets. Marked, because otherwise the work is invisible. */
.today__ask--personal { border-left: 2px solid var(--dawn); }

.today__from {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dawn);
  margin-bottom: 6px;
}

/* ------------------------------------------------------------------
   Writing without your name on it
   ------------------------------------------------------------------ */

/* A mask reads as a name but must not read as a link, because there is
   nothing behind it to go to. */
.card__name--masked {
  cursor: default;
  color: var(--dust);
  font-style: italic;
}

.card__unsigned { color: var(--faint); }

/* The choice in the composer, set apart because it is the one decision in
   there that cannot be taken back. */
.unsignedbox {
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--violet);
  border-radius: 0 var(--r-md, 12px) var(--r-md, 12px) 0;
  padding: 12px 14px;
  background: var(--night);
}

.unsignedbox .field__hint { color: var(--faint); }

/* ------------------------------------------------------------------
   A letter from the person you were
   ------------------------------------------------------------------ */

.letter {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
  padding: 18px;
  background: var(--night);
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--ember);
  border-radius: 0 14px 14px 0;
}

.letter__label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ember);
}

.letter__note { margin: 0; font-size: 15px; color: var(--dust); }

/* Set in the display face once opened: it is the only thing on the page
   written to be read rather than scanned. */
.letter__body {
  margin: 0;
  font-family: var(--display);
  font-size: 20px;
  line-height: 1.55;
  color: var(--starlight);
  white-space: pre-wrap;
}

.letter__from { font-size: 12px; color: var(--faint); }

.letter--open { border-left-color: var(--ember); }

/* ------------------------------------------------------------------
   How this works

   The one long page in an app made of short ones, so it sets its own
   measure: a reading column narrower than the feed, more air between
   sections than anywhere else, and the display face used for headings
   the way the written pages use it. It is a document, and it should
   feel like one without leaving the sky it is printed on.
   ------------------------------------------------------------------ */

.guide { padding-bottom: 40px; }

.guide__top { padding: 34px 0 8px; }

.guide__eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 18px;
}

.guide__title {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(34px, 6vw, 52px);
  line-height: 1.06;
  letter-spacing: -.01em;
  margin-bottom: 20px;
  text-wrap: balance;
}
.guide__title em { font-style: italic; }

.guide__standfirst {
  font-size: 18px;
  line-height: 1.65;
  color: var(--dust);
  max-width: 56ch;
}

/* The contents. Small, and set in the mono face so it reads as apparatus
   rather than as the first section. */
.guide__contents {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 10px;
  align-items: baseline;
  padding: 18px 0 4px;
  margin: 22px 0 8px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 18px;
}

.guide__contents-label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--faint);
  width: 100%;
}

.guide__contents-links { display: flex; flex-wrap: wrap; gap: 6px 8px; }

.guide__contents-link {
  background: none;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  color: var(--dust);
  font: inherit;
  font-size: 12.5px;
  padding: 5px 12px;
  cursor: pointer;
  min-height: 32px;
}
.guide__contents-link:hover { color: var(--starlight); border-color: var(--haze); }
.guide__contents-link:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }

/* --- a section --- */

.guide__block {
  padding: 40px 0;
  border-top: 1px solid var(--hairline);
  scroll-margin-top: 24px;
}
.guide__block:first-of-type { border-top: none; }

.guide__h {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(25px, 4vw, 33px);
  line-height: 1.14;
  margin-bottom: 10px;
  text-wrap: balance;
}

.guide__lede {
  font-size: 16px;
  color: var(--dust);
  max-width: 58ch;
  margin-bottom: 20px;
}

.guide__p {
  font-size: 16px;
  line-height: 1.72;
  color: var(--dust);
  max-width: 62ch;
  margin-bottom: 16px;
}
.guide__p:last-child { margin-bottom: 0; }

/* The product argument, said once. Set apart because it is the only
   sentence on the page that is an argument rather than an instruction. */
.guide__quote {
  margin: 24px 0 0;
  padding: 4px 0 4px 20px;
  border-left: 2px solid var(--violet);
  font-family: var(--display);
  font-size: 20px;
  line-height: 1.5;
  color: var(--starlight);
  max-width: 56ch;
}

/* --- the four moves --- */

.guide__steps {
  list-style: none;
  counter-reset: guide-step;
  display: grid;
  gap: 14px;
  margin: 0 0 22px;
  padding: 0;
}

.guide__step {
  counter-increment: guide-step;
  position: relative;
  padding: 14px 16px 14px 52px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  font-size: 15px;
  line-height: 1.6;
  color: var(--dust);
}

.guide__step::before {
  content: counter(guide-step);
  position: absolute;
  left: 16px;
  top: 14px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--raised);
  border: 1px solid var(--haze);
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--dust);
}

.guide__step strong { color: var(--starlight); font-weight: 600; }

.guide__cta { margin: 0; }

/* --- the three kinds --- */

.guide__kinds { display: grid; gap: 10px; margin-bottom: 20px; }

.guide__kind {
  display: flex;
  gap: 14px;
  align-items: center;
  padding: 14px 16px;
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: color-mix(in srgb, var(--wash) 2%, transparent);
}
.guide__kindstar { display: grid; place-items: center; flex: none; }
.guide__kind strong { display: block; font-size: 15.5px; }
.guide__kind span { font-size: 13.5px; color: var(--faint); }

/* --- a word, and what it means here --- */

.guide__terms { margin: 0 0 20px; }
.guide__terms:last-child { margin-bottom: 0; }

.guide__term {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 15.5px;
  font-weight: 600;
  color: var(--starlight);
  margin-top: 22px;
}
.guide__terms > .guide__term:first-child { margin-top: 0; }

/* The icon is the same one the app uses for that control, so the word and
   the button it names are recognisably the same thing. */
.guide__mark {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--hairline);
  background: var(--night);
  color: var(--dust);
}

.guide__def {
  margin: 6px 0 0 0;
  font-size: 15px;
  line-height: 1.68;
  color: var(--dust);
  max-width: 62ch;
}
/* Lined up under the word rather than under its icon, for every row in a
   list that uses icons — including the rows that have none. A class rather
   than :has(), so this does not quietly stop working in an older browser. */
.guide__def--under { padding-left: 37px; }

/* The space an icon would have taken, holding the left edge steady. */
.guide__mark--none { border-color: transparent; background: none; }

/* --- questions people ask --- */

.guide__asked { display: grid; gap: 22px; }

.guide__q {
  font-family: var(--body);
  font-size: 16px;
  font-weight: 600;
  color: var(--starlight);
  margin-bottom: 6px;
}

.guide__a {
  margin: 0;
  font-size: 15px;
  line-height: 1.68;
  color: var(--dust);
  max-width: 62ch;
}

/* --- the end of it --- */

.guide__foot {
  padding-top: 30px;
  border-top: 1px solid var(--hairline);
}
.guide__foot .guide__p { font-size: 14.5px; color: var(--faint); }

.guide__footlinks {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13.5px;
  color: var(--faint);
  margin-top: 12px;
}
.guide__footlinks a { color: var(--dust); text-decoration: underline; text-underline-offset: 2px; }
.guide__footlinks a:hover { color: var(--starlight); }

/* The two ways to find out before making an account, side by side under the
   form. Wrapping rather than shrinking, so neither is ever a stub. */
.gate__before {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 20px;
}

@media (max-width: 620px) {
  .guide__block { padding: 32px 0; }
  .guide__step { padding-left: 46px; }
  .guide__def--under { padding-left: 0; }
}

/* ------------------------------------------------------------------
   The inner space

   A room, not a filter. It is set apart from every other feed on
   purpose: its own quiet ground, a hairline that reads as a wall, and
   the promise printed at the top rather than tucked into a hint. The
   accent is the dream violet — the colour of the half of the app that
   was never for anybody else.
   ------------------------------------------------------------------ */

.sanctum {
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--violet);
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
  background: linear-gradient(180deg, color-mix(in srgb, var(--violet) 5%, transparent), color-mix(in srgb, var(--wash) 2%, transparent));
  padding: 20px 22px;
  margin-bottom: 16px;
}

.sanctum__title {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--display);
  font-weight: 400;
  font-size: 23px;
  line-height: 1.2;
  margin-bottom: 10px;
}
.sanctum__title svg { color: var(--violet); flex: none; }
.sanctum__toggle {
  display: flex; align-items: center; gap: 9px;
  width: 100%;
  background: none; border: none;
  color: inherit; font: inherit;
  padding: 0;
  cursor: pointer;
  text-align: left;
}
.sanctum__toggle::after {
  content: '−';
  margin-left: auto;
  font-family: var(--body);
  font-size: 18px;
  line-height: 1;
  color: var(--dust);
}
.sanctum--closed .sanctum__toggle::after { content: '+'; }
.sanctum--closed .sanctum__body { display: none; }
.sanctum--closed .sanctum__title { margin-bottom: 0; }

.sanctum__lede {
  font-size: 15px;
  line-height: 1.65;
  color: var(--dust);
  margin-bottom: 16px;
  max-width: 56ch;
}

.sanctum__list {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  display: grid;
  gap: 9px;
}
.sanctum__list li { display: flex; gap: 10px; align-items: flex-start; }
.sanctum__list strong { display: block; font-size: 14px; }
.sanctum__list li > span:last-child > span { font-size: 13px; color: var(--faint); }

.sanctum__tick {
  display: grid;
  place-items: center;
  width: 19px;
  height: 19px;
  margin-top: 2px;
  flex: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--violet) 12%, transparent);
  color: var(--violet);
}

.sanctum__after {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--faint);
  margin: 0;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  max-width: 58ch;
}

.sanctum__write { margin-bottom: 18px; }

/* The floating writing button, while in the room. Violet rather than the
   usual, so a thumb reaching for it can see which one it is before it
   lands — the two do different things. */
.compose-launch--private {
  background: var(--violet);
  color: var(--void);
}

/* What stands where the audience dropdown would be, in a box that has no
   audience to choose. A statement, so it is styled as one rather than as a
   disabled control. */
.lockedaudience {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--violet);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: var(--night);
  padding: 13px 15px;
}
.lockedaudience strong { display: block; font-size: 14px; }
.lockedaudience__mark {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--violet) 12%, transparent);
  color: var(--violet);
}

/* ------------------------------------------------------------------
   Changing who an entry is for
   ------------------------------------------------------------------ */

.audiencepick { display: grid; gap: 8px; }

.audiencepick__row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  width: 100%;
  text-align: left;
  padding: 13px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: border-color .16s, background .16s;
}
.audiencepick__row:hover:not(:disabled) { border-color: var(--haze); background: color-mix(in srgb, var(--wash) 4%, transparent); }
.audiencepick__row:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }

/* The one it already is. Not offered again, and not pretending to be a
   button that does nothing. */
.audiencepick__row.is-current {
  cursor: default;
  border-color: var(--haze);
  background: color-mix(in srgb, var(--wash) 4%, transparent);
}
/* Except the one that is still worth pressing when it is already the answer,
   because what it holds — a list of people — can change while it does not. */
.audiencepick__row.is-current:not(:disabled) { cursor: pointer; }
.audiencepick__row:disabled { opacity: 1; }
.audiencepick__row:disabled:not(.is-current) { opacity: .5; cursor: default; }

.audiencepick__mark {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--hairline);
  background: var(--night);
  color: var(--dust);
}
.audiencepick__what strong { display: block; font-size: 14.5px; margin-bottom: 2px; }
.audiencepick__what span { font-size: 13px; line-height: 1.55; color: var(--faint); }

.audiencepick__now {
  margin-left: auto;
  align-self: center;
  flex: none;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--violet);
}

@media (max-width: 620px) {
  .sanctum { padding: 17px 16px; border-radius: 0 var(--r-md) var(--r-md) 0; }
  .sanctum__title { font-size: 20px; }
}

/* ---------------------------------------------------------------------
   Choosing the people an entry is written for  (public/js/people.js)

   A list of names in a box that scrolls on its own, so a long following
   list cannot push a dialog taller than the phone it is open on. Every
   name may wrap out of its own width — the labels here are people's
   names and none of them was chosen to fit — so each line is cut with an
   ellipsis rather than allowed to paint outside its row, which is what
   made the picture choices unreadable on a phone in September.
   ------------------------------------------------------------------ */

.readers { margin-top: 10px; }
.readers__summary { margin: 0 0 8px; }
.readers__filter { margin-bottom: 8px; }

.readers__list {
  max-height: 34vh;
  overflow-y: auto;
  padding: 0 11px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--wash) 2%, transparent);
}
.readers__list > .field__hint { padding: 12px 0; margin: 0; }

.readers__person {
  display: flex;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  border-radius: var(--r-sm);
}
.readers__person:hover { background: color-mix(in srgb, var(--wash) 4%, transparent); }
.readers__person:focus-visible { outline: 2px solid var(--violet); outline-offset: -2px; }

.readers__person .person__name,
.readers__person .person__why {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.readers__tick {
  display: grid;
  place-items: center;
  width: 22px;
  flex: none;
  margin-left: auto;
  align-self: center;
  color: var(--violet);
}
.readers__person.is-on .person__name { color: var(--violet); }

/* --- feels like an app on a phone ---------------------------------------
   docs/PLAY-STORE.md, R1. None of this changes the page on a desk. It stops
   the things that give a website away in the hand: the page bouncing or
   pulling down to reload, a button's label turning into selected text, a tap
   that waits to see whether it is a double-tap, a control smaller than a
   thumb, and a notch or gesture bar sitting on top of something to press.
   Writing itself stays selectable — only controls are switched off. */

html, body { overscroll-behavior-y: none; }

button, .nav__item, .tabbar__item, .compose-launch, .act, .btn {
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}
a, button, input, select, textarea, label { touch-action: manipulation; }
a, button { -webkit-tap-highlight-color: transparent; }

@media (max-width: 820px) {
  .main {
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
  }
  .tabbar {
    padding-left: max(4px, env(safe-area-inset-left));
    padding-right: max(4px, env(safe-area-inset-right));
  }
  .compose-launch {
    right: max(18px, calc(env(safe-area-inset-right) + 10px));
    bottom: calc(84px + env(safe-area-inset-bottom));
  }
  .backdrop {
    padding: max(16px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right))
             max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  }
  .modal {
    max-height: calc(100dvh - 32px - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  }

  /* 44px is the least a thumb hits without missing. Only controls that were
     under it are lifted; nothing else is resized. */
  .btn, .modal__close { min-height: 44px; }
  .modal__close { min-width: 44px; }
  .act { min-height: 44px; }
  .tabbar__item { min-height: 48px; }

  /* A field of 16px or more is what stops iPhone Safari zooming the whole page
     in when one is tapped. */
  input, select, textarea { font-size: max(16px, 1em); }
}

/* --- reporting and blocking ---------------------------------------------- */

/* The "…" button sits at the far end of a conversation header and a profile's
   actions, where there is a row to push it along. */
.topbar--convo .act--more { margin-left: auto; }

.safety__lede { margin: 0 0 14px; color: var(--secondary); font-size: 13.5px; line-height: 1.55; }
.safety__reasons { display: grid; gap: 2px; margin-bottom: 14px; }
.safety__reason {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  font-size: 13.5px;
  cursor: pointer;
}
.safety__reason:hover { background: color-mix(in srgb, var(--wash) 5%, transparent); }
.safety__reason input { flex: none; width: 17px; height: 17px; margin: 0; }
.safety__also {
  display: flex; align-items: flex-start; gap: 8px;
  margin-top: 4px;
  color: var(--secondary); font-size: 13px; line-height: 1.5;
  cursor: pointer;
}
.safety__also input { flex: none; width: 17px; height: 17px; margin: 1px 0 0; }
.safety__blocked { display: grid; gap: 4px; margin-bottom: 8px; }
.safety__blocked .person { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

.msg__report {
  margin-left: 8px;
  padding: 0;
  border: none;
  background: none;
  color: var(--faint);
  font: inherit;
  cursor: pointer;
  opacity: 0;
  transition: opacity .16s, color .16s;
}
.msg:hover .msg__report,
.msg__report:focus-visible { opacity: 1; }
.msg__report:hover { color: var(--danger); }
/* A phone has no hover, so it is always there, and quiet. */
@media (hover: none) { .msg__report { opacity: .7; } }

/* --- messages: the speech bubble above the moon ----------------------------
   Third in the stack on the right — writing button, moon, messages — and
   sharing their centre line at both widths, like the moon does. */

.msg-launch {
  position: fixed;
  right: 38px;
  bottom: 152px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--panel);
  border: 1px solid var(--hairline);
  color: var(--starlight);
  cursor: pointer;
  z-index: 20;
}
.msg-launch:hover,
.msg-launch[aria-current="page"] { border-color: var(--violet); }
.msg-launch:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }
.msg-launch__badge {
  position: absolute;
  top: -5px; right: -5px;
  min-width: 18px;
  padding: 3px 5px;
  border-radius: 999px;
  background: var(--violet);
  color: var(--void);
  font-family: var(--mono);
  font-size: 10.5px; font-weight: 600; line-height: 1;
  text-align: center;
  box-shadow: 0 0 0 2px var(--deep);
}
@media (max-width: 900px) {
  /* the moon is at 142 and 40 tall here */
  .msg-launch { right: 23px; bottom: 192px; width: 40px; height: 40px; }
}

/* --- finding somebody to message ------------------------------------------ */

.chatfind { margin-bottom: 14px; }

/* --- the corner: one button that unfolds the other three -------------------
   The toggle takes the writing button's old place. Writing, the moon and
   messages sit one slot higher (by `translate`, so the writing button's own
   hover `transform` is untouched) and are folded away until it is pressed.
   Folded means invisible AND unreachable — pointer-events and visibility
   both, so a keyboard cannot tab into a button nobody can see. */

.fab-toggle {
  position: fixed;
  right: 38px; bottom: 32px;
  z-index: 31;
  width: 44px; height: 44px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: linear-gradient(150deg, var(--dawn), var(--violet));
  border: 1px solid color-mix(in srgb, var(--wash) 12%, transparent);
  color: var(--void);
  cursor: pointer;
  box-shadow: 0 14px 40px -12px color-mix(in srgb, var(--dawn) 40%, transparent), 0 0 0 1px var(--void);
}
.fab-toggle svg { transition: transform .2s; }
.fab-toggle[aria-expanded="true"] svg { transform: rotate(45deg); }
.fab-toggle:focus-visible { outline: 2px solid var(--violet); outline-offset: 3px; }
.fab-toggle__dot {
  position: absolute; top: -2px; right: -2px;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--starlight);
  box-shadow: 0 0 0 2px var(--deep);
}

.compose-launch, .night-launch, .msg-launch {
  transition: opacity .18s, visibility .18s, translate .18s, transform .18s;
  opacity: 0; visibility: hidden; pointer-events: none;
}
body.fabs-open .compose-launch,
body.fabs-open .night-launch,
body.fabs-open .msg-launch {
  opacity: 1; visibility: visible; pointer-events: auto;
}
/* Folded, they sit on the toggle and slide out of it. */
body:not(.fabs-open) .compose-launch,
body:not(.fabs-open) .night-launch,
body:not(.fabs-open) .msg-launch { translate: 0 0; }
body.fabs-open .compose-launch,
body.fabs-open .night-launch,
body.fabs-open .msg-launch { translate: 0 -58px; }

@media (max-width: 900px) {
  .fab-toggle {
    right: 23px; width: 40px; height: 40px;
    bottom: calc(89px + env(safe-area-inset-bottom));
  }
}
@media (max-width: 820px) {
  .fab-toggle { right: max(23px, calc(env(safe-area-inset-right) + 15px)); }
}
@media (prefers-reduced-motion: reduce) {
  .compose-launch, .night-launch, .msg-launch, .fab-toggle svg { transition: none; }
}

/* --- the top right corner: the bell, and your picture ----------------------
   Fixed, so it is in the same place on every screen. The sticky title bar
   leaves room for it on its right, or a page's own buttons would sit under it. */

.corner {
  position: fixed;
  top: 14px; right: 24px;
  z-index: 32;
  display: flex; align-items: center; gap: 10px;
}
.corner__bell, .corner__me {
  position: relative;
  width: 40px; height: 40px;
  border-radius: 50%;
  display: grid; place-items: center;
  cursor: pointer; padding: 0;
  border: 1px solid var(--haze);
  background: color-mix(in srgb, var(--deep) 88%, transparent);
  color: var(--starlight);
  transition: background .16s, border-color .16s;
}
.corner__me { overflow: hidden; border-color: var(--haze); }
.corner__bell:hover, .corner__me:hover { border-color: var(--violet); }
.corner__bell[aria-current="page"], .corner__me[aria-current="page"] { border-color: var(--violet); }
.corner__bell:focus-visible, .corner__me:focus-visible { outline: 2px solid var(--violet); outline-offset: 3px; }
.corner__badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px;
  display: grid; place-items: center;
  background: var(--dawn); color: var(--void);
  font-size: 11px; font-weight: 700; line-height: 1;
  box-shadow: 0 0 0 2px var(--deep);
}
.shell:has(~ .corner) .topbar { padding-right: 104px; }
@media (max-width: 820px) {
  .corner { top: max(10px, env(safe-area-inset-top)); right: 14px; gap: 8px; }
}
