:root {
  --bg: #faf7f2;
  --card: #ffffff;
  --ink: #2b2925;
  --muted: #756f65;  /* 4.6:1 on --bg, 5:1 on --card (was #8a8378, 3.5:1) */
  --accent: #7c5cbf;
  --accent-soft: #ece5f8;
  --line: #e8e2d8;
  --serif: Georgia, "Times New Roman", serif;
  --sans: system-ui, -apple-system, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  line-height: 1.6;
}

main {
  max-width: 720px;
  margin: 0 auto;
  padding: 1.5rem 1rem 4rem;
}

/* Header */
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .5rem;
  padding: .9rem 1.25rem;
  background: var(--card);
  border-bottom: 1px solid var(--line);
}
.brand {
  font-family: var(--serif);
  font-size: 1.4rem;
  font-weight: bold;
  color: var(--accent);
  text-decoration: none;
}
#nav-back {
  background: var(--accent-soft);
  color: var(--accent);
  border: none;
  border-radius: 8px;
  font-size: 1.15rem;
  padding: .15rem .6rem;
  cursor: pointer;
  margin-right: .3rem;
}
.site-header nav { display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }
.site-header nav a { color: var(--ink); text-decoration: none; font-size: .95rem; }
.site-header nav a:hover { color: var(--accent); }
/* Which account is signed in. A label, not a destination -- it is a <span>,
   because as a link it read as a fourth nav item and invited a click that only
   went where Settings already goes. Truncates instead of wrapping the nav onto
   a new line on a narrow phone. */
.nav-account {
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: .1rem .5rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent) !important;
  font-size: .85rem !important;
  cursor: default;
}
.nav-logout {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--muted); font-size: .95rem; font-family: inherit;
  margin-left: 2.5rem; /* distanced from the other links to prevent mis-clicks */
}
.nav-logout:hover { color: var(--accent); }

.site-footer { text-align: center; color: var(--muted); font-size: .85rem; padding: 1rem; }

/* Headings */
h1, h2, h3 { font-family: var(--serif); font-weight: 600; }
.page-head { display: flex; align-items: center; justify-content: space-between; }
.subtitle { color: var(--muted); margin-top: -0.5rem; }

/* Buttons and forms */
.button {
  display: inline-block;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: .55rem 1.1rem;
  font-size: .95rem;
  text-decoration: none;
  cursor: pointer;
}
.button:hover { filter: brightness(1.08); }
.button.small { padding: .35rem .8rem; font-size: .85rem; }
.button.danger { background: #b3453f; }
.button-link { margin-left: .8rem; color: var(--muted); text-decoration: none; }
.button-link:hover { color: var(--ink); }
/* A real link that sits under a card's button: its own line, and looks like
   a link (founder, 2026-10-09: "Read the security guide is a link but it
   doesn't look like a link, and it is on the same line as the button").
   .button-link stays for the quiet "Cancel" beside a button. */
.more-link { display: block; width: fit-content; margin-top: .7rem; color: var(--accent);
             text-decoration: underline; text-underline-offset: 2px; }
.more-link:hover { color: var(--ink); }
button.button-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--muted);
  text-decoration: underline;
  cursor: pointer;
}
button.button-link:hover { color: var(--ink); }

/* Style allauth's plain buttons/links to match ours. `.button-link` is exempt:
   it is the deliberately quiet secondary action ("No thanks", "Not now"), and
   painting it the same purple as the primary button made the two look like a
   pair of equal choices. It reads as a link on an <a>; it now does on a
   <button> too. */
main form button:not(.link-button):not(.button-link) {
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: .55rem 1.1rem;
  font-size: .95rem;
  cursor: pointer;
}
main form button.button.danger { background: #b3453f; }

form div { margin-bottom: .9rem; }
label { display: block; font-size: .9rem; color: var(--muted); }
input[type="text"], input[type="date"], input[type="search"],
input[type="email"], input[type="password"], textarea, select {
  width: 100%;
  padding: .55rem .7rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  font: inherit;
  color: var(--ink);
}
textarea { font-family: var(--serif); font-size: 1.05rem; }
.helptext { font-size: .8rem; color: var(--muted); }

/* Entry cards */
.entry-card, .entry-full {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 1.25rem 1.4rem;
  margin: 1.1rem 0;
}
.entry-date {
  font-family: var(--serif);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
}
.entry-location { color: var(--muted); font-size: .85rem; margin-left: .6rem; }
.entry-body { font-family: var(--serif); font-size: 1.08rem; }
.entry-full .entry-body { font-size: 1.15rem; }
.years-ago { color: var(--accent); font-style: italic; }
.title-time { color: var(--muted); font-size: .7em; font-weight: normal; }
.otd-link { margin: -0.4rem 0 0; }
.otd-link a { color: var(--accent); text-decoration: none; font-size: .92rem; }

/* Pinned to the top of the timeline.
   .pin-form is inline because it sits in the row of entry actions next to
   Edit/History/Delete, and a form is block-level by default — which would
   drop those links onto their own line. The button carries .button-link so
   `main form button:not(...)` (line ~123) doesn't paint it a solid pill
   among three text links: the same rule that once turned every emotion chip
   into a submit button. */
.pin-form { display: inline; }
/* Match the three text links it sits beside. `button.button-link` is underlined
   and inherits its font size — right for a standalone "Not now", wrong for the
   first item in a row of .85rem actions, where it would be the odd one out and
   carry a stray leading margin. */
.entry-actions .pin-form button { font-size: .85rem; margin-left: 0; text-decoration: none; }
.entry-actions .pin-form button:hover { color: var(--accent); }
.pin-flag { margin-right: .35rem; font-size: .9rem; }
.pinned-block { margin-bottom: .4rem; }
.pinned-block .entry-card { border-color: var(--accent); background: var(--accent-soft); }

/* Archive browser */
.archive {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: .5rem .9rem;
  margin-top: .8rem;
}
.archive summary { cursor: pointer; color: var(--muted); font-size: .92rem; }
.archive-year { margin: .55rem 0; display: flex; gap: .6rem; flex-wrap: wrap; align-items: baseline; }
.archive-year-link {
  font-family: var(--serif);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  min-width: 3.2rem;
}
.archive-months { display: inline-flex; gap: .35rem; flex-wrap: wrap; }
.archive-month {
  color: var(--accent);
  text-decoration: none;
  font-size: .88rem;
  background: var(--accent-soft);
  border-radius: 999px;
  padding: .05rem .55rem;
}
.archive-month .archive-count { color: var(--muted); font-size: .75em; margin-left: .25rem; }
.archive-month.current, .archive-year-link.current { background: var(--accent); color: #fff; border-radius: 999px; padding: .05rem .55rem; }
.archive-month.current .archive-count { color: #eee; }
.filter-note { color: var(--muted); font-size: .92rem; }
.filter-note a { color: var(--accent); }
.written-at { color: var(--muted); font-size: .8rem; float: right; }

.tag {
  display: inline-block;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 999px;
  padding: .1rem .65rem;
  font-size: .8rem;
  margin-right: .35rem;
}
.reply-count { font-size: .85rem; color: var(--accent); text-decoration: none; }

/* Media */
.media-grid { display: flex; flex-wrap: wrap; gap: .6rem; margin: .8rem 0; }
.media-grid img { max-height: 180px; max-width: 100%; border-radius: 8px; display: block; }
.media-grid video { max-height: 260px; max-width: 100%; border-radius: 8px; }
.media-grid audio { width: 100%; }

/* Thread */
.thread { margin-top: 2rem; }
.comment {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 0 10px 10px 0;
  padding: .8rem 1rem .5rem;
  margin: .8rem 0;
}
.comment-meta { color: var(--muted); font-size: .8rem; }
.comment-body { font-family: var(--serif); }
.comment-body.deleted { color: var(--muted); font-style: italic; }
.comment-actions { margin-left: .6rem; }
.comment-actions a { color: var(--muted); font-size: .8rem; margin-right: .5rem; }
.inline-form { display: inline; margin: 0; }
.link-button {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--muted); font-size: .8rem; text-decoration: underline;
}
.link-button:hover, .comment-actions a:hover { color: #b3453f; }
.edited { font-style: italic; }
.entry-actions a { color: var(--muted); font-size: .85rem; margin-left: .7rem; text-decoration: none; }
.entry-actions a:hover { color: var(--accent); }
.entry-actions a.danger-link:hover { color: #b3453f; }
.entry-card.revision { opacity: .75; border-style: dashed; }
.media-manage { border: 1px solid var(--line); border-radius: 8px; }
.media-manage legend { font-size: .9rem; color: var(--muted); }
.media-remove { display: inline-flex; align-items: center; gap: .4rem; margin-right: 1rem; }
.media-remove img { max-height: 60px; border-radius: 6px; }
.media-remove input { width: auto; }
.comment-children { margin-left: 1.25rem; }
.reply-toggle summary {
  cursor: pointer;
  color: var(--accent);
  font-size: .85rem;
  margin-bottom: .5rem;
}
.comment-form { margin-top: 1rem; }
.empty-thread { color: var(--muted); font-style: italic; }

/* Search */
.search-form { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 1rem 1.2rem; }
.search-filters { display: flex; gap: 1rem; flex-wrap: wrap; }
.search-filters label { flex: 1 1 130px; }
.result-count { color: var(--muted); }

/* Login page */
.login-hero { max-width: 440px; margin: 1.5rem auto 0; text-align: center; }
.login-pitch { font-family: var(--serif); font-size: 1.08rem; }
.login-pitch-sub { color: var(--muted); font-size: .88rem; }
.passkey-big {
  width: 100%;
  padding: 1.05rem 1rem;
  font-size: 1.2rem;
  border-radius: 12px;
  box-shadow: 0 2px 10px rgba(124, 92, 191, .35);
}
.passkey-big::before { content: "👤 "; }
/* The other way in, as a button rather than a small link (founder,
   2026-10-17: "make sign in with password username also a button that is
   easy to see"). Same size and colour as the passkey one ("use the same
   color... the email emoji is enough"); the icons tell them apart. */
.button.password-big {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 1.05rem 1rem;
  font-size: 1.2rem;
  border-radius: 12px;
  box-shadow: 0 2px 10px rgba(124, 92, 191, .35);
}
.button.password-big::before { content: "✉️ "; }
.passkey-none { background: var(--accent-soft); border-radius: 10px; padding: .7rem 1rem;
                margin: .2rem 0 1rem; font-size: .92rem; }
.passkey-none p { margin: .3rem 0; }
.login-divider { border: 0; border-top: 1px solid var(--line); margin: 1.6rem 0 1rem; }
.login-email { text-align: left; }
.login-email-title { font-size: 1.05rem; text-align: center; margin-bottom: .8rem; }

/* Voice notes */
.recorder { margin: .6rem 0; display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; }
.recorder-preview { flex-basis: 100%; }
.recorder-preview audio { width: 100%; max-width: 360px; display: block; margin-bottom: .3rem; }
.recorder-start.recording {
  background: #b3453f;
  border-color: #b3453f;
  color: #fff;
  animation: recording-pulse 1.4s ease-in-out infinite;
}
@keyframes recording-pulse { 50% { opacity: .72; } }
@media (prefers-reduced-motion: reduce) {
  .recorder-start.recording { animation: none; }
}

/* Draft restored / available */
.draft-banner {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  align-items: center;
  justify-content: space-between;
  background: #fdf6e3;
  border: 1px solid #e8d9a0;
  border-radius: 10px;
  padding: .6rem .9rem;
  margin: 0 0 .8rem;
  font-size: .9rem;
}
.draft-banner-text::before { content: "📝 "; }
/* A disabled button should look disabled — while one button says "Saving…",
   the others on the form are greyed rather than silently inert. */
button[disabled], input[type=submit][disabled], .button[disabled] {
  opacity: .55;
  cursor: default;
  box-shadow: none;
}

.draft-banner-warn { background: #fdecea; border-color: #f5b7b1; }
.draft-banner-warn .draft-banner-text::before { content: "⚠️ "; }
.draft-banner-actions { display: flex; gap: .6rem; align-items: center; }

/* Signup — the privacy choice at the door */
.signup-page { max-width: 620px; margin: 1.5rem auto 0; }
.signup-page h1 { text-align: center; margin-bottom: .2rem; }
.signup-page .login-pitch { text-align: center; margin-top: 0; }
.signup-field { margin: .8rem 0; }
.signup-terms { margin: 1.2rem 0; font-size: .9rem; }
/* Grid, not flex: the label text contains inline links, and flexing the label
   would lay each link out as its own column. */
.signup-terms label {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .55rem;
  align-items: start;
  line-height: 1.5;
}
.signup-terms input { margin-top: .25rem; }
.signup-field .helptext ul { margin: .3rem 0 0; padding-left: 1.1rem; }
.privacy-choice { border: 0; padding: 0; margin: 1.6rem 0 1rem; }
.privacy-choice legend { font-weight: 600; font-size: 1.05rem; padding: 0; }
.privacy-intro { color: var(--muted); font-size: .9rem; margin: .3rem 0 .8rem; }
.privacy-card {
  display: block;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: .9rem 1.1rem;
  margin-bottom: .8rem;
  cursor: pointer;
}
.privacy-card:hover { border-color: var(--accent); }
.privacy-card:has(input:checked) {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(124, 92, 191, .25);
}
.privacy-card input { float: left; margin: .35rem .7rem 0 0; }
.privacy-card-body { display: block; overflow: hidden; }
.privacy-card-title { display: block; font-weight: 600; font-size: 1.05rem; }
.privacy-card-sub { display: block; color: var(--muted); font-size: .9rem; margin-bottom: .45rem; }
.privacy-card-points { display: block; font-size: .88rem; line-height: 1.5; }
.privacy-switch { color: var(--muted); font-size: .85rem; }
/* The chooser at /choose-privacy/: each card is its own form, and the
   recommended one is meant to look recommended. Cursor stays default because
   here the button is the click target, not the whole card. */
.privacy-card form, form.privacy-card { cursor: default; }
.privacy-card.recommended {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(124, 92, 191, .25);
}
.privacy-recommended {
  display: inline-block;
  background: var(--accent);
  color: #fff;
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .02em;
  border-radius: 999px;
  padding: .15rem .6rem;
  margin-bottom: .5rem;
}
.privacy-card .button, .privacy-card .button-link { margin-top: .8rem; }
.form-errors { color: #b3261e; }

/* Security checklist */
.security-checklist {
  background: #fdf6e3;
  border: 1px solid #e8d9a0;
  border-radius: 12px;
  padding: 1rem 1.4rem;
  margin: 1.2rem 0;
}
.security-checklist h3 { margin: 0 0 .3rem; }
.security-checklist p { margin: .2rem 0; color: var(--muted); font-size: .92rem; }
.security-checklist ul { list-style: none; padding: 0; margin: .6rem 0; }
.security-checklist li { margin: .45rem 0; }
.security-checklist li.done { color: var(--muted); }
.security-checklist a { color: var(--accent); }
.checklist-more { font-size: .9rem; }
.site-footer a { color: var(--muted); }

/* Landing */
.landing { text-align: center; padding-top: 1.5rem; }
/* Smaller than it was (founder 2026-08-18: "it doesn't have to be so big").
   Clamped rather than fixed so it never dominates a narrow phone screen. */
.landing-title { font-size: clamp(1.45rem, 5vw, 1.85rem); line-height: 1.25; }
.landing-lede {
  font-family: var(--serif);
  font-size: 1.12rem;
  max-width: 560px;
  margin: 0 auto 1rem;
}
.landing-free {
  color: var(--muted);
  font-size: 0.95rem;
  margin: -0.4rem auto 1rem;
}
/* The story row sits directly under the title, so it needs less air above it
   than the principles row further down. */
.landing-story { margin-top: 1.4rem; }
.landing-principles {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 2.2rem 0;
  text-align: left;
}
.principle {
  flex: 1 1 180px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: .3rem 1.1rem .8rem;
}
/* Slightly smaller than a default h3: at three-across these headings are only
   ~14 characters wide, and the full size orphaned a word off most of them. */
.principle h3 { margin-bottom: .2rem; font-size: 1.02rem; line-height: 1.3; }
.principle p { color: var(--muted); font-size: .92rem; margin: 0; }
/* The objection-handler above the CTA. Deliberately not a .principle card:
   it answers a question rather than listing a feature, so it gets the full
   width and the serif voice of the lede instead of sitting in the row. */
.landing-answer {
  max-width: 560px;
  margin: 2.2rem auto 0;
  text-align: left;
}
.landing-answer h3 { margin-bottom: .3rem; }
.landing-answer p {
  font-family: var(--serif);
  font-size: 1.05rem;
  color: var(--muted);
  margin: 0;
}
/* The two-factor page's option cards. The recommended one is ringed the same
   way the privacy chooser rings its recommendation, so "this is the one" reads
   identically in both places a new account has to make a choice. */
.mfa-option h2 { margin: .2rem 0 .1rem; font-size: 1.15rem; }
.mfa-option.recommended {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(124, 92, 191, .25);
}
.mfa-option .button, .mfa-option .button-link { margin-top: .6rem; }
/* "Not now" / "Don't remind me again" -- deliberately plain and equal-weight.
   Making the dismissal hard to find would be the dark pattern this product
   says it does not build. */
.checklist-dismiss { margin-top: .6rem; display: flex; gap: 1.2rem; }
.checklist-dismiss .button-link { margin-left: 0; font-size: .85rem; }
/* Authenticator-app setup, step by step. */
.totp-escape { margin: -.4rem 0 1.2rem; }
.totp-apps { margin: .4rem 0 0; padding-left: 1.2rem; font-size: .92rem; color: var(--muted); }
.totp-apps li { margin-bottom: .25rem; }
.totp-qr { text-align: center; margin: .8rem 0; }
/* White plate behind the code: a QR on a cream background scans less reliably
   on a cheap camera, and this page exists for people who will blame
   themselves when it does not work. */
.totp-qr img {
  width: 190px; height: 190px;
  background: #fff; padding: .6rem; border-radius: 10px;
}
.totp-manual summary { cursor: pointer; font-size: .9rem; color: var(--accent); }
.totp-secret {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 1.02rem; letter-spacing: .06em; word-break: break-all;
  background: var(--accent-soft); border-radius: 8px; padding: .5rem .7rem;
}
/* The name, explained. Typographic rather than another card -- six cards
   already sit either side of it, and this is the one beat on the page that
   should feel like someone saying something rather than listing something. */
.landing-name {
  max-width: 560px;
  margin: 2.4rem auto;
  text-align: left;
}
.landing-name p {
  font-family: var(--serif);
  font-size: 1.08rem;
  margin: 0 0 .8rem;
}
.landing-name-mark {
  font-size: 1.6rem !important;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: .01em;
  margin-bottom: .5rem !important;
}
.landing-name-mark span { opacity: .45; padding: 0 .12em; }
.landing-cta { margin-top: 1.5rem; }
/* Sign up and sign in as equals. Wraps rather than shrinking on a narrow
   phone, so neither ever ends up too small to hit. */
.landing-buttons {
  display: flex;
  gap: .8rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: .9rem;
}
.landing-note { color: var(--muted); font-size: .9rem; }

/* The example journal (/demo/) and the link to it under the landing buttons */
.landing-example-link { margin: -.2rem 0 1rem; }
.landing-example-link a { color: var(--accent); }
.demo-note {
  background: var(--accent-soft);
  border-radius: 10px;
  padding: .8rem 1rem;
  margin: 1rem 0 1.5rem;
}
.demo-note p { margin: 0; }
.demo-date { font-size: 1.35rem; margin: 0 0 .3rem; }
.demo-thread { margin: -.4rem 0 2rem 1.25rem; }
.demo-cta { text-align: center; margin-top: 2.5rem; }

/* App lock */
#app-lock {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.app-lock-brand { font-family: var(--serif); font-size: 2rem; font-weight: bold; color: var(--accent); }
.app-lock-inner p { color: var(--muted); }
.app-lock-alt a { color: var(--muted); font-size: .85rem; }
body.app-locked main, body.app-locked .site-header { filter: blur(14px); }

/* Vault / E2EE */
.faceid-nudge {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .8rem;
  flex-wrap: wrap;
  background: var(--accent-soft);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: .6rem 1rem;
  margin-bottom: 1rem;
  font-size: .92rem;
}
.faceid-nudge-actions { display: flex; gap: .5rem; align-items: center; }
.recovery-key {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 1.15rem;
  letter-spacing: .06em;
  background: var(--accent-soft);
  border-radius: 8px;
  padding: .8rem 1rem;
  text-align: center;
  word-break: break-all;
}
.enc-body .enc-note { color: var(--muted); font-style: italic; }
.enc-clamp { max-height: 14em; overflow: hidden; }
/* Encrypted short field (a location): decrypted in place by enc.js. */
.enc-text { color: var(--muted); }
/* Photo viewer (gallery.js): full-screen, swipe or arrows between photos. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, .93);
  /* Respect the phone's notch/home indicator. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}
.lightbox[hidden] { display: none; }
body.lightbox-open { overflow: hidden; }
.lb-img {
  max-width: 96vw;
  max-height: 88vh;
  object-fit: contain;
  border-radius: 4px;
  /* Let the phone pinch-zoom the photo itself. */
  touch-action: pinch-zoom;
}
.lightbox button {
  background: rgba(255, 255, 255, .12);
  color: #fff;
  border: 0;
  cursor: pointer;
  line-height: 1;
}
.lb-close {
  position: absolute;
  top: calc(env(safe-area-inset-top) + .6rem);
  right: .8rem;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  font-size: 1.6rem;
}
.lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 2.8rem;
  height: 4rem;
  border-radius: 6px;
  font-size: 2rem;
}
.lb-nav[hidden] { display: none; }
.lb-prev { left: .5rem; }
.lb-next { right: .5rem; }
.lb-bar {
  position: absolute;
  bottom: calc(env(safe-area-inset-bottom) + .8rem);
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.2rem;
  color: rgba(255, 255, 255, .8);
  font-size: .9rem;
}
.lb-save {
  color: #fff;
  text-decoration: none;
  background: rgba(255, 255, 255, .12);
  padding: .35rem .9rem;
  border-radius: 999px;
}
@media (hover: none) {
  /* On touch devices the swipe is the gesture; keep the arrows small. */
  .lb-nav { opacity: .55; }
}

/* Encrypted media placeholder: swapped for the real element by enc.js. */
.enc-media {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  min-height: 6rem;
  padding: .8rem;
  border-radius: 8px;
  background: rgba(124, 92, 191, .07);
  color: var(--muted);
  font-size: .9rem;
  text-align: center;
}
.helptext.error { color: #b3453f; }

/* Tag manager */
.tag-row {
  display: flex;
  align-items: center;
  gap: .8rem;
  flex-wrap: wrap;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: .55rem .9rem;
  margin: .5rem 0;
}
.tag-count { color: var(--muted); font-size: .85rem; }
.tag-actions { display: flex; gap: .4rem; margin-left: auto; align-items: center;
               flex-wrap: wrap; max-width: 100%; }
.tag-actions input[type="text"] { width: 11rem; max-width: 100%; min-width: 0; padding: .35rem .55rem; }

/* Journal text never makes the page wider than the screen (monkey test,
   2026-10-05). It found pages up to 2,900px wide -- on phones, tablets and
   desktops alike -- from a long code line, a long URL or unbroken word, or a
   wide markdown table. Long words and links now wrap; code blocks and tables
   scroll sideways inside themselves instead of dragging the page with them. */
.entry-body, .comment-body { overflow-wrap: anywhere; min-width: 0; }
.entry-body pre, .comment-body pre { max-width: 100%; overflow-x: auto; }
.entry-body pre code, .comment-body pre code { overflow-wrap: normal; }
.entry-body table, .comment-body table { display: block; max-width: 100%; overflow-x: auto;
  overflow-wrap: normal; }  /* a table scrolls; it must not split "another" into "anot / her" */
.entry-body img, .comment-body img { max-width: 100%; height: auto; }

/* Back to top */
#to-top {
  position: fixed;
  right: 1.1rem;
  bottom: 1.3rem;
  z-index: 900;
  width: 2.9rem;
  height: 2.9rem;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  color: #fff;
  font-size: 1.3rem;
  cursor: pointer;
  box-shadow: 0 3px 12px rgba(43, 41, 37, .25);
  opacity: .85;
}
#to-top:hover { opacity: 1; }
/* Its twin: down to the page numbers. Same look; sits above it when both show. */
#to-bottom {
  position: fixed; right: 1.1rem; bottom: 1.3rem; z-index: 900;
  width: 2.9rem; height: 2.9rem; border-radius: 50%; border: none;
  background: var(--accent); color: #fff; font-size: 1.3rem; cursor: pointer;
  box-shadow: 0 3px 12px rgba(43, 41, 37, .25); opacity: .85;
}
#to-bottom.stacked { bottom: 4.6rem; }
#to-bottom:hover { opacity: 1; }

/* Misc */
.empty-state {
  text-align: center;
  color: var(--muted);
  background: var(--card);
  border: 1px dashed var(--line);
  border-radius: 12px;
  padding: 2.5rem 1rem;
  margin-top: 1.5rem;
}
.pagination { display: flex; flex-direction: column; align-items: center; gap: .7rem; color: var(--muted);
              margin: 1.5rem 0 4.5rem; }  /* bottom room so the round buttons never cover it */
.pagination-pages { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: .35rem; }
.page-num, .page-gap, .page-step { min-width: 2.2rem; min-height: 2.2rem; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 8px; padding: 0 .4rem; }
.page-num { border: 1px solid var(--line); background: var(--card); }
.page-num.current { background: var(--accent); color: #fff; border-color: var(--accent); font-weight: 600; }
.page-step { padding: 0 .7rem; }
.page-jump { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: .4rem; font-size: .92rem; }
.page-jump input[type="number"] { width: 4.5rem; padding: .3rem .4rem; margin-left: .3rem; }
.pagination a { color: var(--accent); text-decoration: none; }
.messages { list-style: none; max-width: 720px; margin: 1rem auto 0; padding: 0 1rem; }
.msg { background: var(--accent-soft); border-radius: 8px; padding: .5rem .9rem; margin-bottom: .4rem; }

/* Attachments on a composer: whatever is on the form right now, playable and
   removable — including files restored from a draft, which used to be
   invisible. */
.attachments { margin: .6rem 0; }
.attachment {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  padding: .5rem .7rem;
  margin-bottom: .4rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.attachment img { max-height: 64px; border-radius: 6px; }
.attachment audio { max-width: 260px; }
.attachment video { max-height: 90px; }
.attachment-name { font-size: .85rem; color: var(--muted); flex: 1 1 8rem; word-break: break-word; }
.attachment-name.error { color: #b3453f; }
.attachment-remove { color: #b3453f; }

/* ---------------------------------------------------------------- Emotions --
   Who inside you is speaking on a line (journal/emotions.py). The colour comes
   from the name itself, as a hue set inline by the renderer, so a palette of
   any size stays legible without the stylesheet knowing a single emotion by
   name -- and "Fear" is the same colour in an entry written today, in one
   restored from an export, and in a journal imported into another account. */
.emotion {
  display: inline-block;
  background: hsl(var(--emotion-hue, 265) 70% 94%);
  color: hsl(var(--emotion-hue, 265) 55% 32%);
  border-radius: 999px;
  padding: .02rem .55rem;
  margin-right: .35rem;
  font-family: var(--sans);
  font-size: .82em;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: baseline;
}

/* The chip row above a composer. Scrolls sideways rather than wrapping into
   three rows on a phone: the textarea is what should be on screen, not the
   menu above it. */
/* A voice: everything one emotion says, until the next one (2026-10-07). The
   chip names the speaker once; this bar in the same colour shows how far the
   voice runs, so continuing lines don't need a chip each. */
.voice {
  border-left: 3px solid hsl(var(--emotion-hue, 265) 60% 75%);
  padding-left: .7rem;
  margin: .5rem 0;
}
.voice > :first-child { margin-top: 0; }
.voice > :last-child { margin-bottom: 0; }
.emotion-chip-none {
  background: transparent !important;
  border: 1px dashed var(--line) !important;
  color: var(--muted) !important;
}


/* Writing tips page */
.tips-nav { display: flex; flex-wrap: wrap; gap: .4rem .9rem; margin: .5rem 0 1.5rem; font-size: .92rem; }
.tips-section { margin: 2rem 0; }
.tip { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
       padding: .8rem 1rem; margin: .8rem 0; }
.tip h3 { margin: 0 0 .5rem; font-size: 1.02rem; }
.tip-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.tip-pair > div { min-width: 0; }
@media (max-width: 640px) { .tip-pair { grid-template-columns: 1fr; gap: .4rem; } }
.tip-label { font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
             margin-bottom: .25rem; }
.tip-source { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: .88rem;
              background: var(--bg, #f6f3ee); padding: .5rem .6rem; border-radius: 6px; }
.tip-result > :first-child { margin-top: 0; }
.tips-list li { margin: .5rem 0; }

.emotion-bar { margin: .2rem 0 .4rem; }
.emotion-bar-row {
  display: flex;
  align-items: center;
  gap: .35rem;
}
/* Only the chips scroll. The "+" stays put -- a long
   palette must never push the button that grows it off the screen. */
.emotion-chip-scroll {
  display: flex;
  align-items: center;
  gap: .35rem;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  padding-bottom: .25rem;
  -webkit-overflow-scrolling: touch;
}
/* Paired selectors, not another :not() on the blanket rule above: adding a
   :not() there raises that rule for EVERY button and turns Delete purple.
   The second half here matches its weight exactly -- three types (main, form,
   button) and two classes, the blanket rule's two coming from its :not()
   arguments -- and wins the tie by sitting later in the file. */
.emotion-chip,
main form .emotion-bar button.emotion-chip {
  flex: none;
  background: hsl(var(--emotion-hue, 265) 70% 96%);
  color: hsl(var(--emotion-hue, 265) 55% 32%);
  border: 1px solid hsl(var(--emotion-hue, 265) 45% 86%);
  border-radius: 999px;
  padding: .2rem .7rem;
  font-size: .85rem;
  font-family: var(--sans);
  cursor: pointer;
  white-space: nowrap;
}
.emotion-chip.is-on,
main form .emotion-bar button.emotion-chip.is-on {
  background: hsl(var(--emotion-hue, 265) 60% 40%);
  border-color: hsl(var(--emotion-hue, 265) 60% 40%);
  color: #fff;
}
.emotion-chip-new,
main form .emotion-bar button.emotion-chip-new { color: var(--muted); border-style: dashed; background: transparent; }
.emotion-add {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex-wrap: wrap;
  margin-top: .35rem;
}
/* [hidden] is display:none only while nothing else sets display -- a flex
   row would ignore it and the add-a-emotion box would sit open forever. */
.emotion-add[hidden] { display: none; }
.emotion-add input[type="text"] { padding: .3rem .5rem; }
/* Beats the full-width rule every text input in a form otherwise gets. */
input[type="text"].emotion-add-emoji { width: 3.4rem; text-align: center; }
input[type="text"].emotion-add-name { width: 12rem; }
.emotion-add-note { color: var(--muted); font-size: .78rem; flex: 1 1 12rem; }
.emotion-new-form {
  display: flex;
  gap: .4rem;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
.emotion-new-form input[type="text"] { padding: .35rem .55rem; }

/* The landing beat. A worked example, because "each line gets an emotion" is one
   of those ideas that takes a paragraph to explain and a second to see. */
.landing-emotions {
  max-width: 560px;
  margin: 2.4rem auto;
  text-align: left;
}
.landing-emotions h3 { margin-bottom: .3rem; }
.landing-emotions > p {
  font-family: var(--serif);
  font-size: 1.08rem;
  margin: 0 0 .9rem;
}
.landing-demo {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: .9rem 1rem;
  font-family: var(--serif);
  font-size: 1rem;
}
.landing-demo p { margin: 0 0 .45rem; }
.landing-demo p:last-child { margin-bottom: 0; }

/* ------------------------------------------------------- passphrase strength
   The verdict under the passphrase field (static/js/passphrase.js). Colour
   carries the level, but never alone -- the message says the same thing in
   words, because a colour is not readable to everyone and this is the one
   field where misunderstanding is unrecoverable. */
.pp-meter {
  font-size: .85rem;
  line-height: 1.45;
  margin: -.4rem 0 .7rem;
}
.pp-meter[data-level="blocked"],
.pp-meter[data-level="weak"] { color: #b3453f; }
.pp-meter[data-level="fair"] { color: #8a6414; }
.pp-meter[data-level="strong"] { color: #2c7a54; }

/* A generated passphrase, shown because one nobody ever saw is a locked
   journal. Same treatment as the recovery key, which is the same kind of
   "write this down now" moment. */
.generated {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 1.05rem;
  letter-spacing: .04em;
  background: var(--accent-soft);
  border-radius: 8px;
  padding: .7rem .9rem;
  word-break: break-word;
  margin: .2rem 0 .8rem;
}

/* Drag to reorder emotions (static/js/emotion_order.js). touch-action: none
   on the grip only, so a finger on it drags the row instead of scrolling the
   page, while the rest of the page scrolls as usual. */
.drag-handle {
  background: none; border: 0; padding: .2rem .35rem; margin: 0;
  font-size: 1.2rem; line-height: 1; color: var(--muted);
  cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none;
}
.drag-handle:focus-visible { outline: 2px solid var(--accent); border-radius: 6px; }
.emotion-row.dragging { position: relative; z-index: 5; opacity: .92; box-shadow: 0 10px 24px rgba(0,0,0,.2); cursor: grabbing; }
.emotion-row { will-change: transform; }

/* Picking an emotion's colour (Emotions page, 2026-10-10). A swatch shows the
   chip's own background and border, so what you tap is what you get. */
.emotion-colour { flex-basis: 100%; }
.emotion-colour summary { cursor: pointer; color: var(--muted); font-size: .85rem; }
.swatches { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin-top: .4rem; }
/* Two classes, to outweigh the purple `main form button:not(...):not(...)`. */
main form .swatches button.swatch {
  width: 1.9rem; height: 1.9rem; padding: 0; border-radius: 50%; min-height: 0;
  background: hsl(var(--emotion-hue) 70% 88%);
  border: 2px solid hsl(var(--emotion-hue) 55% 55%);
}
main form .swatches button.swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--bg, #fff), 0 0 0 4px hsl(var(--emotion-hue) 55% 32%); }
main form .swatches button.swatch-auto {
  font-size: .8rem; padding: .25rem .7rem; background: transparent; color: var(--muted);
  border: 1px dashed var(--line);
}
main form .swatches button.swatch-auto[aria-pressed="true"] { color: var(--ink); border-style: solid; font-weight: 600; }
.emotion-row.dragging .drag-handle { cursor: grabbing; }
body.emotion-dragging { user-select: none; -webkit-user-select: none; }

/* Tags dropdown on the entry form (static/js/tag_picker.js). */
.tag-combo { position: relative; }
/* In the page, not floating over it: a floating list covered the Save button
   on an iPad (monkey test, 2026-10-13). tag_picker.js keeps it open until a
   tap lands, so closing it never moves a button out from under a finger. */
.tag-suggest {
  list-style: none; margin: .2rem 0 0; padding: .25rem 0;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  max-height: 14rem; overflow-y: auto;
}
.tag-option { padding: .45rem .8rem; cursor: pointer; }
.tag-option:hover, .tag-option.active { background: var(--accent-soft); }
.tag-option[aria-selected="true"] { color: var(--accent); font-weight: 600; }
.form-tips { margin-top: 1.2rem; }
.checkbox-line { display: flex; align-items: center; gap: .5rem; margin: .4rem 0 .8rem; font-weight: normal; }
.checkbox-line input { width: auto; margin: 0; }

/* An emotion looks the same wherever its line ends up (founder, 2026-10-09:
   "if I put # in the text, the emotion also becomes title ... we want emotion
   to be consistent in size and appearance"). The chip sizes itself from the
   text around it (.82em), which is right in a paragraph and wrong in a
   heading or a quote. There it takes the paragraph's size, weight and style
   instead. CSS only, so the server and browser renderers still emit
   identical HTML (test_render_parity). */
.emotion { font-style: normal; letter-spacing: normal; text-transform: none; }
:is(h1, h2, h3, h4, h5, h6) > .emotion {
  font-size: .885rem;  /* = .82em of a 1.08rem entry paragraph */
  font-weight: 600;
  vertical-align: middle;
  position: relative; top: -.1em;
}
.entry-full :is(h1, h2, h3, h4, h5, h6) > .emotion { font-size: .94rem; }
