/* =========================================================================
   Navy and lilac - S16.17 rev 1.13g, SETTLED P62, ruled 29 September 2026.
   (Forest and cream, decided 29 August 2026, was the first palette; U1.)

   THE DARK MODE WAS MADE EASIER ON THE EYE ON 2 OCTOBER 2026 (SETTLED P90,
   the operator's choice after seeing it on his own screen): the grounds are
   charcoal with a hint of blue instead of saturated navy, and the text is a
   warm ivory instead of a bluish white - so the letters differ from the
   page in tint as well as brightness, and the eye is not reading on blue.
   The accent and the bold tone stay periwinkle. Light mode is unchanged.

   ONE set of variable names, two modes. Never a second stylesheet: a second
   stylesheet is how the two drift apart, and light mode is not optional
   (S16.17 rule 6 - dreams are written on waking, in bright light).

   The four behavioural rules are marked where they are implemented, so a
   later change can see what it is breaking.
   ========================================================================= */

:root {
  --shell:          #14161A;
  --page:           #1B1E24;
  --surface:        #25282F;
  --surface-raised: #2A2D34;
  --border:         #33373F;
  --border-strong:  #474C56;
  --text:           #E4DFD2;
  --text-secondary: #BCB7AB;
  --text-muted:     #9A9588;
  --accent:         #9DB0E8;
  /* BOLD PROSE, added 6 September 2026 on the operator's report. In dark
     mode `.prose strong` was `var(--text)` - the SAME colour as the text
     around it - so bold differed by weight alone and, in a near-white
     text colour, barely differed at all. A second tone separates it by hue as
     well - a pale periwinkle since P62 (29 September 2026), warm gold before.
     Lighter than --accent because this is running text at 600 weight, not a
     button. */
  --text-strong:    #C9D6FA;

  --column-max: 46rem;
  --radius: 10px;
}

:root[data-mode="light"] {
  --shell:          #ECEDF6;
  --page:           #F5F5FA;
  --surface:        #FFFFFF;
  --surface-raised: #E6E7F1;
  --border:         #DADCE8;
  --border-strong:  #B9BDD4;
  --text:           #1A1C29;
  --text-secondary: #4B4F63;
  --text-muted:     #646779;
  --accent:         #2B3A7A;
  /* UNCHANGED IN LIGHT MODE, deliberately. The operator's report was that
     bold on the light page is already distinct and bold on the dark page is
     not; near-black at 600 weight on lilac needs no help. The two modes
     differ here because the problem does. */
  --text-strong:    #1A1C29;
}

* { box-sizing: border-box; }

/* S16.5 text size. The four names in app.accounts.text_size map here and
   nowhere else, which is what bounds the range: a pixel value in the column
   could be 4 or 400 and the layout has no defence against either.
   17px is `medium` and is what every page was before this setting existed,
   so an account that never touches it is unchanged.

   It works because everything else in this file is in rem. Changing the root
   size scales the column, the spacing and the type together, rather than
   growing the text inside a box that stays the same size. */
html {
  font-size: 17px;
}
:root[data-text="small"]  { font-size: 15px; }
:root[data-text="large"]  { font-size: 19px; }
:root[data-text="larger"] { font-size: 21px; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--shell);
  color: var(--text);
  font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
  line-height: 1.62;
}

body { font-size: 1rem; }

/* -------------------------------------------------------------------------
   Layout: menu-only, no sidebar (S16.17). One menu button, the conversation,
   the compose box. Fluid widths, never fixed - the later phone phase is then
   a CSS block rather than a rebuild.
   ------------------------------------------------------------------------- */
.shell {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  background: var(--page);
}

.topbar {
  /* Stays put. The menu is the only way out of a long conversation, and
     making somebody scroll to the top to find it is friction at exactly the
     wrong moment. `sticky` also makes this the anchor for the menu panel
     below, which is why the panel lives inside it. */
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.9rem 1.25rem;
  border-bottom: 1px solid var(--border);
  background: var(--shell);

  /* NOTHING FALLS OFF THE END. The light/dark switch vanished entirely on a
     narrow screen: the bar could not wrap, so the last control was simply
     pushed past the right edge with no scrollbar of its own to reach it by.
     A control that is invisible and unreachable is worse than one that has
     moved to a second line. */
  flex-wrap: wrap;
}

.menu-button {
  background: none;
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  border-radius: var(--radius);
  padding: 0.4rem 0.7rem;
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}
.menu-button:hover { color: var(--text); border-color: var(--text-muted); }

/* Rule 1: one accent, used sparingly. Wordmark and send control only. */
/* THE LOGO - added 6 September 2026, operator's ruling on placement: the owl
   alone in the top bar, the owl with the name on the login card.

   PAINTED WITH A CSS MASK RATHER THAN AN <img>, and that is the whole reason
   it works in both themes from ONE file. An <img> carries its own colours
   and cannot be recoloured; the SVG is traced as a single silhouette, so
   masking `currentColor` through it makes the mark take whatever colour the
   text around it has - periwinkle on the navy ground, navy on the lilac
   (P62; gold on forest and olive on cream until 29 September 2026).

   NOT INLINED INTO EVERY PAGE EITHER. The owl is 18KB of path data and the
   top bar is on every page; inlining would put that in every response
   forever rather than fetching it once and caching it.

   THE URL COMES FROM A CUSTOM PROPERTY SET IN base.html, not from a literal
   here, because it has to carry the asset version. A URL written in this
   file would be re-fetched only when THIS file changed - so a new logo with
   an unchanged stylesheet would stay cached, which is the fault the
   versioning was built to fix, one level down. */
.brand-owl,
.brand-mark {
  display: block;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* 1488 x 2600 in the file, so a shade over half as wide as it is tall. The
   height is tied to the bar's other controls rather than to a round number,
   so it grows and shrinks with them. */
.brand-owl {
  width: 1.25rem;
  height: 2.2rem;
  -webkit-mask-image: var(--owl-url);
  mask-image: var(--owl-url);
}

/* 3756 x 3004 after the type was enlarged - now wider than tall. The height
   is what the eye reads as "how big is the logo", so it is held roughly
   where it was and the width follows the file. */
.brand-mark {
  width: 11.1rem;
  height: 8.9rem;
  -webkit-mask-image: var(--mark-url);
  mask-image: var(--mark-url);
}
.brand-heading {
  display: flex;
  justify-content: center;
  /* Nothing below: the sub-line owns the gap now, so the two are set in one
     place rather than added together from two. */
  margin: 0;
}

.wordmark {
  color: var(--accent);
  letter-spacing: 0.02em;
  font-size: 1rem;
  margin: 0;
  font-weight: normal;
  /* It is the only elastic thing in the bar, so it is the thing that gives
     way. It shrinks; the controls keep their size and stay hittable. */
  flex: 0 0 auto;
  min-width: 0;
  display: flex;
  align-items: center;
}

.spacer { flex: 1; }

/* BACK TO CONVERSATION - added 6 September 2026.
   It borrows .menu-button entirely, so it cannot drift away from the two
   controls either side of it: one set of paddings, one border, one hover.
   Only the things an anchor needs differ. */
.backlink {
  text-decoration: none;
  display: inline-block;
  white-space: nowrap;      /* two words must not wrap inside the border */
}

/* On the chat page. Faded, and a <span> rather than a link - see
   _topbar.html for why that is safety rather than tidiness. */
.backlink[aria-disabled="true"] {
  opacity: 0.4;
  cursor: default;
}
.backlink[aria-disabled="true"]:hover {
  /* Explicitly cancels .menu-button:hover. Without this the control lights
     up under the mouse and invites a click it will not answer. */
  color: var(--text-secondary);
  border-color: var(--border-strong);
}

/* The label is short on a phone and long everywhere else. Two spans swapped
   in CSS rather than one string chosen in the template: the width has to
   change with the VIEWPORT, and the server does not know the viewport. */
.backlink .short { display: none; }

.conversation {
  flex: 1;
  width: 100%;
  max-width: var(--column-max);
  margin: 0 auto;
  padding: 2rem 1.25rem 1rem;
}

/* Rule 2: the dreamer's words are BOXED; the agent's run as plain text.
   This gives the dreamer's own words visual weight, which is the correct
   hierarchy for a method built on their associations. */
.turn-user {
  background: var(--surface);          /* Rule 3: layered, not contrasted */
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.85rem 1.1rem;
  margin: 1.5rem 0;
  white-space: pre-wrap;
}

.turn-agent {
  margin: 1.5rem 0 2.25rem;
  white-space: pre-wrap;
}

.turn-agent.incomplete {
  border-left: 2px solid var(--border-strong);
  padding-left: 1rem;
}
.incomplete-note {
  display: block;
  margin-top: 0.6rem;
  color: var(--text-muted);
  font-size: 0.85rem;
  font-style: italic;
}

/* Rule 5: the waiting state is the most important element on the page.
   Mean response is 38 seconds. It must be calm and unmissable, and must
   never read as an error or a stall. */
.waiting {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.5rem 0;
  color: var(--text-secondary);
}
.waiting[hidden] { display: none; }

.breath {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.35;
  animation: breathe 3.4s ease-in-out infinite;
}
@keyframes breathe {
  0%, 100% { opacity: 0.25; transform: scale(0.85); }
  50%      { opacity: 0.75; transform: scale(1.1); }
}
@media (prefers-reduced-motion: reduce) {
  .breath { animation: none; opacity: 0.55; }
}

.compose {
  position: sticky;
  bottom: 0;
  background: var(--page);
  border-top: 1px solid var(--border);
  padding: 1rem 1.25rem 1.5rem;
}
/* SEND SITS BESIDE THE BOX ON A DESKTOP AND BELOW IT ON A PHONE - operator's
   ruling, 6 September 2026, after two wrong answers.

   THE FIRST ATTEMPT PUT THE BUTTON IN THE BOX'S CORNER and gained nothing.
   A textarea cannot flow text around an overlapping element, so clearing the
   button meant `padding-right` - which shortens EVERY line, not the two
   beside the button. The box got wider and the text handed the same column
   straight back. The operator photographed it: identical usable width, one
   more thing in the way.

   Only a `contenteditable` div can wrap text around a float, and swapping
   the compose box for one would put paste handling, mobile keyboards, the
   character limit and accessibility back on the table for a few characters
   of width.

   SO: BELOW, AND ONLY WHERE THE PROBLEM IS. On a desktop the box is capped
   at 46rem and centred, so the button sits in space the text was never going
   to use - moving it there would cost 3rem of height and gain nothing. The
   fault only exists at phone width. See the media query at the foot of this
   file. */
.compose-inner {
  width: 100%;
  max-width: var(--column-max);
  margin: 0 auto;
  display: flex;
  gap: 0.75rem;
  align-items: flex-end;
}

textarea {
  flex: 1;
  resize: vertical;
  /* Room for about four lines before it scrolls. A dream runs to a paragraph
     and people edit as they recall - seeing one line at a time means writing
     into a slot, which is the wrong invitation for the one thing this site
     asks anyone to do. It also grows as you type; see chat.html. */
  min-height: 7.5rem;
  max-height: 40vh;
  padding: 0.75rem 0.9rem;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  /* 16px MINIMUM, or iOS zooms the whole page when the field is tapped -
     and a floor rather than a fixed value, so that S16.5's text size still
     reaches the one box on the site people actually write in. Somebody who
     enlarged the type because they cannot read the small size would
     otherwise find the compose box exempt from their own setting. */
  font-size: max(16px, 0.95rem);
  line-height: 1.5;
}
textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.send {
  background: var(--accent);
  color: var(--shell);
  border: none;
  border-radius: var(--radius);
  padding: 0.75rem 1.4rem;
  font: inherit;
  font-size: 0.95rem;
  cursor: pointer;
}
.send:disabled { opacity: 0.45; cursor: default; }

/* The Send button where it now lives. Only inside the compose bar - the
   Save buttons on My Account use the same class and must stay where they
   are. */


.notice {
  max-width: var(--column-max);
  margin: 0.75rem auto 0;
  color: var(--text-muted);
  font-size: 0.9rem;
}
.notice.error { color: var(--text-secondary); }

/* Operator spend alert - S16.13, ADMIN ONLY -------------------------------
   Never rendered for a dreamer; the route decides, not the template.
   Deliberately quiet: the accent rule carries it, not colour. This is a
   reading for the operator, not an alarm for the room, and S4.7's objection
   to a moving number during a session applies with less force to an
   operational figure than to a dreamer's balance - but it is the reason this
   is understated rather than red. */
.spend-alert {
  max-width: var(--column-max);
  margin: 0.75rem auto 0;
  padding: 0.5rem 0.75rem;
  border-left: 3px solid var(--accent);
  color: var(--text-muted);
  font-size: 0.85rem;
  letter-spacing: 0.01em;
}

/* Login ------------------------------------------------------------------ */
.centred {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 1.5rem;
}
.card {
  width: 100%;
  max-width: 24rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 2rem;
}
.card h1 { color: var(--accent); font-size: 1.15rem; font-weight: normal; margin: 0 0 0.35rem; }
/* "Sign in to continue." BELONGS TO THE FORM, NOT TO THE LOGO - operator's
   ruling, 6 September 2026. It sat equidistant between the mark above and
   the Email field below, so it read as a caption on the logo rather than as
   the instruction it is. Proximity is what groups things; the gap above it
   is now larger than the gap below. */
.card p.sub {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 1.75rem 0 1rem;
}
.card label { display: block; font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.3rem; }
.card input {
  width: 100%;
  padding: 0.65rem 0.8rem;
  margin-bottom: 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--page);
  color: var(--text);
  font-family: inherit;
  font-size: 16px;
}
.card input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.card .send { width: 100%; }
.error-text { color: var(--text); background: var(--surface-raised);
  border-left: 2px solid var(--accent); padding: 0.6rem 0.8rem;
  border-radius: 4px; font-size: 0.9rem; margin-bottom: 1rem; }

@media (max-width: 40rem) {
  .conversation { padding: 1.25rem 1rem 0.5rem; }
  .compose { padding: 0.75rem 1rem 1rem; }

  /* THE TOP BAR RUNS OUT OF ROOM ON A PHONE. Menu, the wordmark and Light
     already very nearly fill 375px; "Back to conversation" is about another
     150px and would push the bar into wrapping.
     Operator's ruling, 6 September 2026: the BUTTON gives way, not the
     wordmark and not the layout. */
  .backlink .long  { display: none; }
  .backlink .short { display: inline; }

  /* SEND DROPS BELOW THE BOX. Every line then gets the full width of the
     screen, which is about 5.5rem more than it had - eight to ten characters
     on a phone, on every line of every dream.

     IT COSTS ABOUT 3rem OF HEIGHT, once, and that is the whole trade. The
     compose box already grows to 40% of the window, so what this spends
     comes off the conversation above it. `wrap` rather than `column`, so the
     button keeps its own baseline behaviour and only moves when it must. */
  .compose-inner { flex-wrap: wrap; }
  .compose-inner .send { width: 100%; }

  /* Ordered by what can be given up. The wordmark is decoration once you are
     inside the service; Menu, the way back, and the light/dark switch are
     all things somebody presses. */
  .topbar { gap: 0.6rem; padding: 0.75rem 0.9rem; }
  .wordmark { font-size: 0.85rem; }
}

/* A reply that arrived after the dreamer closed the page. Quiet by design:
   it explains rather than alerts, and it appears once (S16.17 - nothing may
   compete with the dreamer's attention, and markers extinguish by use). */
.waited {
  margin: 1.75rem 0 -0.5rem;
  color: var(--text-muted);
  font-size: 0.9rem;
  font-style: italic;
  border-left: 2px solid var(--border-strong);
  padding-left: 0.9rem;
}

/* The menu. Shut by default; nothing here may compete with the conversation
   (S16.17), so it is a panel that appears on a deliberate act, not a rail
   sitting permanently on the page. */
.menu-panel {
  /* Anchored to the sticky bar, so it travels with it. */
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 1.25rem;
  z-index: 20;
  min-width: 14rem;
  padding: 0.4rem;
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}
.menu-panel[hidden] { display: none; }
.menu-panel form { margin: 0; }

.menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: 6px;
  padding: 0.6rem 0.75rem;
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
  text-decoration: none;
  cursor: pointer;
}
.menu-item:hover { background: var(--surface); }

/* Step 81 (his "(B)", 10 October 2026): a small outline icon before each
   line, in the text colour - no colour of its own (rule 1: one accent,
   used sparingly). Flex so the icon and the words stay on one line and the
   first-run dot still follows the words. */
.menu-item { display: flex; align-items: center; gap: 0.7rem; }
.menu-icon { flex: 0 0 auto; width: 1.15rem; height: 1.15rem;
             opacity: 0.85; }

.menu-rule {
  height: 1px;
  background: var(--border);
  margin: 0.4rem 0.25rem;
}

/* Erasure is set apart, not made frightening. It is a legitimate thing to
   want, and someone reaching for it should not be scolded by the interface. */
.menu-item-grave { color: var(--text-secondary); }

/* First-run guidance - S16.17, SETTLED U4.
   ---------------------------------------------------------------------------
   A dot. Not a badge, not a count, not a colour that means "warning", and
   nothing that moves. Escalation is prohibited: this states itself once and
   stops. It must be findable by someone looking and ignorable by someone
   who is not, which is why it is small, unlabelled, and made of the accent
   already on the page rather than a new colour the eye has to categorise
   (rule 1: one accent, used sparingly).

   It extinguishes by use, and only by use (SETTLED U4: the stop after
   ONBOARDING_HINT_LOGINS sign-ins was removed on 2 September 2026). That is
   decided server-side; nothing here can keep a marker alive that
   app/menu.py has retired. */
.marker {
  display: inline-block;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.85;
  margin-left: 0.45rem;
  vertical-align: 0.15em;
  /* Explicitly still. A pulsing dot is an escalation with a short period. */
  animation: none;
}

.menu-item { position: relative; }

/* On the button the dot sits at the corner rather than after the word, so
   the bar's width does not change when guidance retires. A top bar that
   reflows on the sixth sign-in reads as a fault. */
.menu-button.has-marker { position: relative; }
.menu-button.has-marker .marker {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  margin: 0;
}

/* The dot carries meaning, so it is announced to a screen reader in words.
   Off-screen rather than `display: none`, which is not read at all. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.page-heading {
  color: var(--accent);
  font-weight: normal;
  font-size: 1.3rem;
  margin: 0 0 1.25rem;
}

/* S16.17: every page behind the menu opens with one or two plain lines
   saying what it is for. Permanent, not onboarding-only. Set below the
   heading and above everything else, quieter than body text but not so
   quiet it reads as a footnote. */
.page-description {
  color: var(--text-secondary);
  margin: -0.5rem 0 2rem;
  max-width: 34rem;
}

/* A page whose feature is not built. Stated once, in the muted register, and
   never dressed up as a countdown or a promise. */
.not-yet {
  color: var(--text-muted);
  font-style: italic;
}

/* Long-form reading - the "Read this" page. Narrower than the chat column:
   prose read straight through wants a shorter line than a conversation
   scanned in turns. */
.prose { max-width: 36rem; }
.prose p { margin: 0 0 1.25rem; }
/* Step 71: Read this is the operator's text now; a long address or an
   unbroken run in it wraps instead of pushing a phone sideways. */
.prose p, .prose li { overflow-wrap: anywhere; }
/* Step 72: the line under About Me's title is his text too. */
.page-description { overflow-wrap: anywhere; }
.prose strong { font-weight: 600; color: var(--text-strong); }
.erase-form { margin-top: 2rem; }
.erase-form label { display: block; margin-bottom: 0.4rem; color: var(--text-secondary); }
.erase-form input {
  width: 100%;
  max-width: 14rem;
  padding: 0.65rem 0.8rem;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 16px;
}
.erase-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.25rem;
}
.send-grave { background: var(--surface-raised); color: var(--text); border: 1px solid var(--border-strong); }

.destroyed-list {
  margin: 0 0 1.25rem;
  padding-left: 1.1rem;
  color: var(--text-secondary);
  font-size: 0.95rem;
}

/* The one sentence on the erase page that must not be skimmed. Weight and
   space, not colour or alarm - the reader is doing something legitimate and
   should not be shouted at, only made certain they have understood. */
.grave {
  font-size: 1.08rem;
  line-height: 1.55;
  color: var(--text);
  border-left: 2px solid var(--accent);
  padding-left: 1.1rem;
  margin: 0 0 1.5rem;
}

/* Room beneath the last reply so it can sit at the top of the screen. Sized
   by JavaScript and collapses to nothing when the reply is tall enough to
   fill the viewport on its own. */
.scroll-spacer { height: 0; }

/* -------------------------------------------------------------------------
   S16.3 signup. Added 3 September 2026.

   No new colours and no second palette: everything here is built from the
   variables at the top of this file, so both modes follow automatically
   (S16.17 rule 6).
   ------------------------------------------------------------------------- */
.card-wide { max-width: 30rem; }

/* A notice given the accent rule and real space rather than the muted grey
   of a hint. It was S16.3's recovery notice beneath the sign-up email field;
   SETTLED P82 took that notice off the sign-up page (1 October 2026), and
   the only page using this class now is contact.html. */
.recovery-notice {
  margin: 0.2rem 0 1.5rem;
  padding: 0.75rem 0.9rem;
  border-left: 2px solid var(--accent);
  background: var(--surface-raised);
  color: var(--text-secondary);
  font-size: 0.92rem;
  line-height: 1.55;
  border-radius: 0 var(--radius) var(--radius) 0;
}

.field-note {
  color: var(--text-muted);
  font-size: 0.85rem;
  margin: 0.35rem 0 1.25rem;
}
/* PROSE LINKS. There was NO rule for `a` anywhere in this file until
   6 September 2026, so every inline link in a sentence fell through to the
   BROWSER'S OWN blue-and-purple. The operator reported it on two pages -
   "write to us here" on Leave feedback, and Erase everything - but it was
   every prose link on the site, and a visited one on the dark background
   was close to unreadable.

   One rule, both states. `visited` is stated explicitly because a browser
   default for it exists whether or not we set one. */
a { color: var(--accent); }
a:visited { color: var(--accent); }
a:hover { color: var(--text); }

.field-note a { color: var(--text-secondary); }

.consent { margin: 1.25rem 0 1.5rem; }

/* The whole row is the target, not just the box. A 13px checkbox is a hard
   thing to hit on a phone at three in the morning, which is when I20 says a
   good share of this service is used. */
.check {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
  font-size: 0.9rem;
  color: var(--text-secondary);
  cursor: pointer;
}
.check input {
  width: auto;
  margin: 0.3rem 0 0;
  accent-color: var(--accent);
  flex: 0 0 auto;
}
.check a { color: var(--text); }

.resend-form { margin-top: 1rem; }

/* -------------------------------------------------------------------------
   S16.7 About Me, S16.15 the journal, S16.8 My Stuff. 3 September 2026.
   Existing variables only; both modes follow automatically.
   ------------------------------------------------------------------------- */
.about-form textarea, .journal-form textarea {
  width: 100%;
  background: var(--surface-raised);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem;
  font: inherit;
  line-height: 1.6;
  resize: vertical;
}
.about-form textarea:focus, .journal-form textarea:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
.journal-form label {
  display: block; margin: 1.1rem 0 0.3rem;
  color: var(--text-secondary); font-size: 0.85rem;
}
.journal-form input[type="date"], .journal-form input[type="text"] {
  background: var(--surface-raised); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.55rem 0.7rem; font: inherit;
}
.journal-form .send { margin-top: 1.4rem; }
#count.over { color: var(--accent); }

.filters {
  display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center;
  margin: 1.25rem 0 1.75rem;
}
.filters input, .filters select {
  background: var(--surface-raised); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.45rem 0.6rem; font: inherit; font-size: 0.9rem;
}

.records { list-style: none; margin: 0; padding: 0; }
.record {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 1rem 1.1rem; margin-bottom: 0.9rem;
}
/* THE DATE CARRIES THE ACCENT and the kind does not - operator's ruling,
   6 September 2026. Both were the same muted grey, which made a list of
   twenty rows a wall of identical small text with nothing to scan by. The
   date is the thing somebody scans for; DREAM and CONVERSATION repeat down
   the column and are already told by the coloured rule on the left edge. */
.record-head {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.5rem;
}
.record-date { color: var(--accent); font-weight: 600; }
/* Distinguishable at a glance (S16.8) - by a rule down the edge rather than
   by colour alone, which nobody can see in a screenshot or with the light
   variant behaving differently. */
.record-journal { border-left: 3px solid var(--accent); }
.record-conversation { border-left: 3px solid var(--border-strong); }
.record-kind { letter-spacing: 0.06em; text-transform: uppercase; }
/* THE ROW WRAPS AND THE FIELD IS ALLOWED TO SHRINK - operator's report,
   6 September 2026, from two phone-width screenshots.

   The Rename button was CUT OFF at the right edge while the Delete button
   below it wrapped onto a new line quite happily, and the whole page had
   grown a horizontal scrollbar. Two separate causes, both here:

   1. No `flex-wrap`, so the row could not put the button on a second line
      the way the actions row beneath it does.
   2. **`flex: 1` DOES NOT MEAN "shrink to fit".** A flex item's default
      `min-width: auto` refuses to go below its content, so a long title
      pushed the row wider than the panel, the panel wider than the page, and
      the page grew a scrollbar - which is what pushed the light/dark switch
      off the top bar as well. `min-width: 0` is what actually lets it
      shrink, and its absence is the single commonest cause of an
      unexplained horizontal scrollbar in a flex layout. */
.rename {
  display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.6rem;
}
/* THE TITLE IS AN INPUT, NOT A HEADING - it is editable in place (S16.8),
   which is why it cannot simply be an <h4>. It still has to READ as the
   title of the row, so it carries the accent and the weight a heading
   would. It keeps its invisible border until hovered, so the row stays a
   list rather than a form. */
.rename input {
  flex: 1 1 12rem; min-width: 0;
  background: transparent; color: var(--accent);
  font-weight: 600; font-size: 1.05rem;
  border: 1px solid transparent; border-radius: var(--radius);
  padding: 0.35rem 0.5rem; font-family: inherit;
}
.rename input::placeholder { color: var(--text-muted); font-weight: normal; }
.rename input:hover, .rename input:focus {
  border-color: var(--border); background: var(--surface-raised);
}
/* Three lines of the dream, no more. The list is for finding one, not for
   reading one. */
.record-text {
  color: var(--text-secondary); margin: 0 0 0.75rem;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.record-actions {
  display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center;
}
.record-actions form { margin: 0; }

/* S16.20 delete. One screen, one confirmation (SETTLED P9). Since step 72
   (SETTLED P101) the row's Delete opens the warning with Cancel and Delete,
   and the second Delete is the confirmation. The row's own button is quiet
   rather than red and shouting: the
   person using this may be removing something painful, and S16.20's own
   reasoning is that friction is not care. */
.delete-form {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
  margin: 0.6rem 0 0; width: 100%;
}
.delete-confirm { margin: 0; flex: 1 1 100%; font-size: 0.85rem; }
/* SETTLED P101: the row's Delete opens the warning with Cancel and Delete
   (a <details>, so it works with no script). */
.delete-ask { width: 100%; margin: 0.6rem 0 0; }
.delete-ask > summary { display: inline-block; list-style: none; }
.delete-ask > summary::-webkit-details-marker { display: none; }
.delete-ask[open] > summary { display: none; }
.delete-ask .delete-form {
  margin: 0; padding: 0.7rem 0.8rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
}
.delete-cancel { text-decoration: none; }
.delete-ask .delete-go {
  /* THE ONE THAT DELETES MUST NOT LOOK LIKE CANCEL (step-72 review: the two
     were identical, side by side). Filled, like the site's other
     committing buttons; still not red and shouting. */
  background: var(--accent); color: var(--shell);
  border: 1px solid var(--accent);
  padding: 0.4rem 0.7rem; font-size: 0.9rem;
}
.delete-button {
  background: transparent; color: var(--text-muted);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.4rem 0.8rem; font: inherit; font-size: 0.85rem; cursor: pointer;
}
.delete-button:hover { color: var(--text); border-color: var(--border-strong); }

/* S11.3 placeholder documents, SETTLED ST5. The banner is a safety device,
   not decoration: it is what stops somebody accepting a document that does
   not exist. Given the accent rule and real weight for that reason. */
.draft-note {
  border: 1px solid var(--accent);
  border-left-width: 3px;
  border-radius: var(--radius);
  background: var(--surface-raised);
  padding: 1rem 1.1rem;
  margin: 0 0 1.75rem;
}
.draft-note p { margin: 0 0 0.6rem; color: var(--text-secondary); }
.draft-note p:last-child { margin-bottom: 0; }
.draft-note strong { color: var(--text); }
.document-text { color: var(--text-secondary); line-height: 1.7; }

/* -------------------------------------------------------------------------
   S16.5 My Account. Added 4 September 2026.

   Existing variables only, so both modes follow automatically (S16.17 rule
   6). No new colour: this page holds a balance, a password field and the one
   operation that can lose an account, and every one of those is a place
   where a colour that means "danger" would be the interface having an
   opinion about what the dreamer is doing.
   ------------------------------------------------------------------------- */
.panel {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 1.25rem 1.35rem;
  margin: 0 0 1.5rem;
}
/* PANEL HEADINGS CARRY THE ACCENT - operator's ruling, 6 September 2026,
   overruling S16.17 rule 1 ("one accent, wordmark and send control only").

   The photograph that settled it: on the Display panel, the words "Follow my
   device" were more prominent than the heading "Display" above them. A
   heading quieter than the options beneath it is not a heading. My Account
   is six stacked panels and the operator has to find one of them at a
   glance, which is not the reading task S16.17 rule 1 was protecting - that
   rule is about the CONVERSATION, where nothing may compete with the
   dreamer's attention. This page is not a conversation. */
.panel h3 {
  margin: 0 0 0.75rem;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--accent);
  letter-spacing: 0.03em;
}
.panel p { margin: 0 0 0.9rem; }
.panel p:last-child { margin-bottom: 0; }
.panel form { margin: 0; }

/* The balance. S16.6 shows CREDITS, and this is the only place on the site
   where a balance appears at all (S4.7, SETTLED P5). Given size because it
   is the answer to the question that brought the dreamer here, and no
   colour, because a number that turns amber as it falls is the ambient
   financial pressure S4.7 exists to keep away from this work. */
.balance {
  font-size: 2rem;
  line-height: 1.2;
  color: var(--text);
  margin: 0 0 0.35rem;
}
.balance-unit { color: var(--text-muted); font-size: 1rem; }

/* S16.6's low-balance warning. Two rungs, ONE class, and that is the point:
   the second rung is a plainer sentence, not a louder one. There is no
   `.low-credit.urgent`, no colour, no icon, no weight change and no
   animation, because S16.6 forbids the prompt growing more insistent and
   the obvious way to break that rule is to style the escalation rather than
   to word it.

   Muted rather than accented, unlike .spend-alert - that one is the
   operator's own reading of his business and this one is money pressure
   near a dreamer, which S4.7 wants as quiet as it can be while still being
   read. */
.low-credit {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0.75rem 0 0;
}
.low-credit a { color: var(--text-secondary); }

/* On the session screen it sits directly under .shell rather than inside a
   panel, so it needs the reading column the conversation itself uses -
   otherwise it runs the full width of the window, which is the opposite of
   quiet. Same treatment .spend-alert gets in the same position. */
.shell > .low-credit {
  max-width: var(--column-max);
  margin: 0.75rem auto 0;
  padding: 0 0.75rem;
}

/* SETTLED P106 (h), step 74: the operator's site notice on the members'
   pages. Worded, never styled (U11): the page's own text colour inside a
   plain bordered box, no accent, no icon, no animation. `anywhere` because
   the words are his and may hold a long address. */
.site-notice {
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  padding: 0.6rem 0.8rem;
  margin: 0.75rem 0 1rem;
  color: var(--text);
  font-size: 0.95rem;
  overflow-wrap: anywhere;
}
.site-notice p { margin: 0; }
.site-notice p + p { margin-top: 0.35rem; }
.shell > .site-notice {
  max-width: var(--column-max);
  margin: 0.75rem auto 0;
  box-sizing: border-box;
  width: calc(100% - 1.5rem);
}

/* S16.6 requires the rate to be visible "so a change is seen rather than
   discovered", and the day-of-use deal disclosed rather than buried. */
.rate-note {
  color: var(--text-secondary);
  font-size: 0.9rem;
  margin: 0.75rem 0 0;
}

.setting-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem 1rem;
  margin-bottom: 0.75rem;
}
.setting-value { color: var(--text); }
.setting-label { color: var(--text-muted); font-size: 0.85rem; }

.account-form label {
  display: block;
  margin: 0.9rem 0 0.3rem;
  color: var(--text-secondary);
  font-size: 0.85rem;
}
.account-form input[type="email"],
.account-form input[type="password"],
.account-form input[type="text"] {
  width: 100%;
  max-width: 22rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--surface-raised);
  color: var(--text);
  font-family: inherit;
  font-size: max(16px, 0.95rem);
}
.account-form input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* THE BUTTON GOES UNDER THE FIELD, NOT BESIDE IT - operator's ruling,
   6 September 2026, from a screenshot of My Account.

   It sat beside the field for no designed reason: an <input> and a <button>
   are both inline by default, the field is capped at 22rem, and the panel is
   wider than that, so the button simply flowed into the space left over. It
   looked deliberate and was not.

   IT MATTERS MOST WHERE THE SCREENSHOT WAS NOT TAKEN. On a phone there is no
   space left over, so the button wraps to the next line anyway - but wrapped
   by accident, its position depending on the length of the label above it.
   Under the field on every width is one predictable place to look.

   `display: block` on both, rather than a flex column on the form: the form
   also holds labels and hidden inputs, and turning it into a flex container
   would make every one of them a flex item with its own spacing to argue
   about. */
.account-form input[type="email"],
.account-form input[type="password"],
.account-form input[type="text"] { display: block; }
.account-form .send,
.account-form .button-quiet { display: block; margin-top: 1.1rem; }

/* Radio groups for theme, text size and the history setting. The whole row
   is the target for the reason the signup checkboxes are: a 13px control is
   a hard thing to hit on a phone. */
.choice {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin-bottom: 0.65rem;
  cursor: pointer;
}
/* THE RADIOS ARE DRAWN HERE RATHER THAN LEFT TO THE BROWSER, and the reason
   is a real defect the operator photographed on 6 September 2026: **the
   UNSELECTED circles were bright white and the SELECTED one was muted
   olive.** Salience exactly inverted - the eye went to every option except
   the one that was actually in force.

   That is what `accent-color` alone produces. It tints the dot of a CHECKED
   radio and leaves the unchecked ones to the browser, which in a dark theme
   fills them solid white because it assumes a light page underneath. One
   property cannot express "quiet when off, loud when on"; two states have to
   be drawn.

   OFF is the background with a pale border - present, legible, and claiming
   nothing. ON is filled with the accent. */
.choice input {
  margin: 0.35rem 0 0;
  flex: 0 0 auto;
  appearance: none;
  -webkit-appearance: none;
  width: 1.05rem;
  height: 1.05rem;
  border: 2px solid var(--border-strong);
  border-radius: 50%;
  background: var(--surface);
  cursor: pointer;
  display: inline-block;
  position: relative;
}
.choice input:checked {
  border-color: var(--accent);
  background: var(--accent);
}
.choice input:checked::after {
  /* The hole in the middle, so a filled circle still reads as a radio
     rather than as a dot of paint. */
  content: "";
  position: absolute;
  inset: 0.22rem;
  border-radius: 50%;
  background: var(--surface);
}
.choice input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.choice:hover input:not(:checked) { border-color: var(--text-muted); }
.choice-text { color: var(--text); }
.choice-note { display: block; color: var(--text-muted); font-size: 0.85rem; }

/* A quiet secondary action - Save on a settings panel, Cancel on a pending
   request. The filled accent is the send control's alone (rule 1). */
.button-quiet {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 0.5rem 0.9rem;
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}
.button-quiet:hover { color: var(--text); border-color: var(--text-muted); }

/* The open email change. It is the one thing on this page that is mid-flight
   and reversible, so it is set off by the accent rule the way the recovery
   notice and the placeholder banner are - both of those are also things that
   must not be skimmed past. */
.pending-change {
  border-left: 3px solid var(--accent);
  background: var(--surface-raised);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 0.9rem 1.1rem;
  margin: 0 0 1.1rem;
}
.pending-change p { margin: 0 0 0.7rem; }
.pending-change p:last-child { margin-bottom: 0; }
.pending-change code {
  font-family: inherit;
  color: var(--text);
}

/* S16.22 Contact Admin. The card is the login card's, because somebody
   reaching this page is usually somebody who could not get past that one. */
.card textarea {
  width: 100%;
  margin-bottom: 1rem;
  padding: 0.65rem 0.8rem;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--page);
  color: var(--text);
  font-family: inherit;
  font-size: max(16px, 0.95rem);
  line-height: 1.5;
  resize: vertical;
}
.card textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* -------------------------------------------------------------------------
   SETTLED U18 to U21. The admin dashboard. Added 5 September 2026.

   NO NEW COLOURS AND NO SECOND PALETTE, like every block above it: this is
   built from the variables at the top of the file, so both modes follow
   automatically (S16.17 rule 6). And nothing here is a status colour - U20's
   line 6 can be a number the operator does not want to see, and painting it
   red would be the page having an opinion about his business.

   THE FIGURES ARE TABULAR-NUMS AND RIGHT-ALIGNED. U20's shape is a waterfall
   in which each line is checked against the one above it, and that is only
   possible if the decimal points line up.
   ------------------------------------------------------------------------- */
.ledger-lines {
  width: 100%;
  max-width: 26rem;
  border-collapse: collapse;
  margin: 0 0 2rem;
}
.ledger-lines th {
  text-align: left;
  font-weight: normal;
  color: var(--text-secondary);
  padding: 0.45rem 0;
}
.ledger-lines .figure {
  text-align: right;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  padding: 0.45rem 0;
  white-space: nowrap;
}
/* A line the operator did not have to add up himself. Marked by a rule
   above it rather than by weight: the emphasis says "this one is derived",
   not "this one matters more". */
.ledger-lines .derived th,
.ledger-lines .derived .figure { border-top: 1px solid var(--border); }
.ledger-lines .indented th { padding-left: 1.1rem; }
.ledger-lines .total th,
.ledger-lines .total .figure { color: var(--text); font-weight: 600; }
.as-of { margin-top: 0; }

/* S16.23 the spend cap form. Built from the existing card input rules rather
   than a new set: this is the same kind of control in a different place, and
   a second styling of a text box is how two text boxes start to disagree. */
.cap-form { max-width: 26rem; margin: 0 0 2rem; }
.cap-form label {
  display: block;
  margin: 1.1rem 0 0.4rem;
  color: var(--text-secondary);
}
.cap-form input {
  width: 100%;
  padding: 0.65rem 0.8rem;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: max(16px, 0.95rem);
  font-variant-numeric: tabular-nums;
}
.cap-form input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.cap-form .send { margin-top: 1.25rem; }
