/* Agent surface — THE CANVAS.
   Built one locked decision at a time (docs/design-notes.md); George's call
   at every step. Do not pre-style anything he has not called. The styled v1
   lives in git history (fd6a01d) as reference, never as a default.

   Eligibility speaks its OWN language (George, 2026-07-07): rounded, robust,
   modern — dark contrast on the light warm field. */

* { box-sizing: border-box; }

:root {
  /* Eligibility's own vocabulary — only what George has called lives here. */
  --e-ink: #1A1613;   /* warm near-black — all marks are opacities of this */
  /* ── THE APPROACH (2026-08-21, adopted from the hosted viewer with the fold).
        A hand ARRIVING is not a thing landing. --motion-state is front-loaded on
        purpose — cubic-bezier(0.16, 1, 0.3, 1) spends most of its change in the
        first sixth of the beat — so a hover riding it reads as INSTANT however
        long it is set: the ink is simply on. This dial is symmetric instead,
        slow at the start, quickest in the middle, slow into rest, and enter and
        exit both take it. George's word on the viewer's own version of it:
        "quite slow, and it's good".
        It lives HERE and not in tokens.css because tokens.css is the family
        sheet both surfaces load and the buyer journey has no hover voice; a
        second definition on a shared file is the one that drifts. ── */
  --motion-hover: 0.5s cubic-bezier(0.4, 0, 0.6, 1);
  /* ── ONE MARK, ONE PAIR OF NUMBERS (2026-08-21, with the fold).
        The chevron's box and its gap to the word are the only two measurements
        the gutters on this surface are cut from: the mark itself, the register
        row's text edge, the head's first label, the empty gutter a shared buyer
        keeps so its name shares that edge, the invited row's own gap, and the
        belonging rule inside an open invite. They were spelled as six separate
        literals, so widening the mark at one depth left the other five behind
        and every test stayed green — a uniform mark held together by nothing but
        arithmetic done by hand. The derived edges state the arithmetic instead. ── */
  --mark-box: 16px;
  --mark-gap: 14px;
}

/* The family's own refusal, extended to this sheet's one dial: a hand that
   asked for no motion must not be answered by a mark still warming. tokens.css
   zeroes --motion-state and --motion-quick in the same breath. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-hover: 0s;
  }
}

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: Inter, system-ui, sans-serif;
  font-weight: var(--weight-regular);
  font-size: var(--text-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* Sticky-floor layout: the register grows, the foot rests on the viewport's
   bottom while the book is young and waits at the scroll's end once the book
   outgrows a screen — a back cover, never a bar that follows. The pop, the one
   fixed overlay left on this surface, is untouched by the flex. */
/* THE COLUMN STANDS ON ONE X (2026-08-21, the viewer's own finding, and it
   lands harder here). This page is one viewport tall until a property unfolds,
   so opening a fold pushed the document past the fold line, summoned a classic
   scrollbar and moved the centred record — and the whole ledger with it — by the
   scrollbar's full width in a single unanimated frame. The gutter is reserved
   permanently instead; it costs nothing on an overlay-scrollbar machine. TWO
   costs, both named rather than left to be found. One: a classic scrollbar's
   gutter is outside the initial containing block, so the pop's fixed wash stops
   that far short of the window's right edge in EVERY state now, where before it
   did so only once the book outgrew a screen. Two: the reservation is scoped to
   the register below, so a lapse that stands the door over a painted book moves
   the page by the gutter's width on those same machines. That flip is the one
   the book is being emptied on, so nothing an eye was reading is what moves. */
html {
  height: 100%;
  scrollbar-gutter: stable;
}
/* THE DOOR STATE PAYS NO GUTTER (2026-08-21). The reservation above is the
   register's law: the book grows, a fold opens past the viewport, and the column
   must not jump. The door has no scroll to stabilise — a 480 panel in a document
   that never overflows — so it buys the door nothing, and it costs the hosted
   viewer's sign-in page its exactness. That page was ported from THIS door on
   2026-08-20 to land on its numbers, and viewer.css carries the same carve-out
   for the same reason (`html:has(main.signin)`, measured there at 1280x800: a
   reserved gutter put its panel at x=416.5 where this door lands at x=424). Two
   doors ported to be one door reserve the same nothing. The exemption is carried
   by the state element itself, exactly as the hidden law above is. */
html:has(main.door:not([hidden])) { scrollbar-gutter: auto; }
body {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}
.register { flex: 1 0 auto; width: 100%; }

/* ── One address, two states (2026-08-18) ──
   The root serves this document to everyone, and it resolves itself: agent.js
   asks the guarded pipeline API who is holding and the answer decides which
   state stands. Until the answer lands the field is simply empty — the page's
   own stillness, not a placeholder and not a spinner — so neither state is
   ever shown and taken back. Whichever one lands arrives on the register's own
   arrival (rows-in, the state curve): a plain fade, nothing travels. */
/* Which state stands is carried on the elements themselves, in the attribute
   the page's planes already use — the document holds one visible main at a
   time, never two. `.register-foot` sets its own display, so the plain
   [hidden] rule would not reach it: each is named. */
.register[hidden],
.register-foot[hidden],
.door[hidden] { display: none; }

.door {
  flex: 1 0 auto;
  width: 100%;
  /* The door stands where the pop stands — 38vh, the pop's own height (a
     capture over a book): it sits with the capture, not above it. */
  max-width: 480px;
  margin: 0 auto;
  padding: 38vh var(--space-4) var(--space-6);
  animation: rows-in var(--motion-state) both;
}
/* The register and its foot arrive as ONE state on the one curve. The foot is
   a member of the register's state, not a thing standing behind it: revealed
   un-faded while the book above it faded in, it read as already-there. */
.state-register .register,
.state-register .register-foot { animation: rows-in var(--motion-state) both; }

/* The panel — the pop's lit parchment, exactly (elevation follows light). */
.door-panel {
  background: var(--plane-bg);
  border: 1px solid rgba(26, 22, 19, 0.08);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 24px 60px -36px rgba(26, 22, 19, 0.35);
}

/* Inputs carry no box of their own — the panel is the box. Quiet lines that
   wake: the placeholder lifts under the hand (the invite line's values). */
.door-input {
  width: 100%;
  font: inherit;
  font-size: 1.0625rem;
  color: var(--e-ink);
  background: transparent;
  border: 0;
  outline: none;
  padding: 18px 22px;
}
.door-input::placeholder {
  color: rgba(26, 22, 19, 0.38);
  transition: color 0.4s ease;
}
.door-input:hover::placeholder,
.door-input:focus::placeholder {
  color: rgba(26, 22, 19, 0.55);
}

/* The password row — the price-row shape: the line continues, the verb sits
   at its end. */
.door-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  border-top: 1px solid rgba(26, 22, 19, 0.1);
  padding-right: 14px;
}
.door-row .door-input { flex: 1; min-width: 0; }

/* The verb — the room's invite-word, verbatim: underlined at rest (a control
   declares itself), dormant until both lines hold text, waking on the slow
   curve. */
.door-verb {
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 450;
  color: rgba(26, 22, 19, 0.85);
  background: none;
  border: 0;
  padding: 0 2px;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(26, 22, 19, 0.3);
  transition: color 0.8s ease, text-decoration-color 0.8s ease;
}
.door-panel:has(.door-input:placeholder-shown) .door-verb {
  color: rgba(26, 22, 19, 0.3);
  text-decoration-color: rgba(26, 22, 19, 0.22);
}
.door-verb:hover {
  color: var(--e-ink);
  text-decoration-color: rgba(26, 22, 19, 0.55);
}
.door-verb:focus-visible {
  outline: none;
  color: var(--e-ink);
}

/* State, stated in words, in place — the slot is reserved so the page never
   moves when it speaks. Ink, not semantic colour: a refused password is not
   a data verdict. The voice is the family's own quiet note (.pipeline-note,
   0.6 ink — panel verdict 2026-07-09: 0.75 was a hover value misquoted as a
   resting ink; the reserved slot and the words carry the clarity). */
.door-state {
  margin: 16px 2px 0;
  min-height: 1.5em;
  font-size: 0.9375rem;
  color: rgba(26, 22, 19, 0.6);
}

/* ── The register ledger (George, 2026-07-07) ──
   His call: professional; a subtle/light top line (address / guide price /
   buyers / verified / listed), clickable and sorted by; thin small entries,
   never cards. A register, not a dashboard: every mark is an opacity of the
   ink, numerics right-aligned in tabular figures, zero rendered as silence. */
.register {
  max-width: 960px;            /* widened from the 860 measure at George's
                                  call (2026-07-08, "a bit wider") — the
                                  full-width rejection still stands */
  margin: 0 auto;
  padding: 120px 40px 64px;
}
.ledger-head,
.ledger-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 110px 64px 72px 104px;
  gap: 20px;
  align-items: baseline;
}

/* The top line — subtle and light; each label is a sort control. */
.ledger-head {
  padding-bottom: 9px;
  border-bottom: 1px solid rgba(26, 22, 19, 0.12);
}
.ledger-col {
  font: inherit;
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: rgba(26, 22, 19, 0.42);
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
  /* The control-mark, declared at rest (panel synthesis 2026-07-08): the
     room's underline means "this is a control", and a sort label is one.
     It marks the affordance only — direction still carries NO mark (locked);
     the active label deepens in ink alone, nothing is ever inserted. */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(26, 22, 19, 0.3);
  transition: color 0.4s ease, text-decoration-color 0.4s ease;
}
.ledger-col:hover {
  color: rgba(26, 22, 19, 0.75);
  text-decoration-color: rgba(26, 22, 19, 0.55);
}
.ledger-col:focus-visible {
  outline: 2px solid rgba(26, 22, 19, 0.4);
  outline-offset: 2px;
}
/* The active sort carries NO mark (George): the label deepens, nothing is
   inserted beside it — so it can never shift. Direction is read from the
   data; aria-sort still carries it for screen readers. */
.ledger-col.sorted-asc,
.ledger-col.sorted-desc {
  color: rgba(26, 22, 19, 0.8);
}

/* ── THE ROW THAT FOLDS (George, 2026-08-21) ──
   A property's band: the row that names it, and beneath it the fold holding its
   record. The hairline belongs to the GROUP, not to the row, so an open record
   sits inside the band its own row rules.

   THE GRAMMAR IS THE VIEWER'S, ADOPTED WHOLE. A row that NAVIGATES takes the
   centre-peaked wash — the wash means "this row leaves the page". A row that
   FOLDS carries the mark instead, and its hover lives in that mark alone. This
   row folds now, so the wash is STRUCK (George: remove the grey wash, warm the
   chevron instead) and with it the position/z-index the wash's own layer needed.
   There is no wash left anywhere on this surface's rows. */
.ledger-group {
  position: relative;
}
.ledger-group::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(26, 22, 19, 0.06);
}
/* Thin entries — one hairline apart, the address carries the row. Nothing
   moves and nothing washes; the mark answers the hand. */
.ledger-row {
  color: inherit;
  text-decoration: none;
  font-size: 0.9375rem;
  padding: 11px 0;
}
/* Keyboard focus is visible; mouse focus stays silent — the row takes the same
   ring the record's own toggle wears (:focus-visible is keyboard-driven). */
.ledger-row:focus-visible {
  outline: 2px solid rgba(26, 22, 19, 0.5);
  outline-offset: 2px;
}
/* The address underline STRUCK (George, 2026-07-08: "too much
   underlining") — with every address and all five labels marked, the mark
   was spent by overuse, and a column of underlined addresses read as
   hyperlinks, not a ledger. The underline is fenced to CONTROLS — the sort
   labels above. The address WRAPS rather than truncates (his call: a
   property's name is never cut); numerics align to its first line's baseline.

   The mark shares the address cell rather than taking a grid column of its own,
   so the five-column ledger is untouched and there are two numbers to keep and
   both are declared once at the top of this sheet: the text edge sits one box
   plus one gap out from the row's own edge, and the head's first label is padded
   to the same line by the same sum (the head has no mark — it does not open,
   and absence says so).
   `align-items: baseline` with the mark held OFF the baseline is what keeps the
   numeric columns aligned to the address's first line: the text is then the only
   item in this cell's baseline group, so it sets the cell's baseline. */
.cell-address {
  display: flex;
  align-items: baseline;
  gap: var(--mark-gap);
  min-width: 0;
  color: var(--e-ink);
}
.cell-address-text { min-width: 0; }
.ledger-head .ledger-col:first-child {
  padding-left: calc(var(--mark-box) + var(--mark-gap));
}
.cell-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.cell-price { color: rgba(26, 22, 19, 0.8); }
.cell-quiet { color: rgba(26, 22, 19, 0.5); }

/* THE PROPERTY UNFOLDS IN PLACE — the viewer's machinery, verbatim. grid-rows
   0fr -> 1fr: no measured height, no jump, and an interrupted fold proceeds from
   wherever it stands. `visibility` closes a shut fold to the keyboard and to a
   screen reader as well as to the eye, and transitions as a discrete step, so
   the fold still opens on its first frame and stays visible for the whole of its
   close. */
.ledger-fold {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--motion-state);
}
.ledger-group.is-open .ledger-fold { grid-template-rows: 1fr; }
.ledger-fold-inner {
  overflow: hidden;
  min-height: 0;
  visibility: hidden;
  transition: visibility var(--motion-state);
}
.ledger-group.is-open .ledger-fold-inner { visibility: visible; }

/* The record is CENTRED under the row that names it (George, 2026-08-21: "I
   want it centre, so it looks like a form of indentation") — one step in from
   the rows that name it, never flush left and never the register's own width.
   Its intervals are the family's: one concept step down from the row's word,
   and a closing 20 that keeps the ledger's own rhythm with the group's hairline
   beneath it. Nothing inside carries a top margin — the fold's wrappers have no
   padding and no border, so a margin there would collapse straight out and
   compose a second interval onto the 40. */
.ledger-fold .room {
  max-width: 640px;
  margin-inline: auto;
  padding-top: var(--space-5);
  padding-bottom: 20px;
}
.room > :first-child { margin-top: 0; }

/* ── The register's foot — the page's floor (George, 2026-07-08) ──
   His read: the table wasn't weak, it was LOST — the field just ran out
   beneath it. The foot gives the world an edge; the emptiness becomes
   held space. The keeper's-imprint resident (the firm's own name) was
   tried and STRUCK same-day: in a demo it is always the WRONG agency's
   name, and renaming between visits is a burden — parked for production,
   where each agency sees its own book. The foot stands empty until Sign
   out arrives as its resident with agent auth (the finishing pass). */
.register-foot {
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: var(--space-4) 40px;
  font-size: var(--text-small);
  color: rgba(26, 22, 19, 0.35);
  display: flex;
  justify-content: center;     /* the resident holds the centre (George,
                                  2026-07-09 — subtler); the keeper's imprint
                                  (parked for production) will hold the left —
                                  WHEN it lands, revisit the packing */
  align-items: baseline;
}

/* Sign out — the foot's resident (live-demo plan; rendered only when a live
   session exists, so it is never a dead control). Subtler than the row-verb
   dialect (George, 2026-07-09): no underline, quieter rest ink — the floor's
   resident recedes until the hand arrives; ink still wakes on the slow
   curve. */
.foot-verb {
  font: inherit;
  font-size: var(--text-small);
  font-weight: 450;
  color: rgba(26, 22, 19, 0.45);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color 1.2s ease;
}
.foot-verb:hover {
  color: rgba(26, 22, 19, 0.65);
}
.foot-verb:focus-visible {
  outline: 2px solid rgba(26, 22, 19, 0.5);
  outline-offset: 2px;
}

/* ── The quiet action button — the + circle's language, rectangular ──
   Transparent, faint ink edge, ink text; hover deepens slowly. Used by the
   panel's submit (and the retry note). */
.btn {
  font: inherit;
  font-size: 0.875rem;
  font-weight: 450;
  color: rgba(26, 22, 19, 0.8);
  background: transparent;
  border: 1px solid rgba(26, 22, 19, 0.25);
  border-radius: 10px;
  padding: 8px 16px;
  cursor: pointer;
  transition:
    color 0.8s ease,
    border-color 0.8s ease,
    background 0.8s ease;
}
.btn:hover {
  color: var(--e-ink);
  border-color: rgba(26, 22, 19, 0.45);
  background: rgba(26, 22, 19, 0.03);
}
.btn:focus-visible {
  outline: 2px solid rgba(26, 22, 19, 0.4);
  outline-offset: 2px;
}

/* The line at the head of the register (George, 2026-07-07): finding and
   adding are one gesture — typing an address. A quiet place where text
   begins, above the ledger, where the eye arrives. Touch it — or simply
   start typing anywhere on the register — and the spotlight pop opens with
   the letters carried in. NOT a row: it wears no row hover; its ink wakes
   alone. The foot trigger and the + circle are retired (history).

   The display-scale figure was tried and STRUCK same-day (George,
   2026-07-08: "too loud… it's just a subtle instruction, subtle guidance
   — their focal point is the table"). The law it taught: the tool's own
   voice is never the figure of this page — the agent's book is. This
   line stays at the accepted quiet scale; presence belongs to the
   entries beneath it. */
.head-line {
  display: block;
  width: 100%;
  font: inherit;
  font-size: 1.0625rem;        /* the pop's own scale — accepted 2026-07-07 */
  text-align: left;
  color: rgba(26, 22, 19, 0.38);
  background: none;
  border: 0;
  padding: 0;
  margin-bottom: var(--space-5); /* the family concept break — one cadence
                                    down the head: gesture — book */
  cursor: text;                /* a place where text begins, not a button */
  transition: color 0.4s ease, opacity var(--motion-state), filter var(--motion-state);
}
/* ONE PLACE WHERE TEXT BEGINS (George, 2026-08-23). While a record stands
   open, the page carries two typing instructions: this line at the head and
   the record's own "Start typing a name". Two at once read as wrong, and the
   head's is half-false in that state anyway: typing anywhere no longer opens
   the pop while a record is open (agent.js). So the head's gesture recedes on
   the family's attention law (opacity + a breath of blur, the same idiom the
   record uses for its own rest while an invite is open) and returns whole when
   the fold shuts. It is not disabled: touching it still opens the pop. */
.register:has(.ledger-group.is-open) .head-line {
  opacity: var(--recede-opacity);
  filter: blur(var(--recede-blur));
}
.head-line:hover { color: rgba(26, 22, 19, 0.62); }
/* Focus wears no box (George): the ink wakes instead — the same quiet mark
   as hover, so returning from the pop leaves no chrome behind. */
.head-line:focus-visible {
  outline: none;
  color: rgba(26, 22, 19, 0.62);
}

/* ── The pop — a spotlight capture, not a form (George, 2026-07-07 v2) ──
   Press + and a single darker-warm panel appears high-centre, caret already
   in the address line. No title, no white, no rise — a plain fade. The
   suggestions are rows of the SAME panel, under a hairline. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 38vh var(--space-4) var(--space-4);
  background: rgba(243, 240, 238, 0.55);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  opacity: 0;
  transition: opacity var(--motion-state);
}
.modal[hidden] { display: none; }
.modal.open { opacity: 1; }

.add-panel {
  width: min(560px, 100%);
  background: var(--plane-bg);              /* lit parchment — the field
                                               lifted toward light (#FAF7F2) */
  border: 1px solid rgba(26, 22, 19, 0.08);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 24px 60px -36px rgba(26, 22, 19, 0.35);
  /* The panel NEVER travels: the input line stays put under the caret and
     the list unfolds beneath it. Movement a user can't register is the
     movement that doesn't happen (2026-07-07). */
}

/* The arriving rows start transparent so the growth beneath the caret
   reads as an unfolding, not a jump. */
.suggestions:not([hidden]) {
  animation: rows-in var(--motion-state) both;
}
@keyframes rows-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Inputs in the panel carry no box of their own — the panel is the box. */
.box-input {
  width: 100%;
  font: inherit;
  font-size: 1.0625rem;
  color: var(--e-ink);
  background: transparent;
  border: 0;
  outline: none;
  padding: 18px 22px;
}
.box-input::placeholder { color: rgba(26, 22, 19, 0.38); }

/* Address suggestions — rows of the same surface, under a hairline. */
.suggestions {
  margin: 0;
  padding: 6px 0;
  list-style: none;
  border-top: 1px solid rgba(26, 22, 19, 0.1);
  max-height: 320px;
  overflow: auto;
}
.suggestion {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 11px 22px;
  color: rgba(26, 22, 19, 0.8);
  cursor: pointer;
  transition: background var(--motion-quick);
}
.suggestion:hover,
.suggestion.active {
  background: rgba(26, 22, 19, 0.05);
  color: var(--e-ink);
}
/* An entry from the agent's own book carries its price — the row IS the
   register entry; picking it opens the property. New addresses carry none. */
.sug-price {
  color: rgba(26, 22, 19, 0.5);
  font-variant-numeric: tabular-nums;
}
/* Where the book's matches lead, the first new address sits under a
   hairline — the data separates the tiers, no labels. */
.suggestion.first-new {
  border-top: 1px solid rgba(26, 22, 19, 0.1);
}

/* Chosen state — the address fixes in the input line's exact type and
   position (object continuity: the string doesn't move); the guide price
   follows beneath. */
.chosen-address {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 18px 22px;
}
.chosen-address .addr {
  flex: 1;
  font-size: 1.0625rem;
  color: var(--e-ink);
}
/* The postcode's completion. The span exists from the click (room
   pre-reserved, ~one incode wide) and its INK breathes in on a timeline
   anchored to the click; the text joins the breath whenever the resolve
   returns. Colour animation, not opacity — opacity re-rasterises the
   glyphs and causes the antialiasing tick. */
.addr-tail {
  display: inline-block;
  white-space: pre;
  min-width: 4ch;
  animation: tail-ink 1.6s ease-in-out both;
}
@keyframes tail-ink {
  from { color: rgba(26, 22, 19, 0); }
  to   { color: var(--e-ink); }
}
.chosen-change {
  font: inherit;
  font-size: var(--text-small);
  color: rgba(26, 22, 19, 0.45);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color 0.4s ease;
}
.chosen-change:hover { color: var(--e-ink); }
.chosen-change:focus-visible {
  outline: 2px solid rgba(26, 22, 19, 0.4);
  outline-offset: 2px;
}

.price-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  border-top: 1px solid rgba(26, 22, 19, 0.1);
  padding-right: 14px;
}
.price-field {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
}
.price-prefix {
  padding-left: 22px;
  font-size: 1.0625rem;
  color: rgba(26, 22, 19, 0.35);   /* stays light always (George) */
}
.price-input {
  flex: 1;
  min-width: 0;
  padding-left: 8px;
}

/* ── The room — the property's standing record ──
   Synthesis of the three-lens design panel (docs/design-notes.md, 2026-07-07).
   State is structure: buyers file under "Shared" / "Invited" section heads,
   never annotated per row. The one gesture is the record's own foot line — a
   quiet place where text begins, closing the body under an inset rule (the
   bordered box and the row wash were tried and struck, 2026-07-07: this
   surface's inputs are quiet lines that wake).

   THE ROOM STANDS IN THE LEDGER NOW (George, 2026-08-21). It was a lifted plane
   over the register — a matted, bordered, shadowed sheet on a blurred field —
   and it is now the content of its own row's fold, on the page's own parchment,
   set to the reading measure and centred under the row. Everything the plane
   carried as a plane is gone with it: the overlay, the backdrop blur, the mat,
   the border, the radius, the shadow, and the masthead that repeated the
   address and the guide price. THE FOLD HAS NO HEAD — the row is the head, and
   the record opens straight onto its buyers. */

/* Buyers file by state — the section head carries what the row used to
   whisper. Zero is silence: a section renders only when it has entries. */
.room-section + .room-section { margin-top: 32px; }
.room-section-head {
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  letter-spacing: 0.01em;
  color: rgba(26, 22, 19, 0.55);
  margin-bottom: 14px;
}
.room-buyer {
  position: relative;
  z-index: 0;
  padding: 16px 2px;
  font-size: 1rem;
}
.room-buyer + .room-buyer {
  border-top: 1px solid rgba(26, 22, 19, 0.06);
}
.buyer-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
}
.buyer-name { color: var(--e-ink); }

/* A shared row is DECLARED AT REST too (George, 2026-08-24, striking the
   empty-gutter rule that stood here — its absence was the mark that nothing
   opened, and the shared row opens now): the one chevron stands in the gutter
   and the signal's full face folds beneath, so every name still shares one
   left edge with the invited names, cut from the mark's own box + gap like
   every other opening row. Name, verdict and the dated meta stay standing.

   THE ROW IS ONE TRACK UNLESS IT HAS A VERB TO PUT IN A SECOND (George,
   2026-08-24: a SUPPORTED row's Remove stands inside its own fold — the morning
   ruling that kept a supported buyer outright, softened the same day — so its
   right edge holds nothing). A declared `1fr auto` reserves its column-gap
   whether or not the second track holds anything, so a supported row would have
   stood its verdict 16px in from the right edge — a shifted badge on the very
   rows whose verb is elsewhere, and the only trace at the right edge of an
   affordance that does not stand there. So the default is the row exactly as it
   stood before removal landed, and the verb track is added only where a
   right-edge verb exists. The fold's own verb is not one: it is inside the fold
   the row spans beneath, at no track of its own.

   The invited face's opposite rule — an empty verb track still holds its gap,
   so the name column never reflows between rows — deliberately does NOT carry
   here. It rests on every invited row wearing the same verbs; shared rows do
   not, and once one shared row has a verb and another has none their badges
   cannot align at any gap, because the verb widths differ. Alignment is not
   purchasable on this face, so the right edge is. */
.room-buyer.is-shared {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  column-gap: var(--space-3);
}
.room-buyer.is-shared:has(.row-remove) { grid-template-columns: 1fr auto; }

/* An invited entry is DECLARED AT REST: a drawn chevron in a fixed leading
   gutter says "this opens", the name wakes to full ink, and the row's verbs
   are its own controls at the right edge. The row is the grid — toggle in column one, then
   one column per verb, each sized to its own words; the fold spans all of them
   beneath. FOUR tracks since removal landed: Copy link, Re-issue, Remove. Each
   verb track stands whether or not the register delivered its block (an empty
   track still holds its gap), so the verbs never reflow the name column between
   rows. Nothing in the gutter ever shifts. */
.room-buyer.is-invited {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: start;
  column-gap: var(--space-3);
}
/* The fold spans every track at BOTH faces. Written once against the row rather
   than once per face: a shared row grew a verb track of its own (the removal
   verb, 2026-08-24), so a rule naming only the invited face would have left the
   shared row's fold sitting in column one, at the width of the name. */
.room-buyer .invite-fold { grid-column: 1 / -1; }

/* The toggle — chevron gutter + name block. No wash, no box (struck): the name
   rests a shade quiet and WAKES to full ink. An open entry holds full ink. */
.buyer-open {
  display: flex;
  align-items: flex-start;
  gap: var(--mark-gap);
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* ── ONE MARK, UNIFORM (George, 2026-08-21) ──
   The property row's chevron on the register and the invited buyer's chevron
   inside its fold are the same mark at two depths, so they are ONE rule: same
   16px box, same gap to the word, same tertiary ink, same curve. Two rules would
   be two marks free to part while every test stayed green. The svg is drawn once
   in agent-property.js and its stroke is `currentColor`, so the ink ladder lives
   here and no colour literal lives in the JS.
   `height: 1.5em` is the name's own line box at either scale — em, not rem,
   because the two rows are set at different sizes — and the box is held OFF the
   baseline so the mark centres on the FIRST line of an address that wraps. */
.ledger-mark,
.buyer-chevron {
  width: var(--mark-box);
  flex: none;
  height: 1.5em;
  align-self: flex-start;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(26, 22, 19, 0.42);
  transition: color var(--motion-hover);
}
.ledger-mark svg,
.buyer-chevron svg {
  transition: transform var(--motion-state);
  transform-origin: center;
}
/* THE HAND ANSWERS IN THE MARK. A row that folds warms its chevron, tertiary
   ink to solid, on the approach. On the PROPERTY row that is the whole answer:
   nothing else on it moves, and this is what replaced its grey wash. Inside the
   record the mark answers beside the name's own warm, which is not new and was
   not touched — the invited row has been declared at rest since 2026-07-08, and
   what George struck was the register row's wash, not that wake (the rule is
   `.buyer-open:hover .buyer-name`, below). */
.ledger-row:hover .ledger-mark,
.buyer-open:hover .buyer-chevron,
.room-buyer.open .buyer-chevron {
  color: var(--e-ink);
}
/* The turn is the chevron's statement about ITS OWN fold. On the buyer row it
   rides the toggle's aria state, not the row's: a row opened by the other verb
   holds the light without claiming the code is showing. On the property row the
   group's open class is that same statement. */
.ledger-group.is-open .ledger-mark svg,
.buyer-open[aria-expanded="true"] .buyer-chevron svg { transform: rotate(90deg); }
.buyer-main { flex: 1; min-width: 0; }
.buyer-issued {
  margin-top: 2px;
  font-size: var(--text-small);
  color: rgba(26, 22, 19, 0.45);
  font-variant-numeric: tabular-nums;
}
.room-buyer.is-invited .buyer-name {
  color: rgba(26, 22, 19, 0.9);
  transition: color 0.4s ease;
}
.buyer-open:hover .buyer-name,
.buyer-open:focus-visible .buyer-name,
.room-buyer.is-invited.open .buyer-name {
  color: var(--e-ink);
}

/* Keyboard focus is visible; mouse focus stays silent (:focus-visible is
   keyboard-driven). This replaces the room controls' outline:none, honouring
   the no-box-on-mouse-return strike. The invite INPUT is excluded (struck,
   2026-07-08): browsers fire :focus-visible on text inputs even for mouse
   focus, which drew a box around a quiet line — this surface's inputs are
   quiet lines that wake, and the caret is the input's own focus mark. */
.buyer-open:focus-visible,
.row-verb:focus-visible,
.fold-verb:focus-visible,
.invite-word:focus-visible {
  outline: 2px solid rgba(26, 22, 19, 0.5);
  outline-offset: 2px;
}
.invite-line input:focus-visible { outline: none; }

/* Copy link — the row's own declared control: an underline marks it a control
   at rest; the label swaps to "Copied" in a reserved slot so nothing moves. */
.row-verb {
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 450;
  color: rgba(26, 22, 19, 0.6);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(26, 22, 19, 0.3);
  transition: color 0.8s ease, text-decoration-color 0.8s ease;
}
.row-verb:hover,
.row-verb:focus-visible {
  color: var(--e-ink);
  text-decoration-color: rgba(26, 22, 19, 0.55);
}
.row-copy { min-width: 4.9em; text-align: left; }
/* A control the hand cannot use must not offer the hand's cursor. The word
   itself never changes while a write is out — no swap, no mark, no wait. */
.row-verb[disabled] { cursor: default; }

/* A pushed signal — a verdict is the ONLY place semantic colour appears. */
.signal-verdict { font-weight: var(--weight-medium); }
.signal-verdict.supported     { color: var(--accept-fg); }
.signal-verdict.not_supported { color: var(--decline-fg); }
.signal-meta {
  margin-top: 2px;
  font-size: var(--text-small);
  color: rgba(26, 22, 19, 0.45);
  font-variant-numeric: tabular-nums;
}
.signal-artifact {
  margin-top: 6px;
  font-size: var(--text-small);
  color: rgba(26, 22, 19, 0.65);
}

/* THE SUPPORTED ROW'S REMOVAL VERB, STANDING UNDER ITS OWN FACE (George,
   2026-08-24). It is the row verbs' line one step quieter, and every step down
   is stated here and nowhere else: the small text size against their 0.9375rem,
   the body's own weight against their 450, the dated meta's ink against their
   0.6, and an underline at the dormant thickness the invite word rests at. It
   wakes to full ink on the row verbs' own dial, so the two answer the hand at
   the same speed — what separates them is where they rest, not how they move.
   No box, no ground, no colour: the semantic inks state verdicts and never dress
   a control, and this one sits a line under a verdict.
   14px is the fold's own act interval (`.reissue-act`), not a number of its
   own — a verb standing under a face steps the same distance at either depth.

   AND IT IS AS WIDE AS ITS OWN WORD. `.reissue-act` is a full-width block and
   can be: it stands alone in its fold. This one stands under prose the agent
   opened the fold to READ, and a full-width control would take a press in the
   whitespace beside the artifact's last line — a removal asked for by a click
   that was not aimed at anything. `fit-content` puts the target on the word. */
.fold-verb {
  display: block;
  width: fit-content;
  text-align: left;
  margin-top: 14px;
  font: inherit;
  font-size: var(--text-small);
  font-weight: var(--weight-regular);
  color: rgba(26, 22, 19, 0.45);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(26, 22, 19, 0.22);
  transition: color 0.8s ease, text-decoration-color 0.8s ease;
}
.fold-verb:hover,
.fold-verb:focus-visible {
  color: var(--e-ink);
  text-decoration-color: rgba(26, 22, 19, 0.45);
}

/* One thing in the light — while an invite is open, the rest of the record
   recedes (the family's attention law: opacity + a breath of blur, on the
   state curve); the open entry alone holds full presence. */
.room-gesture,
.room-section-head,
.room-buyer,
.room-foot {
  transition: opacity var(--motion-state), filter var(--motion-state);
}
.room.attending .room-gesture,
.room.attending .room-section-head,
.room.attending .room-buyer:not(.open),
.room.attending .room-foot:not(.open) {
  opacity: var(--recede-opacity);
  filter: blur(var(--recede-blur));
}

/* The invite, revealed beneath its buyer — one component whether just issued
   or re-opened later (re-sending is a standing affordance). It UNFOLDS: a
   grid-rows fold exhales the sub-record open on the family curve, its content
   uncovered (no opacity fade on any of it — the record was always there, the
   room just opens to it). The verbs and the code fold are quiet lines that
   wake by ink alone; no box, no border, nothing bounded. */
.invite-fold {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--motion-state);
}
.invite-fold.is-unfolded { grid-template-rows: 1fr; }
.invite-fold-inner {
  overflow: hidden;
  min-height: 0;
}

/* The fold holds only the code, behind a left rule that draws what the opened
   thing belongs to — the belonging mark of the declared-at-rest grammar. The
   rule descends visually from the rotated chevron: half the mark's box less
   1px, which lays the 1.5px rule ACROSS the box's own centre line instead of
   beside it — and its content lands on the name's edge; the
   fold's exhale uncovers it (no opacity fade — the record was always there, the
   room just opens to it). */
.invite-reveal {
  margin-left: calc(var(--mark-box) / 2 - 1px);
  border-left: 1.5px solid rgba(26, 22, 19, 0.16);
  padding: 12px 0 16px 22px;
}

/* The code: modules in the room's warm ink on the parchment itself — the
   lib's white ground and black modules are overridden; the quiet zone (its
   margin) is the parchment showing through, kept for scannability. */
.invite-qr { margin-top: 16px; }
.invite-qr svg {
  display: block;
  width: 140px;
  height: 140px;
}
.invite-qr svg rect { fill: transparent; }
.invite-qr svg path { fill: var(--e-ink); }

/* The other thing the fold can hold: the register's note in secondary ink, and
   one act beneath it wearing the row-verb line (its focus ring, its wake and
   its underline all come from .row-verb — nothing is restated here). The note
   is measured for reading, not for the room's width. */
.reissue-note {
  font-size: var(--text-small);
  color: rgba(26, 22, 19, 0.65);
  max-width: 44ch;
}
.reissue-act {
  display: block;
  margin-top: 14px;
}

/* ── The gesture — the record's foot line (the box struck, 2026-07-07) ──
   The record closes with a quiet place where text begins: an inset rule
   closing the body, the section head, then a bare line — no box,
   no border, nothing bounded. "Invite" sits at the line's end, dormant
   until a name is loaded, then waking to ink (the head-line grammar: the
   wake IS the mark). Nothing moves. */
.room-gesture {
  margin-top: 36px;
  border-top: 1px solid rgba(26, 22, 19, 0.1);
  padding-top: 20px;
}
.room-gesture-head {
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  letter-spacing: 0.01em;
  color: rgba(26, 22, 19, 0.55);
  margin-bottom: 2px;
}
.invite-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
}
.invite-line input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 1rem;
  color: var(--e-ink);
  background: transparent;
  border: 0;
  outline: none;
  padding: 8px 0;
}
.invite-line input::placeholder {
  color: rgba(26, 22, 19, 0.38);
  transition: color 0.4s ease;
}
.invite-line input:hover::placeholder,
.invite-line input:focus::placeholder {
  color: rgba(26, 22, 19, 0.55);
}
.invite-word {
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 450;
  color: rgba(26, 22, 19, 0.85);
  background: none;
  border: 0;
  padding: 0 2px;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(26, 22, 19, 0.3);   /* armed */
  transition: color 0.8s ease, text-decoration-color 0.8s ease;
}
/* Dormant until a name is loaded — the action word and its underline both wake
   with the agent's hand. */
.invite-line input:placeholder-shown + .invite-word {
  color: rgba(26, 22, 19, 0.3);
  text-decoration-color: rgba(26, 22, 19, 0.22);   /* dormant */
}
.invite-word:hover {
  color: var(--e-ink);
  text-decoration-color: rgba(26, 22, 19, 0.55);
}
.invite-word:focus-visible { color: var(--e-ink); }

/* ── The record's own foot: one verb for the property itself ──
   It stands LAST, below the invite line, and carries no rule of its own: the
   gesture's hairline already closes the body, and a second rule a few lines
   under it would draw one boundary twice (the settled-ground finding, read for
   this record). The verb is the row-verb family verbatim — its ink, its
   underline, its wake and its focus ring all come from `.row-verb`, and nothing
   is restated here — so the act on the property is as quiet as the act on a
   buyer's row. No colour: the semantic inks state data verdicts and never dress
   a control. Its fold is the buyer rows' own component, so the confirm face
   opens on the same curve at both depths.

   32px is the record's own concept interval (`.room-section + .room-section`),
   not a number of its own: this record steps 36 / 32 / 20 / 16 / 14 and nothing
   between. */
.room-foot { margin-top: 32px; }

/* AND THE FOOT'S FOLD DROPS THE BELONGING RULE. `.invite-reveal`'s left rule
   descends visually from the rotated chevron half a mark-box to its left, and
   the foot has no chevron: at this depth the rule would hang off nothing,
   pointing at a gutter that is not there. The fold, its curve, its content and
   its padding are the buyer rows' unchanged — only the mark that draws a
   belonging the foot does not have comes off. */
.room-foot .invite-reveal {
  margin-left: 0;
  border-left: 0;
  padding-left: 0;
}

/* A quiet note in the room's rhythm — never an error wall. */
.pipeline-note {
  margin: 0;
  font-size: 0.9375rem;
  color: rgba(26, 22, 19, 0.6);
}
