/* ──────────────────────────────────────────────────────────────────────────
   Vectro Rent — token sheet and shell.

   Adapted from ../vectro-family's docs/design/vectro-redesign/tokens.css
   (AGENTS.md: adapt the family mechanism rather than inventing a new one).
   Two deliberate divergences from the family sheet, recorded in
   docs/architecture/adr/0001-web-shell-design-system.md:

   1. No self-hosted display/body webfonts yet. The family sheet ships
      self-hosted Figtree + Fraunces; producing and verifying a Romanian
      (ș/ț) subset of each is real design work nobody has done for this
      product yet. --font-body falls back to the system UI stack, which is
      still fully self-contained (no CDN, no external request) and can be
      replaced with a self-hosted face later without touching any component
      rule below.
   2. Three materials become two. Vectro Rent has no "educational, virtual"
      money — only the landlord's own figures ("real") and the slice of the
      ledger a tenant may see ("shared"). `.m-shared` keeps the family's own
      "shared with a child" treatment (dashed edge, tinted fill) because the
      shape of the idea — "the same fact, deliberately made visible to someone
      else" — is identical; only who that someone is has changed.

   Light is the default. Dark is an explicit override in two directions: the
   media query honours prefers-color-scheme, and a future [data-theme]
   override (none is wired yet — no auth/theme feature has landed) would win
   over it, matching the family convention so that feature can adopt it
   without a second token pass.
   ────────────────────────────────────────────────────────────────────────── */

:root {
  /* ── Colour, light ──
     --v-ink/--v-ink-2/--v-ink-3/--v-bg/--v-paper/--v-hair* and the
     attn/err/ok/focus ramp are numerically identical to the family sheet's
     measured values, but re-measured here rather than trusted verbatim
     (P1-statement-thead-contrast.md found two of the family sheet's own
     claims had drifted): --v-ink-2 is 5.88:1 on paper (not 6.3:1), --v-ink-3
     is ≥4.7:1 on ground and paper but only 4.14:1 on --v-sunk — below the
     4.5:1 AA floor for small text, so --v-sunk fills never take --v-ink-3
     text below var(--fs-small) (see .statement-table thead th, which uses
     --v-ink-2 instead) — and --v-attn is 5.8:1 on its own tint. */
  --v-bg:          #f2efe7; /* page ground. Nothing is written directly on it. */
  --v-paper:       #fbf9f3; /* surface for real (landlord-only) values */
  --v-sunk:        #eae5d8; /* table headers, inert strips */
  --v-quiet:       #ece7d9; /* secondary-button fill, hover fill */

  --v-ink:         #1f3a52; /* brand + primary text + the "real" material */
  --v-ink-2:       #4b6478; /* secondary text — 6.3:1 on paper */
  --v-ink-3:       #57707f; /* labels, eyebrows, small notes — 4.7:1 min */
  --v-on-ink:      #fbf9f3; /* text/icons on a solid --v-ink fill */

  --v-hair:        rgba(31,58,82,.16);
  --v-hair-strong: rgba(31,58,82,.38);

  /* The "shared with the tenant" voice — never on landlord-private figures. */
  --v-shared:      #7a8a5e;
  --v-shared-ink:  #4a5636;
  --v-shared-tint: #eef3e0;

  --v-attn:        #8c491a; /* "this needs you" / overdue. Amber, never error. */
  --v-attn-tint:   #ffe9d8;

  --v-err:         #8a2f25;
  --v-err-tint:    #fbe7e2;
  --v-ok:          #2f6b53;
  --v-ok-tint:     #e2efe6;

  --v-focus:       #c06b2e; /* 3px ring, 3px offset */
  --v-hatch:       rgba(31,58,82,.22);

  /* ── Shape ── */
  --r-xs: 6px; --r-sm: 10px; --r-md: 14px; --r-lg: 24px; --r-pill: 999px;

  /* ── Space, 4px scale ── */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 18px;
  --s-5: 26px; --s-6: 36px; --s-7: 52px;

  /* ── Elevation ── */
  --e-1: 0 1px 2px rgba(20,20,18,.10);
  --e-2: 0 3px 10px rgba(20,20,18,.13);
  --e-3: 0 14px 34px rgba(20,20,18,.20);

  /* ── Motion ── */
  --t-fast: 120ms; --t-mid: 220ms; --t-slow: 420ms;
  --ease: cubic-bezier(.2,.7,.3,1);

  /* ── Type ── */
  --font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --fs-eyebrow: 0.72rem;
  --fs-note:    0.72rem;
  --fs-small:   0.79rem;
  --fs-body:    clamp(0.88rem, 0.85rem + 0.2vw, 0.95rem);
  --fs-lead:    clamp(1rem, 0.95rem + 0.4vw, 1.13rem);
  --fs-h3:      clamp(1.1rem, 1rem + 0.6vw, 1.3rem);
  --fs-h2:      clamp(1.35rem, 1.2rem + 1vw, 1.65rem);
  --fs-h1:      clamp(1.6rem, 1.35rem + 2vw, 2.6rem);

  /* ── Layout ── */
  --gutter: 18px;
  --page-max: 68rem;
  --column-max: 48rem;
  --measure: 34rem;
  --page-pad-y: var(--s-5);
}

/* Money, units, percentages, dates — everywhere, without exception. */
.num { font-variant-numeric: tabular-nums; letter-spacing: -0.005em; }

/* A missing value. Never a zero, never a colour swap alone. */
.hatch {
  background-image: repeating-linear-gradient(
    135deg, var(--v-hatch) 0 1.5px, transparent 1.5px 6px);
}

/* ── Dark, in both directions ─────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
  :root:not([data-theme="light"]) {
    --v-bg:#181a17; --v-paper:#212420; --v-sunk:#131512; --v-quiet:#262922;
    --v-ink:#e7ecef; --v-ink-2:#aab8c2; --v-ink-3:#8695a0; --v-on-ink:#181a17;
    --v-hair:rgba(231,236,239,.16); --v-hair-strong:rgba(231,236,239,.34);
    --v-shared:#aebf92; --v-shared-ink:#cddcb4; --v-shared-tint:#282f20;
    --v-attn:#f6a06b; --v-attn-tint:#3a2617;
    --v-err:#f0a79b; --v-err-tint:#3a201b;
    --v-ok:#8ec7ab;  --v-ok-tint:#1c2c25;
    --v-focus:#f6a06b;
    --v-hatch:rgba(231,236,239,.24);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --v-bg:#181a17; --v-paper:#212420; --v-sunk:#131512; --v-quiet:#262922;
  --v-ink:#e7ecef; --v-ink-2:#aab8c2; --v-ink-3:#8695a0; --v-on-ink:#181a17;
  --v-hair:rgba(231,236,239,.16); --v-hair-strong:rgba(231,236,239,.34);
  --v-shared:#aebf92; --v-shared-ink:#cddcb4; --v-shared-tint:#282f20;
  --v-attn:#f6a06b; --v-attn-tint:#3a2617;
  --v-err:#f0a79b; --v-err-tint:#3a201b;
  --v-ok:#8ec7ab;  --v-ok-tint:#1c2c25;
  --v-focus:#f6a06b;
  --v-hatch:rgba(231,236,239,.24);
}

/* ── Reset ─────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--v-bg);
  color: var(--v-ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.5;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; }

h1 {
  max-width: 24ch;
  margin: 0 0 var(--s-5);
  font-size: var(--fs-h1);
  line-height: 1.06;
  letter-spacing: -0.03em;
}
h2 { font-size: var(--fs-h2); margin: 0 0 var(--s-4); }
h3 { font-size: var(--fs-h3); margin: 0 0 var(--s-3); }

.eyebrow {
  margin: 0 0 var(--s-3);
  font-size: var(--fs-eyebrow);
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--v-ink-3);
}

.lead { font-size: var(--fs-lead); color: var(--v-ink-2); max-width: var(--measure); }

.fine-print {
  max-width: var(--measure);
  margin-top: var(--s-6);
  color: var(--v-ink-2);
  font-size: 0.9rem;
  line-height: 1.5;
}

/* ── Shell ─────────────────────────────────────────────────────────────── */
.shell {
  display: flex;
  min-height: 100svh;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--page-pad-y) var(--gutter);
  flex-direction: column;
  justify-content: flex-start;
}
.shell-narrow { max-width: var(--column-max); }
.shell-centred { justify-content: center; }

/* Visually hidden but still in the accessibility tree and the tab order —
   for content a sighted user does not need (an empty <th> over a row of
   per-row actions, "Acțiuni") but a screen reader still must announce
   (P2-th-scope-missing.md). Clipped rather than display:none/hidden, which
   would also remove it from the tree.

   One name, one rule for this: internal/org/view and internal/declaration/
   view (a W-Q request relayed 2026-08-30, same "Acțiuni" action-column
   header) both use this exact class rather than inventing a second
   spelling. clip-path is the current property; clip(rect...) stays alongside
   it only for the user agents that predate clip-path support -- both
   together is the standard belt-and-braces recipe, and this rule's axe
   scans (e2e/accessibility.spec.ts) see neither as a violation. */
/* The [hidden] attribute must beat every class rule in this file, or it
   stops meaning "hidden". A user agent's own `[hidden] { display: none }` is
   a 0,0,1,0 selector, so any class that sets display — .button-link's
   inline-flex is the one that matters here — silently overrides it, and a
   control the server shipped hidden renders anyway. That is exactly the
   failure mode progressive enhancement is trying to avoid: static/app.js
   reveals a copy button only once it has wired it, and a button that is
   visible before then does nothing when pressed. */
[hidden] { display: none !important; }

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

/* WCAG 2.4.1 (Bypass Blocks): off-screen, not opacity/height, so it stays in
   the tab order. */
.skip-link {
  position: absolute;
  top: 0;
  left: -9999px;
  z-index: 10;
  padding: var(--s-2) var(--s-3);
  background: var(--v-paper);
  color: var(--v-ink);
  font-weight: 700;
  text-decoration: none;
}
.skip-link:focus-visible {
  left: 0;
  outline: 3px solid var(--v-focus);
  outline-offset: 3px;
}

/* ── Chrome (top bars, nav, footers) ──────────────────────────────────────
   Every band below is full-bleed so its rule spans the window, but wraps its
   contents in .chrome-inner, which carries the same column and padding as
   .shell — so the navigation lines up with the content beneath it. */
.chrome-inner {
  max-width: var(--column-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
body:has(main.shell:not(.shell-narrow)) .chrome-inner {
  max-width: var(--page-max);
}

.chrome-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }

.brand-link {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: var(--s-1) var(--s-2) var(--s-1) 0;
  border-radius: var(--r-sm);
  color: var(--v-ink);
  font-weight: 800;
  font-size: var(--fs-lead);
  letter-spacing: -0.02em;
  text-decoration: none;
}
.brand-link:hover, .brand-link:focus-visible { background: var(--v-quiet); }
.brand-mark {
  display: inline-flex;
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-sm);
  background: var(--v-ink);
  color: var(--v-on-ink);
}

/* The landlord's four-destination top bar. Four is a hard limit — spec 02
   names the exact set (Acasă / Proprietăți / Facturi / Chiriași) and it does
   not grow. */
.landlord-nav {
  margin: 0 0 var(--s-5);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--v-hair-strong);
}
.landlord-nav-inner {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: stretch;
  gap: var(--s-4);
}
.landlord-nav-inner .chrome-row {
  margin-inline-start: 0;
  display: grid;
  align-items: stretch;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.landlord-nav-link {
  flex: 0 1 auto;
  display: flex;
  min-height: 44px;
  min-width: 44px;
  align-items: center;
  justify-content: center;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  color: var(--v-ink);
  font-weight: 700;
  text-decoration: none;
  text-align: center;
}
.landlord-nav-link:hover, .landlord-nav-link:focus-visible { background: var(--v-quiet); }
.landlord-nav-link.is-active { background: var(--v-sunk); box-shadow: inset 3px 0 0 var(--v-ink); }

/* The tenant's chrome is deliberately minimal: a brand bar and a session
   block (sign-out, signed-in address) — spec 03: "one screen plus history"
   still holds for destinations; sign-out is chrome, not a destination
   (P1-no-sign-out.md), the same distinction the landlord nav draws. */
.tenant-chrome {
  padding: var(--s-3) 0 var(--s-2);
  border-bottom: 1px solid var(--v-hair);
  margin: 0 0 var(--s-5);
}

/* The session block both chromes share: signed-in address, a sign-out form,
   and — landlord only — links into /org and /jurnal, otherwise unreachable
   from any nav (P1-no-sign-out.md, P1-org-page-orphan.md,
   P1-jurnal-orphan.md). A row that wraps, not a second nav: it carries no
   aria-label and asserts no destination semantics. */
.session-block {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  margin-top: var(--s-2);
  padding-top: var(--s-2);
}
.session-identity {
  margin: 0;
  flex: 1 1 auto;
}
.session-block a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--s-1) 0;
  font-weight: 700;
  text-decoration: underline;
}
.session-block form { margin: 0; }
.session-block .button-link-secondary { padding: var(--s-1) var(--s-3); }

.chrome-version {
  margin: var(--s-2) 0 0;
  padding-inline-start: var(--gutter);
  color: var(--v-ink-3);
  font-size: var(--fs-note);
  font-variant-numeric: tabular-nums;
}

/* ── Buttons and form controls: 44x44 minimum touch target ───────────────── */
button, .button-link, .button-link-secondary, input, select, textarea { min-height: 44px; }
button, .button-link, .button-link-secondary { min-width: 44px; }

/* Every form label wraps its control (`<label>Text<input/></label>`), but
   nothing made that a block before now — labels and inputs flowed inline
   like prose, so on a narrow screen a label could visually attach to the
   wrong field (P1-forms-label-not-block.md: the mobile payment form's "Suma
   (RON)" sat beside the date input while the amount box floated below,
   apparently unlabeled). One rule fixes every form in the product at once. */
form label {
  display: block;
  margin-block: var(--s-3);
  max-width: 28rem;
}
form label > input, form label > select, form label > textarea {
  display: block;
  width: 100%;
  margin-top: var(--s-1);
}

/* A list whose items are cards or notes, not prose bullets: the attention
   queue, the lease page's pending declarations. */
.plain-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }

/* A refused submission (`<p role="alert">`, in every view package's templates) used
   to render as an ordinary paragraph — same colour and weight as body text
   — so a rejected form looked like a no-op rather than a refusal
   (P2-role-alert-unstyled.md). Same visual language as .state-note.state-attn
   ("this needs you"), plus a left border so the message reads as attached to
   the field it refused, not just another line of body copy. */
[role="alert"] {
  max-width: var(--measure);
  margin: var(--s-3) 0;
  border: 1px solid var(--v-attn);
  border-left-width: 4px;
  border-radius: var(--r-xs);
  padding: var(--s-2) var(--s-3);
  background: var(--v-attn-tint);
  color: var(--v-attn);
  font-weight: 700;
}

input[type="checkbox"], input[type="radio"] {
  width: 1.5rem;
  height: 1.5rem;
  min-height: 1.5rem; /* hit area is the wrapping label, not the box itself */
  min-width: 1.5rem;
  accent-color: var(--v-ink);
}

/* .button-link-secondary carries the full base rule, not just a colour
   override, so it is a complete affordance on its own (P2-affordance-
   inconsistency.md: internal/invoice/view/pages.templ:246 used it with no
   base .button-link class and got no pill, no padding, and no 44px target —
   an exception a user has to relearn each time they meet it). Supervisor
   decision: apply the pill consistently to every secondary action; the 44px
   target is an accessibility floor, not a style choice.

   Which anchors are "secondary actions" is one line: an action standing on
   its own — in its own <p>, in a .row-actions row, in a pagination <nav> —
   wears "button-link button-link-secondary". A link inside a table cell, a
   list item, a definition list or a running sentence is a *content* link, a
   different tier, and stays a plain anchor; pilling those turns a table into
   a wall of buttons and destroys the distinction the pill exists to make.
   internal/web/view/affordance_test.go enforces the syntactic core of this
   over every view package's .templ files. */
button, .button-link, .button-link-secondary {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--r-pill);
  padding: var(--s-3) var(--s-4);
  background: var(--v-ink);
  color: var(--v-on-ink);
  font: inherit;
  font-weight: 750;
  text-decoration: none;
  cursor: pointer;
}
.button-link-secondary { background: var(--v-quiet); color: var(--v-ink); }

button:focus-visible, .button-link:focus-visible, .button-link-secondary:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible, a:focus-visible {
  outline: 3px solid var(--v-focus);
  outline-offset: 3px;
}
button:disabled { opacity: 0.75; cursor: progress; }

/* ── The two materials ─────────────────────────────────────────────────────
   Fill, edge and radius are fixed per material so colour is never the only
   carrier; materialMark (internal/web/view/material.templ) supplies the
   fourth property, the glyph + word. Never hand-style a card. */
/* materialClasses (internal/web/view/material.templ) always emits "card
   m-{real,shared}" together, but .card itself was never defined — every
   card rendered with its content flush against its own border
   (P1-card-class-undefined.md). Padding is the one thing every material
   shares, so it lives here rather than being duplicated on .m-real/.m-shared. */
.card { padding: var(--s-4); }

.m-real {           /* the landlord's own figures — private */
  background: var(--v-paper);
  border: 1px solid var(--v-hair-strong);
  border-radius: var(--r-sm);
}
.m-shared {          /* the slice of the ledger a tenant may see */
  background: var(--v-shared-tint);
  border: 1px dashed var(--v-shared);
  border-radius: var(--r-sm);
}
.material-mark {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  margin: 0 0 var(--s-2);
  font-size: var(--fs-note);
  font-weight: 700;
  color: var(--v-ink-2);
}
.m-shared .material-mark { color: var(--v-shared-ink); }
.material-glyph { flex: 0 0 auto; width: 1.1em; height: 1.1em; }

/* .row-actions groups a card's secondary edit/link controls (lease,
   property); it was referenced with no rule at all (P1-card-class-
   undefined.md), so the buttons it wraps fell back to block layout with no
   gap between them. */
.row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin: var(--s-3) 0 0;
}

/* .headline-balance is the tenant's one most important number
   (internal/tenantportal); it was referenced with no rule, so it rendered as
   an unstyled default <h2> with no more visual weight than a section title. */
.headline-balance {
  margin: 0 0 var(--s-2);
  font-size: var(--fs-h1);
  font-weight: 750;
  letter-spacing: -0.02em;
}

/* ── State vocabulary (reduced: ok / attn / pending / empty) ──────────────
   Vectro Rent has no stale-price, missing-price or mixed-currency states —
   those belong to a market-priced portfolio, not a rent ledger. */
.state-note {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  max-width: var(--measure);
  margin: var(--s-3) 0;
  border: 1px solid var(--v-hair-strong);
  border-radius: var(--r-sm);
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-body);
  line-height: 1.5;
  font-weight: 600;
}
.state-note .state-text { color: inherit; }
.state-glyph { flex: 0 0 auto; width: 1.15em; height: 1.15em; }

.state-note.state-ok { border-color: var(--v-ok); background: var(--v-ok-tint); color: var(--v-ok); }
.state-note.state-attn { border-color: var(--v-attn); background: var(--v-attn-tint); color: var(--v-attn); }
.state-note.state-pending { border-style: dashed; border-color: var(--v-hair-strong); color: var(--v-ink-2); }
.state-note.state-empty { border-style: dashed; background: var(--v-sunk); color: var(--v-ink-2); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* At 375px (specs/02's stated primary viewport) the four-column grid above
   leaves each link too narrow for its own label, and nothing let the row
   wrap — "Acasă Proprietăți Facturi Chiriași" rendered jammed together with
   overlapping glyphs (P1-mobile-nav-collision.md). With only four fixed
   destinations, two rows of two is the cheapest correct answer — no
   hamburger needed. */
@media (max-width: 30rem) {
  .landlord-nav-inner { grid-template-columns: 1fr; row-gap: var(--s-2); }
  .landlord-nav-inner .chrome-row {
    display: flex;
    flex-wrap: wrap;
  }
  .landlord-nav-link {
    flex: 1 1 calc(50% - var(--s-2));
    font-size: var(--fs-small);
    padding: var(--s-2) var(--s-1);
  }
}

/* ── Statements (WS-24, specs/07) ──────────────────────────────────────────
   Appended as one block at the end of the sheet on purpose: six workstreams
   edit this file concurrently, and an append conflicts with nobody. A later
   pass should fold anything here that turns out to be general (the table, in
   particular) back up into the shared vocabulary above. */
.statement-list { list-style: none; margin: 0; padding: 0; }
.statement-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--v-hair);
  padding: var(--s-3) 0;
}
.statement-list li > a { font-weight: 700; }

.statement-badge {
  border: 1px solid var(--v-hair-strong);
  border-radius: var(--r-pill);
  padding: 2px var(--s-2);
  color: var(--v-ink-2);
  font-size: var(--fs-note);
  font-weight: 700;
}
.statement-badge-on { border-color: var(--v-ok); color: var(--v-ok); background: var(--v-ok-tint); }
.statement-badge-off { border-color: var(--v-attn); color: var(--v-attn); background: var(--v-attn-tint); }

/* The link is shown exactly once and is meant to be copied, so its input and
   its copy button sit on one row that wraps to two on a narrow phone rather
   than squeezing either below a usable size (P3-share-link-no-copy.md). The
   input takes whatever is left: a share URL is longer than any phone is
   wide, and the more of it is visible the less the copy is an act of faith. */
.statement-link {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
}
.statement-link-value {
  flex: 1 1 12rem;
  min-width: 0;
  border: 1px solid var(--v-hair-strong);
  border-radius: var(--r-xs);
  padding: var(--s-2) var(--s-3);
  background: var(--v-sunk);
  color: var(--v-ink);
  font: inherit;
  font-size: var(--fs-small);
}

/* The pre-2026-08-30 spelling of the same value, a <code> block. Kept
   because nothing guarantees this is the only surface that ever shows a
   token, and it costs three lines. */
.statement-link code {
  display: block;
  overflow-wrap: anywhere;
  border-radius: var(--r-xs);
  padding: var(--s-2) var(--s-3);
  background: var(--v-sunk);
  font-size: var(--fs-small);
}

/* The sheet itself: the one surface in this product that is designed to be
   printed as well as read. */
.statement-sheet {
  margin: 0 0 var(--s-5);
  border: 1px solid var(--v-hair-strong);
  border-radius: var(--r-sm);
  padding: var(--s-5);
  background: var(--v-paper);
}
.statement-head p { margin: 0 0 var(--s-1); color: var(--v-ink-2); }

.statement-balances {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-6);
  margin: var(--s-5) 0;
  border-block: 1px solid var(--v-hair-strong);
  padding: var(--s-4) 0;
}
.statement-balances dt { color: var(--v-ink-3); font-size: var(--fs-note); font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
.statement-balances dd { margin: var(--s-1) 0 0; font-size: var(--fs-h3); font-weight: 750; }
.statement-closing { font-size: var(--fs-h2); }

.statement-group { margin: 0 0 var(--s-5); }
.statement-group h2 { font-size: var(--fs-h3); margin: 0 0 var(--s-2); }

/* Wide content scrolls inside its own container rather than the page. */
.statement-table {
  display: block;
  overflow-x: auto;
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-small);
}
.statement-table th, .statement-table td {
  border-bottom: 1px solid var(--v-hair);
  padding: var(--s-2) var(--s-3);
  text-align: left;
  vertical-align: top;
}
/* --v-ink-3 on --v-sunk is 4.14:1 at this size — below AA's 4.5:1 floor for
   small text (P1-statement-thead-contrast.md). --v-ink-2 on --v-sunk is
   4.92:1; dark mode already passed (5.96:1) so only the light value moves. */
.statement-table thead th { background: var(--v-sunk); color: var(--v-ink-2); font-size: var(--fs-note); text-transform: uppercase; letter-spacing: 0.08em; }
.statement-table td.num, .statement-table th:last-child, .statement-table td:last-child { text-align: right; }
.statement-table tfoot th, .statement-table tfoot td { border-top: 2px solid var(--v-hair-strong); border-bottom: 0; font-weight: 750; }
.statement-table s { color: var(--v-ink-3); }

/* ── Wide tables scroll themselves, not the page ───────────────────────────
   WCAG 2.1 AA 1.4.10 (Reflow) and specs/02's "Mobile-first; every action
   reachable at 44x44": a page wider than the phone showing it puts controls
   past the right edge, where they cannot be seen or tapped. Measured on the
   org home with one pending invitation — 419px of layout in a 360px
   viewport, the overflow coming from the invitations table's email column.

   A data table is the one thing that legitimately needs two dimensions, so
   the fix is not to squeeze it: it becomes its own horizontal scroll
   container. This is the FALLBACK shape — the table itself scrolls — kept
   for any table under .shell that .table-scroll below has not wrapped yet
   (several packages are mid-migration onto that wrapper in this same
   wave; a bare `<table>` here must not silently reflow the whole page in
   the meantime, on this branch or anyone else's). A table that IS wrapped
   in .table-scroll has this undone directly below it, so only the wrapper
   scrolls — see that rule's own comment for why both trying to scroll at
   once is worse than either alone (relayed by W-N, measured against
   lease/invoice's wrapped tables 2026-08-30: a tabindex on the wrapper
   reached nothing because the *table* was still the element that actually
   scrolled). */
.shell table {
  display: block;
  overflow-x: auto;
  max-width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-small);
}

/* Cell padding/border/header treatment used to be scoped to .statement-table
   alone, so every other table (ledger, invoices, tenants, org members,
   declarations) rendered with browser-default ~2px cell spacing inside an
   otherwise designed card, and its row actions came in under WCAG 2.2's 24px
   minimum target (P2-table-system-gaps.md). Generalised here so any table
   under .shell gets the same treatment; .statement-table's own rules below
   still apply on top for its footer/number-column specifics. */
.shell table th, .shell table td {
  border-bottom: 1px solid var(--v-hair);
  padding: var(--s-2) var(--s-3);
  text-align: left;
  vertical-align: top;
}
.shell table thead th {
  background: var(--v-sunk);
  color: var(--v-ink-2);
  font-size: var(--fs-note);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* A plain <table> cannot be given a tabindex, so the fallback above (the
   table itself as the scroll container) leaves a keyboard user no way to
   reach columns clipped past the viewport edge (axe
   scrollable-region-focusable; P2-table-system-gaps.md). The fix is a
   focusable wrapper around an ordinary table, doing the scrolling the
   table used to do for itself:
     <div class="table-scroll" tabindex="0" role="region" aria-label="…">
       <table>…</table>
     </div>
   role="region" with an aria-label is part of the shape, not decoration.
   tabindex="0" alone satisfies axe's scrollable-region-focusable, but it
   also makes the wrapper a stop on the tab order, and a focus stop that
   reaches a screen reader as an unnamed group tells its user nothing about
   what they have landed in — which is the whole reason the audit
   (P2-table-system-gaps.md) prescribed the named form. An earlier revision
   of this comment argued the label was one more thing to keep in sync for
   no rule the scan enforces; three packages had already shipped the named
   form by then, so the sync cost was being paid either way and the
   unlabelled half was simply the worse one. The label names the table, not
   the card: "Fișa de cont", "Membri", "Evenimente". */
.table-scroll {
  /* position: relative makes this wrapper the containing block for any
     position:absolute descendant (.visually-hidden, used on an empty
     action-column header cell here). Without it, such a descendant's
     containing block is the initial containing block (nothing between it
     and <body> is positioned), so it is placed and clipped as if this
     wrapper's overflow-x:auto did not exist -- its geometric box, not just
     its ink, escapes the scroll region and widens document.scrollWidth on
     its own (measured: a 1px-wide, fully clipped .visually-hidden span
     alone accounted for the whole remaining org-home.reflow overflow after
     the table itself was correctly contained). */
  position: relative;
  overflow-x: auto;
  max-width: 100%;
}
/* Undoes .shell table's fallback display:block/overflow-x/max-width on a
   table this wrapper already owns: without this, the <table> and its own
   .table-scroll both set overflow-x:auto, and only one of the two scroll
   containers a browser lays out is the one that actually scrolls — a
   tabindex on the *other* reaches nothing (relayed by W-N, measured
   against lease/invoice's wrapped tables 2026-08-30). With this, the
   <table> goes back to being real table markup (display:table,
   overflow-x:visible) and the wrapper is the only scroll container. width:
   100% only stretches a table narrower than the wrapper to fill it; a
   table whose columns need more room still grows past 100% in table
   auto-layout and triggers the wrapper's scroll, exactly as intended. */
.table-scroll table {
  display: table;
  overflow-x: visible;
  max-width: none;
  width: 100%;
}
.table-scroll:focus-visible {
  outline: 3px solid var(--v-focus);
  outline-offset: 3px;
}

/* An invitation link, a share link and a share token are single unbroken
   strings longer than a phone is wide, and nothing in them is a break
   opportunity a browser will take on its own. They wrap anywhere rather
   than pushing the page sideways — the same 1.4.10 reason as the tables
   above. Measured on the org home showing a just-created invitation: 415px
   of layout in a 360px viewport, all of it one <code>. */
.shell code {
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ── Print: A4, ink-economical ─────────────────────────────────────────────
   specs/07: "Statement pages are print-optimized HTML (A4 CSS); PDF
   generation is deferred — the browser's Save-as-PDF is the v1 exporter."
   So this block is not a nicety, it is the export. It drops every control
   (nothing on paper can be clicked), forces the light palette (a dark-mode
   screenshot of a statement wastes a cartridge), and keeps a group's table
   from being split across a page break. */
@media print {
  @page { size: A4; margin: 16mm; }
  /* Specificity must match (and beat) the dark-scheme block above
     (:root:not([data-theme="light"]), 0,2,0): the plain ":root" selector
     this rule used to open with is only 0,1,0, so a browser honouring
     prefers-color-scheme: dark during print emulation let the dark tokens
     win over this override, computing dark ink (#e7ecef) on the
     forced-white print body — 1.19:1, effectively invisible
     (P1-print-dark-invisible.md). Save-as-PDF is the v1 statement exporter,
     so this sits on the export's critical path. */
  :root, :root:not([data-theme="light"]), :root[data-theme="dark"] {
    --v-bg: #fff; --v-paper: #fff; --v-sunk: #fff; --v-quiet: #fff;
    --v-ink: #000; --v-ink-2: #333; --v-ink-3: #444; --v-on-ink: #fff;
    --v-hair: rgba(0,0,0,.25); --v-hair-strong: rgba(0,0,0,.55);
    --v-ok: #000; --v-ok-tint: #fff; --v-attn: #000; --v-attn-tint: #fff;
    --v-shared: #000; --v-shared-ink: #000; --v-shared-tint: #fff;
  }
  body { background: #fff; font-size: 10.5pt; }
  .landlord-nav, .tenant-chrome, .skip-link, .chrome-version,
  form, button, .button-link, .statement-link { display: none !important; }
  /* On a page that has a sheet, the sheet is the whole document: the share
     panel, the claim CTA and the back link are screen furniture. Written
     with :has() so a browser without it prints the extra blocks rather than
     printing nothing — the safe direction to fail. */
  main:has(.statement-sheet) > *:not(.statement-sheet) { display: none !important; }
  .shell { display: block; min-height: 0; max-width: none; padding: 0; }
  .statement-sheet { border: 0; border-radius: 0; padding: 0; }
  .statement-table { display: table; overflow: visible; }
  /* The screen fallback above turns an unwrapped table into a scroll
     container; on paper there is nothing to scroll and a block table drops
     its column widths, so it goes back to being a table. A table already
     inside .table-scroll is already display:table on screen (that rule
     undoes the fallback), so this only needs to catch the fallback case —
     harmless to apply to both. */
  .shell table { display: table; overflow: visible; max-width: none; }
  /* .table-scroll's own overflow would otherwise clip a wide table to
     whatever fit on screen instead of letting it flow onto the page the
     way paper actually works. */
  .table-scroll { overflow: visible; max-width: none; }
  .statement-group { break-inside: avoid; }
  .statement-table thead { display: table-header-group; }
  a[href]::after { content: ""; }
}

/* ── Expense grid (specs/09 "The grid") ───────────────────────────────────
   Heat shading is five discrete classes computed in Go from a cell's own
   row-relative ratio (internal/expense/handler.go's heatBucket) rather than
   an inline style or a chart library — specs/09: "Cell background shaded
   relative to that line's year maximum (pure CSS, no JS)". Missing/pending/
   not-expected read as words or a quiet dash, never as a colour alone
   (this file's own .hatch precedent for "a missing value is never a colour
   swap alone"). */
.expense-cell { white-space: nowrap; }
.expense-cell-missing { color: var(--v-attn); font-weight: 700; }
.expense-cell-pending { color: var(--v-ink-3); }
.expense-cell-not_expected { color: var(--v-ink-3); }
.expense-cell-amount.heat-1 { background: transparent; }
.expense-cell-amount.heat-2 { background: color-mix(in srgb, var(--v-ink) 8%, transparent); }
.expense-cell-amount.heat-3 { background: color-mix(in srgb, var(--v-ink) 16%, transparent); }
.expense-cell-amount.heat-4 { background: color-mix(in srgb, var(--v-ink) 26%, transparent); }
.expense-cell-amount.heat-5 { background: color-mix(in srgb, var(--v-ink) 38%, transparent); }
.expense-cell-amount a { color: inherit; text-decoration: underline; }

/* ── Tabs (Facturi's Situație / Toate facturile pair) ─────────────────────
   A plain <a> outside .button-link/.button-link-secondary's own 44px rule
   (line 380 above), so it needs its own target-size floor. */
.tab-row {
  display: flex;
  gap: var(--s-4);
  margin: var(--s-4) 0;
  border-bottom: 1px solid var(--v-hair);
}
.tab-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--s-2);
  color: var(--v-ink-2);
  font-weight: 700;
  text-decoration: none;
  border-bottom: 3px solid transparent;
}
.tab-link[aria-current="true"] {
  color: var(--v-ink);
  border-bottom-color: var(--v-ink);
}
.tab-link:focus-visible {
  outline: 3px solid var(--v-focus);
  outline-offset: 2px;
}

/* ── Invoice upload's two-pane form (specs/09 "Upload with live preview") ──
   Mobile-first: one column (document + preview, then the fields, stacked)
   below 40rem; two panes side by side above it. app.js wires the preview
   itself — this is only the layout it renders into. */
.upload-panes {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-5);
  align-items: start;
}
@media (min-width: 40rem) {
  .upload-panes { grid-template-columns: 1fr 1fr; }
}
.upload-preview-pane {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 12rem;
  border: 1px dashed var(--v-hair-strong);
  border-radius: var(--r-md);
  background: var(--v-sunk);
  padding: var(--s-3);
  overflow: auto;
}
.upload-preview-pane img,
.upload-preview-embed {
  max-width: 100%;
  max-height: 24rem;
}
.upload-preview-embed { width: 100%; height: 24rem; border: 0; }

/* The "Pe luni" table (internal/monthly/view): its first four columns — the
   month, what it owed, what was paid, the balance it closed on — are the
   whole check a reader makes, so on a phone they must fit before the
   breakdown columns scroll. Tighter cells here buy that width. */
@media (max-width: 30rem) {
  .shell .monthly-table th, .shell .monthly-table td { padding-inline: var(--s-2); font-size: var(--fs-small); }
}
