/* Five values, and one of them is the absence of a fifth value. */

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  overflow: hidden;
  background: var(--paper, #F2F0EB);
  color: #111111;
  font: 400 15px/1.7 -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* A sheet has tooth. The canvas supplies the irregular grain; these fibres
   remain just visible under every state of the room, including the receipt. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.85;
  /* The same sheet the room is drawn on: render.js builds the grain once and
     publishes it here, so the canvas and the DOM cannot carry different paper.
     The old repeating stripe gradients were a visible repeat and a second
     material; both are gone. */
  background-image: var(--paper-grain, none);
  background-repeat: repeat;
  background-size: var(--paper-tile, 256px) var(--paper-tile, 256px);
}

#room {
  display: block;
  position: fixed;
  inset: 0;
  cursor: crosshair;
}

/* ------------------------------------------------------------------ the gate */

#gate {
  position: fixed;
  inset: 0;
  /* The same sheet, not a paler one: same base colour, same grain, so a panel
     that must be opaque for legibility is still the same physical material. */
  background-color: var(--paper, #F2F0EB);
  background-image: var(--paper-grain, none);
  background-repeat: repeat;
  background-size: var(--paper-tile, 256px) var(--paper-tile, 256px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 10;
  transition: opacity 260ms linear;
}
#gate.gone { opacity: 0; pointer-events: none; }

#gate .one { margin-bottom: 2.2rem; }
#gate .two { color: #9A968D; }
/* One quiet line, only ever occupied when a name is already in the room. */
#gatenote { margin-top: 1.1rem; min-height: 1.7em; }

#gatename {
  margin-top: 2.2rem;
  width: 12ch;
  border: 0;
  outline: 0;
  background: transparent;
  text-align: center;
  font: inherit;
  color: #111111;
  caret-color: #111111;
}

/* ------------------------------------------------------------- the input line
   There is no box, no border or send button. It sits in
   the margin the renderer reserves at the foot of the page (--margin, set from
   render.js so the two cannot drift), where the room is clipped out and the
   paper is always bare. Nothing is drawn over it, so nothing has to be hidden
   to keep it readable. */

:root {
  /* THE reserved bottom band, and the single owner of everything that pushes
     the foot of the page upward: the writing gutter, the home indicator, and
     the software keyboard. render.js publishes it from marginCSS(), which is
     the same number metrics() subtracts from the room — so the room and the
     composer always agree about how much height is left.

     Anything that must stay clear of the keyboard or the home indicator
     positions from THIS and adds nothing else. It used to be otherwise: --kb
     and env(safe-area-inset-bottom) were added a second time on top of a
     --margin that already contained both, so a 300px keyboard moved the
     composer 600px and a phone put the field halfway up the screen. */
  --margin: 48px;
  /* Reported for diagnosis only — how much of the window the keyboard covers.
     Never add this to a position: it is already inside --margin. */
  --kb: 0px;
}

#line {
  position: fixed;
  /* Centred on the paper the room is allowed to use, not on the window. On
     desktop the rail reserves real territory on the right, so a window-centred
     field sits 150px off the centre of the room it belongs to. --rail is the
     same reservation the projection is built from; there is no second idea of
     how wide the room is. On a phone --rail is 0 and this is the viewport
     centre. */
  left: calc((100vw - var(--rail, 0px)) / 2);
  /* Above the home indicator, and above the keyboard when there is one. The
     field is the one thing that must never be underneath either — and --margin
     already carries both. */
  bottom: calc(var(--margin) - 1.7em - 4px);
  transform: translateX(-50%);
  width: min(28rem, 82vw);
  border: 0;
  outline: 0;
  background: transparent;
  color: #111111;
  caret-color: #111111;
  font: inherit;
  text-align: center;
  z-index: 5;
  opacity: 0;
}
#line.on { opacity: 1; }

/* The first verb is present only while the line is empty. It leaves the
   instant a thought begins, so the room never grows a permanent instruction
   panel. */
#line::placeholder {
  color: #9A968D;
}

/* The second verb arrives only after the visitor has added weight. It is a
   brief whisper in the filing gutter, not a tutorial living over the room. */
#cue {
  position: fixed;
  left: calc((100vw - var(--rail, 0px)) / 2);
  bottom: calc(var(--margin) + 1.2rem);
  transform: translateX(-50%);
  z-index: 5;
  color: #9A968D;
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  transition: opacity 420ms linear;
}
#cue.on { opacity: 1; }

/* The only control in the product. It is not labelled. */
#sound {
  position: fixed;
  right: 1.5rem;
  /* Clear of the keyboard and the home indicator, from --margin alone. The
     floor keeps it on the page when a short window shrinks the gutter. */
  bottom: max(1.5rem, calc(var(--margin) - 1.5rem));
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #9A968D;
  cursor: pointer;
  z-index: 6;
}
#sound.off { background: transparent; box-shadow: inset 0 0 0 1px #9A968D; }

/* ---------------------------------------------------- The Misfiled Receipt
   A slip of paler paper than the room. Sparse, ruled, stamped. No box, no
   button, no field, and nothing that reads as an account form. */

#receipt {
  position: fixed;
  inset: 0;
  /* The same sheet, not a paler one: same base colour, same grain, so a panel
     that must be opaque for legibility is still the same physical material. */
  background-color: var(--paper, #F2F0EB);
  background-image: var(--paper-grain, none);
  background-repeat: repeat;
  background-size: var(--paper-tile, 256px) var(--paper-tile, 256px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 20;
  transition: opacity 900ms ease-out;
}
#receipt.filed { opacity: 0; pointer-events: none; }
#receipt.gone  { display: none; }

/* The two reading sides can be taller than the visual viewport once mobile
   browser chrome and safe areas have taken their share. The main receipt stays
   vertically centred; only a reading side becomes a top-aligned native scroll
   surface. Previously the same flex centring placed half of a long side above
   the screen while html/body forbade scrolling, trapping its return line below
   the screen as well. */
#receipt[data-page="lore"],
#receipt[data-page="how"] {
  align-items: flex-start;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  padding-top: max(1.5rem, env(safe-area-inset-top, 0px));
  padding-bottom: max(1.5rem, env(safe-area-inset-bottom, 0px));
}

.rcpt {
  width: 22rem;
  max-width: 84vw;
  text-align: left;
}

/* Receipt headers are set small, wide and flat. */
.rcpt .head {
  font-size: 11px;
  letter-spacing: 0.22em;
  color: #111111;
}
.rcpt .sub {
  font-size: 12px;
  color: #9A968D;
  margin-top: 0.35rem;
}
.rcpt hr {
  border: 0;
  border-top: 1px solid #DAD6CD;
  margin: 1.5rem 0;
}
.rcpt .ask {
  font-size: 12px;
  color: #9A968D;
  letter-spacing: 0.02em;
}

/* The place the phrase is written: a ruled line, not an input. */
.rcpt .line {
  margin-top: 0.55rem;
  border-bottom: 1px solid #C9C4B9;
  min-height: 1.6em;
  font-size: 14px;
  color: #111111;
  white-space: pre;
  word-break: break-all;
}
.rcpt .line #phrase {
  width: 100%;
  border: 0;
  outline: 0;
  background: transparent;
  color: inherit;
  caret-color: #111111;
  font: inherit;
  letter-spacing: inherit;
  text-align: left;
}

.rcpt .note {
  margin-top: 0.9rem;
  font-size: 11px;
  letter-spacing: 0.16em;
  color: #9A968D;
  min-height: 1.2em;
  opacity: 0;
  transition: opacity 200ms linear;
}
.rcpt .note.show { opacity: 1; }
.rcpt .note.fading { opacity: 0; transition: opacity 1100ms ease-out; }

/* The stamp. Rotated, letter-spaced, pressed slightly askew. */
.rcpt .stamp {
  margin-top: 1.6rem;
  font-size: 15px;
  letter-spacing: 0.34em;
  color: #111111;
  opacity: 0;
  transform: rotate(-7deg) scale(1.25);
  transform-origin: left center;
  transition: opacity 120ms linear, transform 220ms cubic-bezier(0.2, 0.9, 0.3, 1);
}
.rcpt .stamp.pressed {
  opacity: 0.62;
  transform: rotate(-7deg) scale(1);
}

/* ------------------------------------------------- the other side of the slip
   One line offers it; one line comes back. The note is the same paper and the
   same type as the receipt — no panel, no border, no illustration, nothing
   that announces itself as a feature. */

.rcpt .turn {
  margin-top: 1.8rem;
  font-size: 11px;
  letter-spacing: 0.02em;
  color: #B4AEA3;
}
.rcpt .turn-how { margin-top: 0.65rem; }
.rcpt .turn span {
  cursor: pointer;
  border-bottom: 1px solid #DAD6CD;
  padding-bottom: 1px;
}
.rcpt .turn span:hover { color: #9A968D; border-bottom-color: #C9C4B9; }

/* Turning it over is a swap, not a performance. */
#receipt .note-side { display: none; }
#receipt[data-page="lore"] .rcpt.main,
#receipt[data-page="how"] .rcpt.main { display: none; }
#receipt[data-page="lore"] #noteside,
#receipt[data-page="how"] #howside { display: block; }

.note-side {
  width: 30rem;
  max-width: 86vw;
}
.note-side .head { margin-bottom: 1.5rem; }
.note-side p {
  font-size: 13px;
  line-height: 1.85;
  color: #4A463F;
  margin-bottom: 1rem;
  max-width: 34em;
}
.note-side .turn { color: #B4AEA3; }
.how-side p { margin-bottom: 1.05rem; }


/* ---------------------------------------------------------------- the margin
   What is being said, while it is still being said. Notes in the margin of a
   filing document: no card, no bubble, no border, no avatar, no timestamp, no
   scrollback. They are written at the edge and the page forgets them.

   It sits in the room's upper air, where the pile is not, so it never competes
   with the words themselves. */

/* The filing rail.
   Not a panel floating over the room — a reserved column of the page. The
   renderer projects the room into what is left (see railCSS/metrics in
   render.js) and publishes the width here, so the boundary in the stylesheet
   and the boundary in the projection are the same number and cannot drift.
   The only thing marking it is a single hairline, like a fold in the paper. */
:root {
  --rail: 300px;
  --railtop: 0px;
}

#margin {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--rail);
  z-index: 4;
  /* Two rows, and that is the whole fix. The header takes what it needs; the
     notes take the rest and scroll nothing. Nothing is layered over anything,
     so no z-index or offset can put a message behind a control. */
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 0.9rem;
  /* The header sat hard against the top of the window. Give the rail a real
     top inset, and respect a notch or status bar where there is one. */
  padding: calc(2.2rem + env(safe-area-inset-top, 0px)) 1.5rem
           calc(var(--margin) + 1.2rem) 1.5rem;
  border-left: 1px solid #E4E0D7;
  overflow: hidden;
  text-align: left;
  pointer-events: none;
}

/* The header region: empty and zero-height until the room has something to
   ask. */
/* Never display:none — removing the item lets the notes auto-place into row 1,
   which top-aligns them and inverts the reading order. The row is pinned
   instead, and simply has nothing in it when the room is quiet. */
#railhead { grid-row: 1; align-self: start; }

/* The notes region. Newest last, sitting on the floor of the rail. */
#railnotes {
  grid-row: 2;
  /* Stay inside the second grid track even when all forty notes are present.
     Bottom alignment belongs to the flex contents, not to the grid item: an
     end-aligned item keeps its max-content height and can climb behind the
     header instead of being clipped by this region. */
  align-self: stretch;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.15rem;
  overflow: hidden;
}

/* Narrow screens: a real strip along the top, never a squeezed sidebar, and
   never anything over the composer or the keyboard-safe area at the foot. */
html[data-rail='top'] #margin {
  top: 0;
  left: 0;
  right: 0;
  bottom: auto;
  width: auto;
  height: var(--railtop);
  grid-template-rows: auto 1fr;
  gap: 0.35rem;
  padding: calc(0.55rem + env(safe-area-inset-top, 0px)) 1rem 0.5rem 1rem;
  border-left: 0;
  border-bottom: 1px solid #E4E0D7;
}
html[data-rail='top'] #railnotes { align-self: start; justify-content: flex-start; }

/* A strip has room for the most recent few; the rest are still held in state.
   Volume, never time, removes any.

   nth-LAST-child, not nth-child. Notes are appended, so the newest is last;
   hiding from the fourth child onward showed the three OLDEST of forty and
   left the strip permanently stale — the page's present conversation, several
   pages ago. */
html[data-rail='top'] #railnotes .note:nth-last-child(n + 4) { display: none; }
html[data-rail='top'] #railnotes .note { margin-bottom: 0.15rem; }

/* Hierarchy, so three notes read as marginalia and not as grey residue.
   The newest carries the most ink; the two behind it step back but stay
   legible. Nothing here is a chat bubble: it is the same one-line note, at
   three depths of pencil. */
html[data-rail='top'] #railnotes .note { color: #6F6B62; font-size: 12.5px; }
html[data-rail='top'] #railnotes .note:nth-last-child(2) { color: #8A867D; }
html[data-rail='top'] #railnotes .note:nth-last-child(3) { color: #A6A299; }
html[data-rail='top'] #railnotes .note .who { opacity: 0.75; }

#margin .note {
  /* One line, always. A long thought is a ledger entry here, not a paragraph:
     the whole sentence is still a hold away on the word itself, and a rail that
     wraps ten of them stops being readable at all. */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.5rem;
  align-items: baseline;
  margin-bottom: 0.35rem;
  font-size: 12px;
  line-height: 1.5;
  color: #9A968D;
  pointer-events: auto;
  cursor: default;
  transition: color 200ms linear;
}
#margin .note > span.said {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Desktop has the height to show the thought itself. Keep the compact mobile
   strip above unchanged, but let right-rail sentences use as many lines as
   they need. `anywhere` is only a last resort for an unbroken token. */
html[data-rail='right'] #margin .note { align-items: start; }
html[data-rail='right'] #margin .note > span.said {
  white-space: normal;
  overflow: visible;
  text-overflow: unset;
  overflow-wrap: anywhere;
}
#margin .note:hover { color: #4A463F; }

/* The desktop rail's depth.
   Six bands, set from the note's distance from the newest (see railBand). The
   three being read are individually distinguishable; behind them the steps get
   further back and then stop. The last two bands share one tone deliberately —
   an archive reads as archive, and inventing another step down would have cost
   legibility to buy a distinction nobody needs. Every band clears 4.5:1 against
   the paper and every name clears 3:1, so "quieter" never becomes "ghosted".
   No cards, borders, badges, shadows or animation: only how dark the pencil is. */
html[data-rail='right'] #margin .note[data-age='0'] { color: #403B33; }
html[data-rail='right'] #margin .note[data-age='1'] { color: #4E4941; }
html[data-rail='right'] #margin .note[data-age='2'] { color: #5D5850; }
html[data-rail='right'] #margin .note[data-age='3'] { color: #656058; }
html[data-rail='right'] #margin .note[data-age='4'] { color: #69645C; }
html[data-rail='right'] #margin .note[data-age='5'] { color: #69645C; }

/* The name stays quieter than the sentence at every depth, so recency never
   turns a speaker into a heading. */
html[data-rail='right'] #margin .note[data-age='0'] .who { color: #6B665E; }
html[data-rail='right'] #margin .note[data-age='1'] .who { color: #716C64; }
html[data-rail='right'] #margin .note[data-age='2'] .who { color: #78736B; }
html[data-rail='right'] #margin .note[data-age='3'] .who { color: #7E7971; }
html[data-rail='right'] #margin .note[data-age='4'] .who { color: #847F77; }
html[data-rail='right'] #margin .note[data-age='5'] .who { color: #847F77; }

/* The name is the quietest part, not a label on a card. */
#margin .who {
  display: block;
  font-size: 10px;
  letter-spacing: 0.14em;
  color: #B4AEA3;
}
#margin .note:hover .who { color: #9A968D; }


/* Present to a screen reader, absent to everything else.
   A class rather than an inline style, because the content policy forbids
   style attributes — and clip-rect rather than display:none, because a hidden
   element is not announced at all. */
.sr-only {
  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;
}


/* -------------------------------------------------------- the held breath
   Said once, quietly, when the room has genuinely stopped — and gone the
   instant anything happens. Not a banner, not a state, not an achievement.
   It sits in the air above the pile and is the faintest thing on the page. */

/* Centred on the ROOM, never on the window: --room-cx/--room-top/--room-h are
   published by the renderer from the same metrics the room is drawn from, so
   the line stays centred in the visible room once the rail is reserved, and
   below the top strip on a narrow screen. A viewport 50% would drift toward
   the messages. */
#breath {
  position: fixed;
  left: var(--room-cx, 0px);
  top: calc(var(--room-top, 0px) + var(--room-h, 0px) * 0.34);
  transform: translateX(-50%);
  z-index: 3;
  font-size: 12px;
  letter-spacing: 0.18em;
  color: #B4AEA3;
  opacity: 0;
  pointer-events: none;
  transition: opacity 2200ms ease-out;
}
/* Arriving is slow; leaving is not. Thought interrupts. */
#breath.held { opacity: 1; }
#breath.gone { opacity: 0; transition: opacity 220ms linear; }


/* ---------------------------------------------------------- the closing note
   What a dead room turned out to be, in one or two plain sentences. Read only
   by someone who has descended into it, and set like the note on the receipt:
   the same paper, the same type, nothing announcing itself. */

#ruinnote {
  position: fixed;
  left: 50%;
  top: 12%;
  transform: translateX(-50%);
  width: 28rem;
  max-width: 82vw;
  z-index: 3;
  text-align: center;
  font-size: 13px;
  line-height: 1.8;
  color: #9A968D;
  opacity: 0;
  pointer-events: none;
  transition: opacity 900ms ease-out;
}
#ruinnote.on { opacity: 1; }


/* On a touch screen iOS zooms the whole page when it focuses an input smaller
   than 16px, which throws the receipt off-centre at the exact moment somebody
   is trying to read it. Only the field grows, only on touch; the typography is
   untouched everywhere else. */
@media (pointer: coarse) {
  .rcpt .line #phrase,
  #gatename,
  #line { font-size: 16px; }
}

/* The crowded notice. It sits in the rail's reserved territory, above the
   notes, in the same grey as everything else the room says quietly. No panel,
   no border, no fill — it is writing on the same sheet. */
#crowded {
  /* Lives inside the rail's header row. No position, no z-index: it cannot
     overlap the notes because the grid gives it its own row. */
  pointer-events: none;
  font-size: 12px;
  line-height: 1.6;
}
#crowded .crowded-head { color: #4A463F; margin-bottom: 0.7rem; }
#crowded .crowded-head.armed { color: #4A463F; pointer-events: auto; cursor: pointer; }

/* Two actions, separated and comfortably hittable. Underlined ink on paper —
   still not a button with a box round it, but unmistakably a thing you press,
   and big enough for a thumb. */
#crowded .crowded-act {
  display: block;
  width: 100%;
  text-align: left;
  pointer-events: auto;
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0.5rem 0;
  min-height: 44px;
  color: #2E2A24;
  font: inherit;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  transition: color 160ms linear;
}
#crowded .crowded-act > span {
  border-bottom: 1px solid #9A968D;
  padding-bottom: 2px;
}
#crowded .crowded-act:hover { color: #111111; }
#crowded .crowded-act:hover > span { border-bottom-color: #111111; }
#crowded .crowded-act:focus-visible { outline: 1px solid #9A968D; outline-offset: 3px; }
/* A line of explanation, not an action. */
#crowded .crowded-note {
  color: #9A968D;
  font-size: 11.5px;
  line-height: 1.55;
  margin-top: 0.2rem;
}
