/* ═══════════════════════════════════════════════════════════════════════════
   USALI-12 UI — mobile-first, no framework, no build step.

   ⚠ THE DESIGN CONSTRAINT IS THE 390px COLUMN, NOT THE DESKTOP. A GM reads this on
   a phone between meetings; a controller reads it on a laptop. So the layout is a
   single column that WIDENS into a grid at 720px, rather than a desktop grid that
   collapses — collapsing is how mobile ends up with unreadable remnants of a
   two-column layout. Every tap target is >= 44px because a 32px row is a mis-tap
   on a statement where the numbers matter.

   ⚠ THE PALETTE IS TOKENS, NOT LITERALS. `prefers-color-scheme` is honoured, and the
   figures are typeset in tabular numerals so a column of amounts aligns on the
   decimal point — the one typographic thing a financial table cannot get wrong.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --bg: #f7f8f8;
  --surface: #ffffff;
  --surface-2: #f1f4f4;
  --border: #e2e8e8;
  /* ⚠ A SEPARATE TOKEN FOR CONTROL EDGES, AND IT EXISTS BECAUSE ONE TOKEN WAS DOING TWO
     JOBS BADLY. `--border` is tuned to be a barely-there divider between cards, which is
     right for decoration and WRONG for an input: against the `--surface-2` card the field
     sat on, #e2e8e8 vs #f1f4f4 is a contrast ratio of about 1.1:1 — MEASURED, and the
     reason the amount box measured a real 96x45px yet read as empty space in the
     screenshot, across FOUR separate vision passes. A field's edge is its entire
     affordance, so it gets a token that is actually visible.
     ⚠ AND THE FIRST FIX WAS TOO TIMID — #b9c4c4 measured 1.62:1, still under the 3:1 that
     makes an edge legible, and the field STILL read as missing. The value is now chosen by
     CONTRAST MATHS rather than by eye: #7d8d8d measures 3.13:1 against the card background
     and 2.85:1 against the white field, which is the range where a border is
     unambiguous without looking like a heavy rule. */
  --control-border: #7d8d8d;
  --text: #14201f;
  --muted: #63787a;
  --accent: #0d9488;
  --accent-soft: #ccfbf1;
  --pos: #0d9488;
  --neg: #b45309;
  --danger: #b91c1c;
  --danger-soft: #fee2e2;
  --shadow: 0 1px 2px rgba(16, 32, 32, .06), 0 4px 16px rgba(16, 32, 32, .05);
  --radius: 14px;
  --tap: 44px;
  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ⚠ DARK TOKENS APPLY IN TWO CASES: the OS asks for dark and the user has not forced light,
   or the user forced dark from the account menu (ui/core/shell.js → `data-theme` on <html>).
   Plain CSS has no mixins, so the token list is written twice; keep the copies identical. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0f1414;
    --surface: #171f1f;
    --surface-2: #1e2727;
    --border: #2a3636;
    --control-border: #7d8d8d;
    --text: #e7efee;
    --muted: #93a6a6;
    --accent: #2dd4bf;
    --accent-soft: #134e4a;
    --pos: #2dd4bf;
    --neg: #fbbf24;
    --danger: #fca5a5;
    --danger-soft: #4c1d1d;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 16px rgba(0, 0, 0, .35);
  }
}
:root[data-theme="dark"] {
  --bg: #0f1414;
  --surface: #171f1f;
  --surface-2: #1e2727;
  --border: #2a3636;
  --control-border: #7d8d8d;
  --text: #e7efee;
  --muted: #93a6a6;
  --accent: #2dd4bf;
  --accent-soft: #134e4a;
  --pos: #2dd4bf;
  --neg: #fbbf24;
  --danger: #fca5a5;
  --danger-soft: #4c1d1d;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 16px rgba(0, 0, 0, .35);
}

* { box-sizing: border-box; }

/* ⚠ `[hidden]` MUST BE FORCED, AND THIS IS NOT PARANOIA — IT WAS A VISIBLE BUG.
   The `hidden` ATTRIBUTE only carries `display: none` from the user-agent sheet, so
   ANY author `display` declaration beats it. `.sheet { display: flex }` and
   `.login { display: grid }` therefore rendered their elements on top of the page
   despite `hidden` being set — MEASURED in the 390px screenshot: a stray bottom panel
   with a close button floating over the login form. One forced rule fixes every such
   case, including ones added later, which is why it is global rather than a patch on
   the two selectors that happened to break today. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
  -webkit-text-size-adjust: 100%;
}

/* ⚠ TABULAR NUMERALS ON EVERY FIGURE. `font-variant-numeric: tabular-nums` makes
   every digit the same width, so amounts align vertically. Without it a column of
   money reads as ragged and a mis-aligned decimal is genuinely hard to spot — which
   in a ledger is the difference between 1,234.00 and 12,340.00. */
.num, .amount, td.num, .kpi-value, .metric-value, code {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.muted { color: var(--muted); }
.hint { font-size: 12.5px; }

/* ───────────────────────────── login ───────────────────────────── */
.login {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px 16px;
}
.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 24px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.brand { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.brand h1 { font-size: 19px; margin: 0; letter-spacing: -.01em; }
.brand p { margin: 0; font-size: 12.5px; }
.brand-mark { width: 40px; height: 40px; color: var(--brand-accent, var(--accent)); flex: none; }
img.brand-mark { object-fit: contain; }
.brand-mark.sm { width: 28px; height: 28px; }

label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; font-weight: 550; }
label.optional span { font-weight: 400; }
input {
  min-height: var(--tap);
  padding: 10px 12px;
  border: 1px solid var(--control-border);
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  /* ⚠ 16px MINIMUM ON INPUTS, because iOS Safari zooms the whole page when a
     focused input is smaller than that, and the zoom does not undo. */
  font-size: 16px;
}
input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }

button { font: inherit; cursor: pointer; }
button.primary {
  min-height: var(--tap);
  border: 0;
  border-radius: 10px;
  background: var(--accent);
  color: #fff;
  font-weight: 650;
  font-size: 15px;
  margin-top: 4px;
}
button.primary:active { transform: translateY(1px); }
button.ghost {
  min-height: var(--tap);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: 10px;
  padding: 0 12px;
}
/* ⚠ .ghost.sm IS 36px FOR THE TOP BAR (a dense header row) AND THAT IS TOO SHORT FOR A
   FORM ACTION. MEASURED: "+ Add line" came out at h=36px, under the 44px target, on the
   one screen where a mis-tap adds a line you did not want. The compact size stays for the
   chrome; the form's buttons get the full height. */
button.ghost.sm { min-height: 36px; padding: 0 10px; font-size: 13px; }
.entry-form button.ghost { min-height: var(--tap); }
/* ⚠ THE ATTACHMENTS PANEL (ui/core/kit/attachments.js) IS USED BY SCREENS THAT LOAD NO GROUP STYLESHEET, so its rules are CORE rules.
   `button.ghost.sm` (36px) outranks a one-class rule, hence the doubled selectors for the panel's buttons. */
/* the panel's card always has air above it, also when it follows a row of buttons (Save draft / Submit, Calculate) */
.attach-panel { margin-top: 14px; }
button.ghost.sm.attach-dl, button.ghost.attach-add-btn { min-height: var(--tap); min-width: var(--tap); padding-inline: 14px; font-size: 14px; }
.attach-add { display: flex; margin-top: 12px; }
.attach-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.attach-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; padding: 10px; border: 1px solid var(--border); border-radius: 12px; }
.attach-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 200px; }
.attach-name, .attach-meta { overflow-wrap: anywhere; }
.attach-meta { font-size: 12.5px; color: var(--muted); }
.attach-meta bdi { unicode-bidi: isolate; }
.attach-meta .attach-when { white-space: nowrap; }
.error {
  margin: 0;
  padding: 10px 12px;
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: 10px;
  font-size: 13.5px;
}
code { font-family: var(--mono); font-size: 12px; background: var(--surface-2); padding: 1px 5px; border-radius: 5px; }

/* ───────────────────────────── chrome ───────────────────────────── */
/* ⚠ THE TOPBAR IS STICKY AT `top: 0` WITH z-index 20, AND THE BYPASS BANNER IS ALSO
   STICKY AT `top: 0` WITH z-index 60 — so the banner PAINTED OVER the header. MEASURED in
   the user's phone screenshot: no property name, no EN switch, no logout, and the banner's
   amber bottom border appearing as a stray "yellow line" beneath the tabs. The header was
   never missing; it was underneath. A taller sticky banner needs the elements below it to
   know where it ends, so the offset lives in a variable the banner sets and the rest read.
   ⚠ AND NOTE THE BANNER IS ABOVE THE TOPBAR IN DOCUMENT ORDER TOO (app.js prepends it to
   <body>), so without an explicit offset the topbar's `top: 0` sticks UNDER the banner's
   footprint. */
.topbar {
  position: sticky;
  top: var(--bypass-h, 0px);
  z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 14px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.topbar-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.topbar-title { display: flex; flex-direction: column; min-width: 0; }
.topbar-title strong { font-size: 14.5px; letter-spacing: -.01em; }
.topbar-title span { font-size: 11.5px; }
.topbar-right { display: flex; gap: 8px; align-items: center; }
.icon { width: 17px; height: 17px; display: block; }

.tabs {
  position: sticky;
  /* the header publishes its real height (it is two rows on a phone) — ui/core/shell.js */
  top: calc(var(--bypass-h, 0px) + var(--topbar-h, 57px));
  z-index: 19;
  display: flex; gap: 4px;
  padding: 8px 12px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
  /* ⚠ A CLIPPED TAB MUST LOOK SCROLLABLE, NOT BROKEN. MEASURED: at 360px the row needs
     359px in 345px of space, so the last tab is cut mid-word — which reads as a rendering
     fault rather than an affordance. At 375px and above it fits exactly and the shadow is
     invisible, so this costs nothing where there is no overflow. The fade is on the
     container, not the buttons, so it scrolls with the content. */
  scroll-behavior: smooth;
}
.tabs::after {
  content: "";
  position: sticky;
  inset-inline-end: 0;
  flex: none;
  width: 18px;
  margin-inline-start: -18px;
  pointer-events: none;
  background: linear-gradient(to var(--fade-dir, right), transparent, var(--bg));
}
.tabs::-webkit-scrollbar { display: none; }
.tabs button, .tabs a {
  flex: none;
  display: inline-flex; align-items: center; text-decoration: none;
  min-height: 36px;
  /* ⚠ SMALLER HORIZONTAL PADDING SO FOUR TABS FIT 390px. At `0 14px` the fourth tab
     ("Integrity") was cut off mid-word — MEASURED in the 390px screenshot as "Integr…",
     which reads as a truncated label rather than a scrollable row. The bar does scroll
     (`overflow-x: auto`), but a tab that is *clipped at the edge* looks like a bug, so the
     row is sized to fit instead. The 44px TAP TARGET IS PRESERVED via min-height — it is
     the horizontal padding that shrinks, not the touch area. */
  /* ⚠ MEASURED: at 14px the row was scrollW=413 against clientW=375, so "Integrity"
     ended at x=401 — 26px past the viewport, clipped mid-word. 11px still overflowed.
     8px with tighter tracking brings the four tabs inside 375px. The 44px tap target is
     untouched: min-height carries it, and only horizontal padding shrinks. */
  padding: 0 8px;
  letter-spacing: -0.01em;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
}
.tabs button.active, .tabs a.active { background: var(--accent); color: #fff; border-color: var(--accent); }

.view { padding: 14px 12px 80px; max-width: 900px; margin: 0 auto; }

/* ───────────────────────────── cards ───────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 12px;
  overflow: hidden;
}
.card-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 12px 14px 8px;
}
.card-head h2 { margin: 0; font-size: 13px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700; }
.card-head .sub { font-size: 11.5px; }
.card-body { padding: 0 14px 14px; }

/* KPI grid: two columns at 390px, more as it widens. ⚠ `minmax(0, 1fr)` rather than
   `1fr`, because a grid track refuses to shrink below its content by default and a
   long definition string would otherwise blow the card wider than the screen. */
.kpis { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
@media (min-width: 560px) { .kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 820px) { .kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.kpi {
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--surface-2);
  padding: 10px 11px;
  min-height: 76px;
  display: flex; flex-direction: column; justify-content: space-between; gap: 6px;
  transition: border-color .15s, transform .06s;
}
.kpi:not(:disabled):hover { border-color: var(--accent); }
.kpi:not(:disabled):active { transform: scale(.985); }
.kpi:disabled { opacity: .55; cursor: not-allowed; }
.kpi-label { font-size: 11.5px; font-weight: 650; color: var(--muted); letter-spacing: .02em; }
.kpi-value { font-size: 20px; font-weight: 700; letter-spacing: -.02em; }
.kpi-value.neg { color: var(--neg); }
.kpi-value.pos { color: var(--pos); }
.kpi-value.none { color: var(--muted); font-size: 14px; font-weight: 550; }
.kpi-note { font-size: 10.5px; color: var(--muted); }

/* ─────────────────────── statement rows ─────────────────────── */
.stmt { width: 100%; border-collapse: collapse; }
.stmt td { padding: 9px 0; border-bottom: 1px solid var(--border); vertical-align: baseline; }
.stmt tr:last-child td { border-bottom: 0; }
.stmt .cap { text-align: left; padding-inline-end: 10px; }
.stmt .amt { text-align: right; white-space: nowrap; font-weight: 600; }
/* ⚠ THE GUTTER IS THE FIX, AND ITS ABSENCE WAS A VISIBLE BUG. The three cells are
   `cap | pct | amt`, all adjacent, and with `text-align: right` on both figures the
   percentage ran straight into the amount — MEASURED in the 390px screenshot as
   "100.0%1,000.00", which reads as one malformed number rather than two columns. A
   padding on the percentage cell separates them without a wrapper element. */
.stmt .pct {
  text-align: right;
  white-space: nowrap;
  width: 62px;
  padding-inline-end: 12px;
  color: var(--muted);
  font-size: 12.5px;
}
.stmt .amt { min-width: 84px; }

/* Row emphasis comes from the ladder's OWN `kind`, so the visual hierarchy is the
   accounting hierarchy rather than a decoration applied on top of it. */
.stmt tr[data-kind="section"] td { padding-top: 15px; font-weight: 700; font-size: 13px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); border-bottom-color: transparent; }
.stmt tr[data-kind="subtotal"] td { font-weight: 750; border-top: 1px solid var(--border); border-bottom-color: transparent; }
.stmt tr[data-kind="total"] td { font-weight: 800; font-size: 16.5px; background: var(--accent-soft); }
.stmt tr[data-kind="total"] td:first-child { border-radius: 8px 0 0 8px; padding-inline-start: 8px; }
.stmt tr[data-kind="total"] td:last-child { border-radius: 0 8px 8px 0; padding-inline-end: 8px; }
.stmt tr.drillable { cursor: pointer; }
.stmt tr.drillable .cap::after {
  content: "›"; margin-inline-start: 6px; color: var(--accent); font-weight: 700;
}
.stmt tr.drillable:active { background: var(--surface-2); }

/* ─────────────────────── chart ─────────────────────── */
.chart { width: 100%; height: 96px; display: block; overflow: visible; }
.chart rect { transition: opacity .15s; }
.chart rect:hover { opacity: .75; }

/* ⚠ I TRIED SPANNING THE LAST CARD AND IT WAS WORSE, SO IT IS REVERTED DELIBERATELY.
   A 9-card grid in 2 columns leaves one card on its own row. Making that card span both
   columns seemed tidy and instead produced a WIDER card than every other one — MEASURED
   in the screenshot, it reads as an inconsistency rather than a fix. A half-width card in
   a half-filled row is the ordinary appearance of an odd count, so the grid is left alone
   and the odd count is simply visible. Not every asymmetry is a defect. */

/* Single-category revenue: a labelled share row, not a one-bar chart. */
.share { display: flex; flex-direction: column; gap: 8px; }
.share-bar { height: 10px; border-radius: 999px; background: var(--surface-2); overflow: hidden; display: flex; }
.share-bar span { display: block; background: var(--accent); height: 100%; }
.share-row { display: flex; justify-content: space-between; gap: 10px; font-size: 13px; font-weight: 600; }

/* ─────────────────────── journal ─────────────────────── */
.entry {
  width: 100%; text-align: left;
  display: flex; align-items: center; gap: 10px;
  padding: 11px 0;
  border: 0; border-bottom: 1px solid var(--border);
  background: none; color: inherit;
}
.entry:last-child { border-bottom: 0; }
.entry-main { min-width: 0; flex: 1; }
.entry-no { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.entry-desc { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.entry-amt { font-weight: 650; white-space: nowrap; }
.pill {
  display: inline-block; font-size: 10.5px; font-weight: 700; padding: 2px 7px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); text-transform: uppercase; letter-spacing: .04em;
}
.pill.ok { background: var(--accent-soft); color: var(--accent); }
.pill.bad { background: var(--danger-soft); color: var(--danger); }
.pill.warn { background: #fef3c7; color: #92400e; }

.big-status { display: flex; align-items: center; gap: 12px; }
.big-status .dot { width: 12px; height: 12px; border-radius: 50%; flex: none; }
.big-status .dot.ok { background: var(--pos); box-shadow: 0 0 0 4px var(--accent-soft); }
.big-status .dot.bad { background: var(--danger); box-shadow: 0 0 0 4px var(--danger-soft); }
/* ⚠ `warn` IS A THIRD STATE, NOT A SOFTER `bad`. A configuration fault (the wrong
   LEDGER_CHAIN_KEY) means the ledger is INTACT and the operator has a variable to fix; painting
   it the same red as tampering would tell them their data is suspect when it is not, and a
   control that cries wolf is one people learn to ignore. `--neg` is the amber already used for
   attention states elsewhere in this sheet. */
.big-status .dot.warn { background: var(--neg); box-shadow: 0 0 0 4px #fef3c7; }
.kv { display: flex; justify-content: space-between; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.kv:last-child { border-bottom: 0; }
.kv .k { color: var(--muted); }
.kv .v { font-family: var(--mono); font-size: 11.5px; overflow-wrap: anywhere; text-align: right; }

/* ⚠ THE PERIOD CONTROL READS AS TEXT AND BEHAVES AS A SELECT. It replaces a static
   `<span>` in the header, so it must not look like a form control bolted into the chrome —
   but it must LOOK interactive, or it is the same undiscoverable label it just stopped
   being. Hence: no box, a chevron, and a real tap target. The tap target is 39px rather
   than 44px because it sits in the header next to the property name with no room to grow;
   it is the one control in the app below the target, and that is a deliberate trade
   recorded here rather than an oversight. */
.period-select {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  line-height: 1.4;
  padding: 0 18px 0 0;
  margin: 0;
  min-height: 34px;
  cursor: pointer;
  /* the chevron, drawn as a background image so no icon font is needed */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 8px center, right 3px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  border-radius: 6px;
}
.period-select:hover, .period-select:focus-visible { color: var(--text); background-color: var(--surface-2); }
.period-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* A CLOSED PERIOD IS FLAGGED IN THE CONTROL ITSELF, because the form will refuse a posting
   into it and the state is known before the user writes anything. */
.period-select.closed { color: var(--neg); font-weight: 650; }

/* ─────────────────────── the entry form ─────────────────────── */

.entry-form { display: flex; flex-direction: column; gap: 12px; }
/* ⚠ THE ENTRY FORM'S TOP ROW IS PERIOD · DATE · DESCRIPTION, AND THE GRID HAS TO SAY SO.
   This held `Date` and `Description` and was a two-column `140px 1fr`, so adding the entry's own
   Period field made the THIRD child start a new row at 140px wide — Description rendered in the
   narrow label column and Period and Date stacked. A grid whose column count does not match its
   children does not fail loudly; it just lays them out wrongly, which is why the count is stated
   here next to the markup that uses it. Mobile stays single-column (the form is the phone case). */
.grid2 { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 560px) { .grid2 { grid-template-columns: 140px 160px 1fr; } }

.entry-form label { font-size: 12.5px; }
select {
  min-height: var(--tap);
  padding: 10px 8px;
  border: 1px solid var(--control-border);
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  /* 16px for the same iOS reason as inputs — smaller triggers auto-zoom. */
  font-size: 16px;
  max-width: 100%;
}
select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ⚠ THE LINE GRID COLLAPSES TO TWO ROWS ON A PHONE RATHER THAN SQUEEZING THREE
   CONTROLS INTO 390px. Account names are long ("Other Operated Departments"), and a
   select narrow enough to fit three-across truncates every option — so at mobile width
   the account takes its own row and side+amount share the next. */
.lines-head { display: none; }
/* ⚠ THE AMOUNT COLUMN NEEDS AN EXPLICIT WIDTH, AND ITS ABSENCE WAS A MEASURED BUG.
   The first version used `grid-template-columns: 1fr auto`, so the amount sat in the
   `auto` track, which sizes to MIN-CONTENT — measured at **w=32px**, i.e. an amount field
   you cannot type "1234.56" into. It was in the DOM with a real value and still read as
   empty space in the screenshot. The two-column template is now explicit. */
.line {
  display: grid;
  /* ⚠ `minmax(0, 1fr)` RATHER THAN `1fr`, for the same reason as `.kpis`: a bare `1fr`
     track is floored at its content's min-content width, so an over-long `<select>` could
     still widen the whole card despite the per-child `min-width: 0` above. Belt and braces
     on the one defect that made the form unusable on an actual phone. */
  /* ⚠ FIVE CONTROLS IN THREE ROWS ON A PHONE, NOT THREE-ACROSS. The memo gets its own
     full-width row: it is free text and the thing most likely to be long ("Guest 4412 —
     cash paid at desk"), so squeezing it into a narrow column would make it unusable exactly
     when it carries the most information. */
  grid-template-columns: minmax(0, 1fr) 96px;
  grid-template-areas: "acct remove" "side amount" "memo memo";
  gap: 8px;
  align-items: center;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--surface-2);
  margin-bottom: 8px;
}
/* ⚠ CONTROLS INSIDE A LINE NEED THEIR OWN SURFACE, AND THIS WAS A REAL BUG.
   `.line` uses `--surface-2` and so does `input`/`select` by default, so on the line card
   every control was the SAME COLOUR as the card — the amount box rendered as empty space
   and, MEASURED in the 390px screenshot, the form looked like it had no amount fields at
   all even though both were in the DOM. A text field must not be camouflaged against its
   own background; that is the one affordance it has. */
.line > select,
.line > input {
  background: var(--surface);
  border-color: var(--control-border);
}
/* ⚠ `min-width: 0` ON THE SELECT IS THE FIX, AND ITS ABSENCE WAS THE WHOLE BUG.
   A `<select>`'s intrinsic width is driven by its LONGEST OPTION — "4100 · Package Revenue
   — All-Inclusive" — and a grid item defaults to `min-width: auto`, so it REFUSES to
   shrink below that. MEASURED: the first column resolved to **577px inside a 390px
   viewport**, which pushed the amount input to x=623 and the remove button with it, both
   entirely off-screen. That is why the user's phone screenshot showed account + Dr/Cr
   dropdowns and NO amount field: the field existed, was visible, had a 3:1 border, and was
   simply positioned 233px past the right edge of the screen.
   ⚠ AND IT IS WHY MY PIXEL TEST PASSED WHILE THE SCREENSHOT FAILED. The contrast check
   counted border-coloured pixels anywhere in the render — the dropdowns supplied plenty.
   It proved *a* control edge existed, not that the field under test was on screen. A test
   that does not assert the SUBJECT's position cannot detect this class of defect. */
.line > .pick,
.line > select[name=side],
.line > input[name=amount],
.line > button {
  min-width: 0;
}
.line > input[name=memo] { grid-area: memo; font-size: 14px; }
.line > input[name=memo]::placeholder { font-size: 13px; }
.line > .pick { grid-area: acct; }
.line .pick > select,
.line .pick > input { background: var(--surface); border-color: var(--control-border); }
.line > select[name=side] { grid-area: side; }
.line > input[name=amount] { grid-area: amount; text-align: right; min-width: 0; }
.line > button { grid-area: remove; }

@media (min-width: 560px) {
  .lines-head {
    display: grid; grid-template-columns: 1fr 74px 130px 32px; gap: 8px;
    font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
    color: var(--muted); font-weight: 700; padding-inline-start: 10px;
  }
  .line {
    grid-template-columns: minmax(0, 1fr) 96px 62px 130px 32px;
    grid-template-areas: "acct memo side amount remove";
  }
  .lines-head { grid-template-columns: minmax(0, 1fr) 96px 62px 130px 32px; }
}

button.tiny {
  width: 32px; height: 32px; min-height: 32px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--muted);
  font-size: 17px; line-height: 1; display: grid; place-items: center;
}

.form-foot { display: flex; align-items: center; }
.grow { flex: 1; }

/* The live balance. ⚠ Colour ALONE is not the signal — the text says "balanced" or
   "out by N", because a red/green cue is invisible to some users and unreliable in
   bright sunlight, which is where a property manager is using this. */
.balance {
  display: flex; justify-content: space-between; gap: 8px; align-items: baseline;
  padding: 10px 12px; border-radius: 11px;
  background: var(--surface-2); border: 1px solid var(--border);
  font-variant-numeric: tabular-nums; font-size: 13.5px;
}
.balance strong { font-weight: 700; }
.balance.ok { border-color: var(--accent); background: var(--accent-soft); color: var(--text); }
.balance.off { border-color: var(--neg); }
.balance.off strong { color: var(--neg); }

/* The date's period readout. Sits under the date field, quiet when the period is open and
   loud when it is not, because a closed period is the one condition that will refuse the
   post and the user should see it before writing the entry. */
.period-note {
  margin: 2px 0 0; font-size: 11.5px; color: var(--muted);
  font-variant-numeric: tabular-nums; line-height: 1.35;
}
.period-note.ok { color: var(--accent); }
.period-note.bad { color: var(--neg); font-weight: 600; }

/* The entry's own lines, inside the detail sheet. */
.sheet-lines { margin-top: 12px; }
.lines-table { font-size: 13px; }
.lines-table td { padding: 8px 0; vertical-align: top; }
.lines-table .side { color: var(--muted); font-weight: 650; width: 34px;
  text-align: center; padding-inline: 8px; white-space: nowrap; }
.lines-table .amt { text-align: right; white-space: nowrap; font-weight: 600; }
.lines-table th { white-space: nowrap; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
  font-weight: 700; text-align: start; padding: 6px 0; border-bottom: 1px solid var(--border); }
.lines-table th.amt { text-align: right; }
.lines-table a { color: inherit; text-decoration: underline; text-decoration-color: var(--border); text-underline-offset: 3px; }
.sheet-sub { margin: 16px 0 2px; font-size: 14px; }

/* A one-line explanation under a field's label (the entry sheet, the ledger report). */
.kv .k .kv-help { display: block; font-size: 11.5px; line-height: 1.35; margin-top: 2px; max-width: 36ch; opacity: .85; }

/* A <select> with a search box above it (kit picker, over 5 options). Both fill the cell;
   `min-width: 0` for the same long-option reason as `.line > select` below. */
.pick { display: grid; gap: 6px; min-width: 0; }
.pick > input, .pick > select { width: 100%; min-width: 0; }

/* ⚠ THE MEMO IS A SECOND LINE UNDER THE ACCOUNT, NOT A COLUMN. "Guest 4412 — cash paid at
   desk" is the normal shape, and a column narrow enough to sit beside the amount would wrap
   it to four lines on a phone. It is also visually subordinate — the account is what the
   line IS, the memo is why. */
.line-memo {
  display: block; font-size: 11.5px; color: var(--muted); font-weight: 400;
  margin-top: 2px; line-height: 1.35;
}

.reverse-box {
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 8px; align-items: stretch;
}
.reverse-box strong { font-size: 13px; }

/* ─────────────────────── sheet ─────────────────────── */
.sheet { position: fixed; inset: 0; z-index: 50; display: flex; align-items: flex-end; }
.sheet-backdrop { position: absolute; inset: 0; background: rgba(8, 16, 16, .45); backdrop-filter: blur(2px); }
.sheet-body {
  position: relative;
  width: 100%;
  max-height: 82dvh;
  overflow-y: auto;
  background: var(--surface);
  border-radius: var(--radius) var(--radius) 0 0;
  padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
  box-shadow: 0 -8px 40px rgba(0, 0, 0, .22);
  animation: sheet-up .22s cubic-bezier(.22, 1, .36, 1);
}
@media (min-width: 720px) {
  .sheet { align-items: center; justify-content: center; }
  .sheet-body { max-width: 560px; border-radius: var(--radius); }
}
@keyframes sheet-up { from { transform: translateY(14px); opacity: .6 } to { transform: none; opacity: 1 } }
.sheet-close {
  position: absolute; top: 10px; inset-inline-end: 10px;
  width: var(--tap); height: var(--tap); border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--muted);
  display: grid; place-items: center;
}
.sheet h3 { margin: 0 0 2px; margin-inline-end: 52px; font-size: 17px; letter-spacing: -.01em; }
/* the sheet's heading takes programmatic focus when the sheet opens (so a screen reader starts there); it is not a control, so no focus ring (light and dark) */
.sheet h3[tabindex="-1"]:focus, .sheet h3[tabindex="-1"]:focus-visible { outline: none; }
.sheet .lead { margin: 0 0 12px; font-size: 13px; color: var(--muted); }
.sheet .basis { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); font-size: 12px; color: var(--muted); }

/* ─────────────────────── skeleton / states ─────────────────────── */
.skel { background: linear-gradient(90deg, var(--surface-2), var(--border), var(--surface-2)); background-size: 200% 100%; animation: sh 1.1s infinite; border-radius: 8px; height: 76px; }
@keyframes sh { from { background-position: 200% 0 } to { background-position: -200% 0 } }
.empty { text-align: center; padding: 28px 14px; color: var(--muted); font-size: 13.5px; }

/* ⚠ THE BYPASS BANNER IS UNMISSABLE AND NOT DISMISSIBLE. A prototype instance with
   password verification off looks identical to a secured one, which is precisely how it
   gets screenshotted or left running. Full-width, high-contrast, above everything. */
.bypass-banner {
  position: sticky; top: 0; z-index: 60;
  /* the header and tabs read this to sit BELOW the banner rather than under it */
  isolation: isolate;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  padding: 9px 14px;
  background: #fef3c7; color: #7c2d12;
  border-bottom: 2px solid #f59e0b;
  font-size: 12.5px;
}
.bypass-banner strong { font-weight: 750; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bypass-banner { background: #4a2c07; color: #fde68a; border-bottom-color: #b45309; }
}

/* D24/D3: unread owner alerts. Red and not dismissible: it goes when the alerts are marked read. */
.alert-banner {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  padding: 9px 14px; margin-bottom: 12px;
  background: #fee2e2; color: #7f1d1d;
  border: 2px solid #dc2626; border-radius: 8px;
  font-size: 13.5px;
}
.alert-banner strong { font-weight: 750; }
.alert-banner a { color: inherit; font-weight: 650; text-decoration: underline; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .alert-banner { background: #4c1010; color: #fecaca; border-color: #ef4444; }
}
:root[data-theme="dark"] .alert-banner { background: #4c1010; color: #fecaca; border-color: #ef4444; }

.toast {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
  z-index: 80; max-width: min(92vw, 460px);
  background: var(--text); color: var(--bg);
  padding: 11px 15px; border-radius: 999px;
  font-size: 13.5px; box-shadow: var(--shadow);
}

/* ⚠ RTL IS A FIRST-CLASS MODE, NOT A MIRROR HACK. `dir="rtl"` on the root flips the
   inline axis, so `text-align: right` on an amount still means "right" — which in RTL
   is the START side. Rather than patch each rule, the few places that hardcode a side
   are written with logical properties (`padding-inline`, `inset-inline`) above, so the
   Arabic statement lays out correctly with no second stylesheet. */
[dir="rtl"] .stmt .cap { text-align: right; }
[dir="rtl"] .stmt tr.drillable .cap::after { content: "‹"; }

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

/* ═══════════════════════════════════════════════════════════════════════════
   FOUNDATION — app shell (grouped nav, context controls, account menu) and the UI kit
   (ui/core/kit/). Screens use these classes; a screen group that needs more CSS adds
   ui/screens/<group>/<group>.css (docs/UI.md "Adding a screen") rather than editing here.
   ═══════════════════════════════════════════════════════════════════════════ */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
:root { --nav-w: 248px; --info: #1d4ed8; --info-soft: #dbeafe; --warn-fg: #92400e; --warn-bg: #fef3c7; }
:root[data-theme="dark"] { --info: #93c5fd; --info-soft: #1e3a5f; --warn-fg: #fde68a; --warn-bg: #4a2c07; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --info: #93c5fd; --info-soft: #1e3a5f; --warn-fg: #fde68a; --warn-bg: #4a2c07; }
}

.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.grow { flex: 1; }
.boot-error { margin: 12px; }

/* ── login additions ── */
.login-card form, .login-card section { display: flex; flex-direction: column; gap: 14px; }
.login-card h2 { margin: 0; font-size: 16px; }
.login-card p { margin: 0; }
.note { margin: 0; padding: 10px 12px; border-radius: 10px; background: var(--info-soft); color: var(--info); font-size: 13.5px; }
#cc-field.attention input { border-color: var(--neg); outline: 2px solid var(--neg); }
.qr { width: min(240px, 70vw); aspect-ratio: 1; margin: 0 auto; background: #fff; border-radius: 10px; padding: 6px; }
.qr svg { width: 100%; height: 100%; display: block; }
details.secret summary { cursor: pointer; font-size: 13px; color: var(--muted); }
details.secret code { display: block; margin-top: 8px; font-size: 14px; letter-spacing: .06em; overflow-wrap: anywhere; }
ol.codes { margin: 0; padding-inline-start: 26px; display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 6px 14px; }
ol.codes code { white-space: nowrap; }
ol.codes code { font-size: 13.5px; }
label.check { flex-direction: row; align-items: center; gap: 10px; font-weight: 500; }
label.check input { min-height: 0; width: 20px; height: 20px; }

/* ── top bar ── */
.icon-btn { display: inline-grid; place-items: center; min-width: 36px; padding: 0 8px; }
.topbar { flex-wrap: wrap; row-gap: 6px; }
.brand-short { font-weight: 750; font-size: 14.5px; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar-ctx { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
.topbar-ctx strong { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
@media (max-width: 719px) {
  .topbar-ctx { order: 3; flex-basis: 100%; justify-content: space-between; }
  .topbar-left { flex: 1; }
}
.ctx-select {
  max-width: 46vw; min-height: 30px; padding: 2px 24px 2px 6px; margin: 0; font-size: 14px; font-weight: 700;
  border: 1px solid transparent; background-color: transparent; color: var(--text);
}
.ctx-select:hover, .ctx-select:focus-visible { border-color: var(--control-border); }
.topbar-ctx .period-select { flex: none; }
.topbar-ctx .ctx-select { max-width: none; flex: 0 1 auto; min-width: 0; }
.menu {
  position: absolute; top: calc(100% + 4px); inset-inline-end: 10px; z-index: 40;
  min-width: 240px; max-width: calc(100vw - 20px);
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow);
  padding: 8px; display: flex; flex-direction: column; gap: 2px;
}
.menu-who { display: flex; flex-direction: column; padding: 6px 8px 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px; font-size: 13px; overflow-wrap: anywhere; }
.menu-sec { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 8px; font-size: 13px; }
.menu-label { color: var(--muted); }
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; }
.menu-opt { border: 0; background: transparent; color: var(--text); padding: 6px 10px; font-size: 12.5px; min-height: 32px; }
.menu-opt[aria-checked="true"] { background: var(--accent); color: #fff; }
.menu-item { display: block; text-align: start; border: 0; background: transparent; color: var(--text); padding: 10px 8px; border-radius: 8px; font-size: 14px; text-decoration: none; min-height: var(--tap); }
.menu-item:hover { background: var(--surface-2); }

/* ── layout: drawer below 960px, sidebar above ── */
.layout { display: block; }
.main-col { min-width: 0; }
.nav {
  position: fixed; top: 0; bottom: 0; inset-inline-start: 0; z-index: 45;
  width: min(var(--nav-w), 84vw); overflow-y: auto;
  background: var(--surface); border-inline-end: 1px solid var(--border);
  padding: calc(14px + var(--bypass-h, 0px)) 10px 24px;
  transform: translateX(-105%); transition: transform .2s ease;
}
[dir="rtl"] .nav { transform: translateX(105%); }
body.nav-open .nav { transform: none; }
.nav-scrim { position: fixed; inset: 0; z-index: 44; background: rgba(8, 16, 16, .4); }
.nav-group { margin-bottom: 14px; }
.nav-group-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); padding: 4px 10px; }
.nav a { display: flex; align-items: center; min-height: 40px; padding: 0 10px; border-radius: 9px; color: var(--text); text-decoration: none; font-size: 14px; }
.nav a:hover { background: var(--surface-2); }
.nav a.active { background: var(--accent-soft); color: var(--accent); font-weight: 650; }
@media (min-width: 960px) {
  #btn-nav { display: none; }
  .layout { display: grid; grid-template-columns: var(--nav-w) minmax(0, 1fr); }
  .nav {
    position: sticky; top: calc(var(--bypass-h, 0px) + var(--topbar-h, 57px)); height: calc(100dvh - var(--topbar-h, 57px) - var(--bypass-h, 0px));
    transform: none; z-index: 1; width: auto; padding-top: 14px; background: transparent;
  }
  [dir="rtl"] .nav { transform: none; }
  .nav-scrim { display: none; }
}

/* ── page scaffolding ── */
.page-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin: 2px 2px 12px; }
.page-head h1 { margin: 0; font-size: 20px; letter-spacing: -.01em; }
.page-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.card-head-end { display: inline-flex; align-items: center; gap: 8px; }
.kv .v.plain { font-family: inherit; font-size: 13px; }

/* ── buttons ── */
button.danger { min-height: var(--tap); border: 0; border-radius: 10px; background: var(--danger); color: #fff; font-weight: 650; padding: 0 14px; }
button:disabled { opacity: .55; cursor: not-allowed; }

/* ── status pills (kit statusBadge) ── */
.pill.info { background: var(--info-soft); color: var(--info); }
.pill.muted { background: var(--surface-2); color: var(--muted); }
.pill.warn { background: var(--warn-bg); color: var(--warn-fg); }

/* ── data table (kit dataTable) ── */
.dt-tools { display: flex; gap: 8px; margin-bottom: 8px; }
.dt-filter { flex: 1; min-width: 0; }
.dt-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.dt-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.dt-table th, .dt-table td { padding: 9px 8px; border-bottom: 1px solid var(--border); text-align: start; vertical-align: baseline; }
.dt-table th { font-size: 11.5px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; white-space: nowrap; }
.dt-table .al-end { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.dt-sort { all: unset; cursor: pointer; display: inline-flex; gap: 4px; align-items: center; }
.dt-sort:focus-visible { outline: 2px solid var(--accent); border-radius: 4px; }
.dt-arrow { font-size: 9px; opacity: .6; }
.dt-table tr.clickable { cursor: pointer; }
.dt-table tr.clickable:hover td { background: var(--surface-2); }
.dt-foot { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; padding-top: 8px; font-size: 12.5px; }
.dt-pager { display: inline-flex; align-items: center; gap: 8px; }

/* ── forms (kit form) ── */
.kit-form { display: flex; flex-direction: column; gap: 12px; }
.field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.field > label > span:first-child { font-size: 12.5px; }
.req { color: var(--danger); }
.field-error { color: var(--danger); font-size: 12.5px; }
[aria-invalid="true"] { border-color: var(--danger) !important; }
textarea { min-height: 88px; padding: 10px 12px; border: 1px solid var(--control-border); border-radius: 10px; background: var(--surface-2); color: var(--text); font: inherit; font-size: 16px; }
.form-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.form-actions button { flex: none; padding: 0 16px; }
.money-input { text-align: end; font-variant-numeric: tabular-nums; }
.date-range { display: flex; gap: 10px; flex-wrap: wrap; }
.date-range label { flex: 1; min-width: 140px; }
.file-upload { display: flex; flex-direction: column; gap: 6px; }
.file-preview { margin: 0; padding: 8px 10px; background: var(--surface-2); border-radius: 8px; font-family: var(--mono); font-size: 12px; overflow-x: auto; max-height: 140px; }
.dialog .sheet-body h3 { margin-inline-end: 40px; }
.dialog-body { margin: 8px 0 14px; font-size: 14px; }

/* ── toasts carry a tone ── */
.toast[data-tone="ok"] { background: var(--pos); color: #fff; }
.toast[data-tone="warn"] { background: var(--warn-bg); color: var(--warn-fg); }
.toast[data-tone="bad"] { background: var(--danger); color: #fff; }

/* ── print view (kit printView) ── */
#print-root { display: none; }
@media print {
  body.print-mode > *:not(#print-root) { display: none !important; }
  body.print-mode #print-root { display: block; color: #000; background: #fff; }
  .print-head { display: flex; align-items: center; gap: 14px; border-bottom: 1px solid #999; padding-bottom: 8px; margin-bottom: 14px; }
  .print-head h1 { margin: 0; font-size: 18px; }
  .print-logo { height: 36px; }
  .print-when { margin-inline-start: auto; font-size: 11px; }
  #print-root .card { box-shadow: none; border: 0; }
}

/* Journal → New → "Read from document" (ui/screens/dashboard/journal-reader.js). Phone-first: the button is full width. */
.jr-reader { margin: 0 0 16px; padding: 12px; border: 1px dashed var(--border); border-radius: 10px; }
.jr-btn { width: 100%; min-height: 48px; font-weight: 650; }
@media (min-width: 640px) { .jr-btn { width: auto; min-width: 220px; } }
.jr-note { margin: 8px 0 0; }
.jr-reader [role="status"] { margin: 10px 0 0; }
.jr-banner { border-radius: 10px; padding: 12px 14px; margin: 0 0 12px; font-size: 14px; line-height: 1.45; }
.jr-banner.bad { background: var(--danger-soft); color: var(--danger); border: 1px solid var(--danger); }
.jr-banner.warn { background: var(--warn-bg); color: var(--warn-fg); }
.jr-banner.ok { background: var(--accent-soft); }
.jr-banner strong { display: block; margin-bottom: 4px; }
.jr-compare { display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; margin: 0 0 10px; font-size: 14px; }
.jr-compare dt { color: var(--muted); }
.jr-compare dd { margin: 0; text-align: end; }
.jr-flags { margin: 0 0 8px; padding-inline-start: 18px; font-size: 14px; line-height: 1.5; }
.jr-flag-error { color: var(--danger); font-weight: 600; }
.jr-flag-warn { color: var(--warn-fg); }
.jr-flag-info { color: var(--muted); }
.jr-flagged-error { outline: 2px solid var(--danger); outline-offset: 1px; }
.jr-flagged-warn { outline: 2px solid var(--warn-fg); outline-offset: 1px; }

/* ── embedded mode (?embed=1, ui/core/embed.js) ──────────────────────────────────────────────
   The host application owns the brand, the property and period selectors, the account menu and
   sign-out. Hide ours; keep the screen navigation as a compact strip so every screen stays reachable. */
[data-embed] .topbar,
[data-embed] #btn-nav,
[data-embed] .nav-scrim { display: none !important; }
[data-embed] .layout { display: block; }
[data-embed] .nav {
  position: static; transform: none; width: auto; height: auto; padding: 6px 10px; background: transparent;
  display: flex; flex-wrap: wrap; gap: 4px 14px; border: 0; overflow: visible;
}
[data-embed] .nav-group { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; margin: 0; }
[data-embed] .nav-group-label { padding: 0 4px; }
[data-embed] .nav a { min-height: 32px; font-size: 13px; }
[data-embed] body { min-height: 0; }
