:root {
  --bg: #F3F1ED;
  --panel: #ffffff;
  --panel-sunk: #FAF9F6;
  --surface-work: #F7F6F2;
  --surface-log: #FBFAF7;
  --ink: #182830;
  --ink-soft: #3C4C52;
  --muted: #7A8C90;
  --muted-strong: #5E7075;
  --line: #D3CEC6;
  --line-soft: #E7E3DC;
  --accent: #176B5F;
  --accent-dark: #0F4F46;
  --accent-bright: #2A8A7B;
  --accent-tint: #E6F0EE;
  --side-bg: #1B3039;
  --side-active: rgba(255,255,255,0.13);
  --side-text: #ffffff;
  --side-text-2: #C5DEDD;
  --side-text-3: #8CAEAA;
  --danger: #B42318;
  --danger-tint: #FBEAE8;
  --warn: #B76E00;
  --warn-tint: #FBF1E2;
  --warn-line: #EBD9B4;
  --ok: #0F4F46;
  --ok-tint: #E6F0EE;
  --mark-amber: #C97A08;
  --mark-amber-bright: #F0A500;
  --fs-display: 20px;
  --fs-h2: 16px;
  --fs-h3: 15px;
  --fs-body: 15px;
  --fs-sm: 13px;
  --fs-xs: 11px;
  --lh-tight: 1.25;
  --lh-body: 1.55;
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --r-xs: 4px;
  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 10px;
  --shadow-card: 0 1px 2px rgba(24,40,48,.05), 0 2px 6px rgba(24,40,48,.05);
  --elev-1: 0 1px 2px rgba(24,40,48,.05), 0 2px 6px rgba(24,40,48,.05);
  --elev-2: 0 2px 4px rgba(24,40,48,.06), 0 8px 20px rgba(24,40,48,.10);
  --elev-3: 0 12px 40px rgba(16,32,40,.18);
  --inset-1: inset 0 1px 2px rgba(24,40,48,.05);
  --shadow-pop: 0 12px 40px rgba(16,32,40,0.18);
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --dur-fast: 120ms;
  --dur: 180ms;
  --sidebar-w: 216px;
  --rail-w: 60px;
  --leaflet-w: 300px;
  --leaflet-rail-w: 44px;
  --pane-gap: 16px;
  --work-min: 300px;
  --work-max: 460px;
  --log-min: 340px;
  --content-max: 880px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

button,
input,
select,
textarea {
  font: inherit;
}

button {
  cursor: pointer;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

a {
  color: var(--accent);
}

:focus-visible {
  outline: 2px solid var(--accent-dark);
  outline-offset: 2px;
}

.boot-spinner {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  color: var(--muted-strong);
}

body:not(.booting) .boot-spinner,
body.booting .app-shell {
  display: none;
}

.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 999px;
  animation: spin 800ms linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: 100vh;
  height: 100vh;
  overflow: hidden;
}

.sidebar {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  height: 100vh;
  position: sticky;
  top: 0;
  overflow-y: auto;
  padding: var(--sp-5) var(--sp-4);
  background: var(--side-bg);
  color: var(--side-text);
  /* Depth cue, transcribed from the mockup (contract F1 -- "things sit on top
     of things"). Missing from the first build; found by Checkman. The sidebar
     casts a shadow onto the content area, which is what stops the three
     columns reading as one flat plane. (position:sticky is already set above
     and creates the stacking context this z-index needs.) */
  z-index: 3;
  box-shadow: 2px 0 6px rgba(16, 26, 32, 0.14);
  border-right: 1px solid #12242C;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 4px;
  margin-bottom: 22px;
}

.brand-word {
  font-size: 18px;
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
}

.brand-ver {
  margin-left: auto;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--side-text-3);
}

.nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 40px;
  padding: 9px 12px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--side-text-2);
  font-size: 14px;
  font-weight: var(--fw-medium);
  text-align: left;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.nav-button .ic {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
}

.nav-button:hover {
  background: rgba(255,255,255,.08);
  color: var(--side-text);
}

.nav-button.active {
  background: var(--side-active);
  color: var(--side-text);
  font-weight: var(--fw-semibold);
  box-shadow: inset 3px 0 0 var(--accent-bright);
}

.nav-button:focus-visible {
  outline-color: #9FD6CB;
}

.sidebar-footer {
  margin-top: auto;
  padding-top: var(--sp-3);
  border-top: 1px solid rgba(255,255,255,0.10);
}

.account-button {
  min-height: 52px;
}

.avatar {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: rgba(255,255,255,0.14);
  color: var(--side-text);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.02em;
}

.account-lines {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
}

.acc-name {
  font-size: 14px;
  font-weight: var(--fw-semibold);
  color: var(--side-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.acc-mail {
  font-size: 11px;
  color: var(--side-text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Below 1200px viewport width the sidebar collapses to a 60px icon rail;
   labels become tooltips (contract A4). */
@media (max-width: 1199px) {
  .app-shell {
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
  }

  .sidebar {
    padding: var(--sp-5) 8px;
    align-items: center;
  }

  .brand-word,
  .brand-ver,
  .nav-label,
  .account-lines {
    display: none;
  }

  .brand {
    justify-content: center;
    padding: 0;
  }

  .nav-button {
    justify-content: center;
    padding: 9px;
  }

  .account-button {
    justify-content: center;
  }
}

.main {
  padding: var(--sp-6);
  min-width: 0;
  height: 100vh;
  overflow-y: auto;
}

.view {
  max-width: var(--content-max);
}

.view.hidden {
  display: none;
}

/* CRITICAL (found in GPT read-only review): the LFM module frame needs the
   full width down to the sidebar edge -- the shared .view max-width (880px,
   a readability cap for text-only pages like Modules/History/Settings) would
   otherwise crush the mockup's pane-width contract (up to ~1044px content
   width at 1560px+, and the whole point of the stage-driven pane widths in
   contract C). Scoped to this one view only; every other view keeps the
   880px cap. #view-lfm-user also needs an explicit height so
   .module-frame's height:100% has something real to resolve against --
   without it the "no page scrolling" promise in contract A2 does not hold. */
#view-lfm-user {
  max-width: none;
  height: 100%;
}

/* HIGH (found 2026-08-07 by a rendered-browser measurement and independently
   by Checkman's box-model computation): removing the 880px cap above was not
   enough on its own. The frame still sat inside .main's 32px padding, which
   the mockup's frame does not have -- the mockup runs .module-frame flush at
   1064px, hard against the sidebar. That inset cost the LOG pane 56px in every
   single state (folded Stage A rendered 300/608 against the mockup's 300/664)
   and burned ~64px of the 584px height budget (contract C4 and A3).

   Scoped with :has() so only the LFM module view goes full-bleed; every other
   view (Modules, History, Settings, Account, Developer) keeps its 32px padding
   and its 880px readability cap untouched. If :has() were ever unsupported the
   rule simply does not apply and the layout falls back to today's inset
   behaviour -- a safe degradation, not a broken one. */
.main:has(> #view-lfm-user:not(.hidden)) {
  padding: 0;
}

.view h1 {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-display);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
}

.subtitle {
  margin: 0 0 var(--sp-5);
  color: var(--muted-strong);
}

.list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--panel);
  color: inherit;
  text-align: left;
}

.row:hover {
  background: var(--panel-sunk);
}

.row-title {
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
}

.row-meta {
  color: var(--muted-strong);
  font-size: var(--fs-sm);
}

.row-action {
  flex: 0 0 auto;
  color: var(--accent-dark);
  font-weight: var(--fw-semibold);
}

.panel {
  padding: var(--sp-5);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--panel);
  box-shadow: var(--shadow-card);
}

.account-label {
  margin: 0 0 var(--sp-1);
  color: var(--muted-strong);
  font-size: var(--fs-sm);
}

.account-name {
  margin: 0;
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
}

.account-email {
  margin: var(--sp-1) 0 var(--sp-5);
  color: var(--ink-soft);
}

.empty {
  padding: var(--sp-5);
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  background: rgba(255,255,255,0.42);
  color: var(--muted-strong);
}

.primary-button,
.secondary-button,
.text-button {
  border-radius: var(--r-sm);
  font-weight: var(--fw-semibold);
}

.primary-button {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 10px 16px;
  border: 0;
  background: var(--accent);
  color: #fff;
}

.primary-button:hover {
  background: var(--accent-dark);
}

.secondary-button {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  padding: 9px 16px;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink-soft);
}

.secondary-button:hover {
  background: var(--panel-sunk);
}

.text-button {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--accent-dark);
  text-decoration: underline;
}

.status-msg {
  margin-top: var(--sp-4);
  padding: 12px 14px;
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
}

.status-msg.ok {
  background: var(--ok-tint);
  color: var(--ok);
}

.status-msg.err {
  background: var(--danger-tint);
  color: var(--danger);
}

@media (max-width: 860px) {
  .app-shell {
    grid-template-columns: 1fr;
    height: auto;
    min-height: 100vh;
    overflow: visible;
  }

  .sidebar {
    position: static;
    height: auto;
    min-height: auto;
    overflow: visible;
    padding: var(--sp-4);
  }

  .nav {
    flex-direction: row;
    flex-wrap: wrap;
  }

  .nav-button,
  .signout-button {
    width: auto;
  }

  .sidebar-footer {
    margin-top: var(--sp-4);
    padding-top: 0;
  }

  .main {
    padding: var(--sp-5);
    height: auto;
    overflow: visible;
  }

  /* K3 fix. The LFM full-bleed rule earlier in this file
     (.main:has(> #view-lfm-user:not(.hidden)) { padding: 0; }, ~line 365)
     carries higher specificity than the plain .main rule immediately above
     -- an ID selector inside :has() always outweighs a bare class -- so it
     wins at every width, including down here below 860px, where every
     other view gets its sp-5 mobile padding back. Checked against every
     other rule that touches .main or this view before picking the fix:
     the unscoped .main{padding:var(--sp-6)} default (~line 323, already
     loses to both of the above), the :has() rule itself (source above,
     kept as-is for >860px so the frame stays full-bleed), and
     #view-lfm-user{max-width:none;height:100%} (~line 347, independent of
     .main's padding, untouched). The only way to beat an ID-bearing
     selector without an arms-race is to repeat it verbatim so the two
     rules tie on specificity, then let source order (this one is later)
     decide -- so this is the same selector as the full-bleed rule, not a
     new one, restoring the mobile padding for the LFM view exactly the
     way every other view already gets it at this width. */
  .main:has(> #view-lfm-user:not(.hidden)) {
    padding: var(--sp-5);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: 1ms !important;
  }

  .spinner {
    animation: none;
  }
}

/* HIGH (found 2026-08-07 by Wilson, looking at the build): this utility sits
   here at ~line 567, but Round 4 added ~61 single-class rules BELOW it that set
   their own `display:` value. Same specificity (0,1,0), later rule wins -- so
   `class="stage-a-collapsed hidden"` resolved to display:flex and simply did
   not hide. Six elements were affected, including #lfmUserConfirmButton, which
   put "Confirm and log" on screen next to "Extract issues" at the same time --
   two money buttons visible at once, breaking the one-button-per-stage rule
   (contract D2/D4) -- and both flagged strips, which regressed the Round-2 fix
   that hides "Flagged" unless something is genuinely flagged.

   `!important` is the right tool for a single-purpose utility class: it must
   beat every component rule regardless of source order, and it only applies
   when .hidden is actually present. The two scoped variants (.view.hidden,
   .lfm-price-edit.hidden) both also declare display:none, so nothing changes
   for them. */
.hidden {
  display: none !important;
}

.back-link {
  margin-bottom: var(--sp-4);
}

.field-label {
  display: block;
  margin-bottom: var(--sp-1);
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.field-input {
  display: block;
  width: 100%;
  min-height: 120px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink);
  resize: vertical;
}

.field-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

.field-input.lfm-file-drop-hover {
  border-color: var(--accent);
  background: var(--accent-tint);
  box-shadow: inset 0 0 0 2px rgba(23,107,95,0.22);
}

.run-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--sp-3);
}

/* Left-aligned variant: keeps the flex row + spacing but starts content on the
   left. Used by the User-panel file-upload row (R11). */
.run-actions--start {
  justify-content: flex-start;
  align-items: center;
}

.spinner-sm {
  width: 14px;
  height: 14px;
  border-width: 2px;
}

.run-result {
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line-soft);
}

.result-heading {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
}

.result-well {
  padding: var(--sp-5);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--panel-sunk);
  white-space: pre-wrap;
}

.result-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  color: var(--muted-strong);
  padding: var(--sp-4) 0;
}

.ai-advisory {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  color: var(--muted-strong);
  font-size: var(--fs-sm);
}

/* =========================================================================
   UI Round 4 — the standing module frame: sidebar -> leaflet -> function page.
   Values lifted from team/business/design/2026-08-06-ui-round-4-mockup.html,
   the visual source of truth (build contract §intro).
   ========================================================================= */

/* ---------- module grid (Modules page, contract G5) ---------- */
.module-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: var(--sp-4);
  max-width: 1000px;
}

.module-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: var(--sp-4);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  background: var(--panel);
  box-shadow: var(--elev-1);
  color: inherit;
  text-align: left;
  transition: box-shadow var(--dur-fast) var(--ease);
}

button.module-card:hover,
button.module-card:focus-visible {
  box-shadow: var(--shadow-pop);
}

.module-card-ic {
  width: 28px;
  height: 28px;
  color: var(--accent);
  margin-bottom: 2px;
}

.module-card-name {
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--ink);
}

.module-card-desc {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--muted-strong);
  margin-bottom: var(--sp-2);
}

.module-card-action {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--accent-dark);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-sm);
}

.module-card-action .ic {
  width: 14px;
  height: 14px;
}

.module-card-soon {
  cursor: default;
  color: var(--muted-strong);
}

.module-card-soon .module-card-ic {
  color: var(--muted-strong);
}

.module-card-pill {
  margin-top: auto;
  align-self: flex-start;
  background: var(--panel-sunk);
  color: var(--muted-strong);
}

/* ---------- settings segmented control ---------- */
.settings-group {
  max-width: 620px;
  margin-bottom: var(--sp-4);
}

.segmented {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
}

.segmented-option {
  padding: 8px 14px;
  border: 0;
  border-right: 1px solid var(--line);
  background: #fff;
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.segmented-option:last-child {
  border-right: 0;
}

.segmented-option.active {
  background: var(--accent);
  color: #fff;
}

.segmented-option:disabled {
  cursor: not-allowed;
  color: var(--muted-strong);
  opacity: 1;
}

.settings-note {
  margin-top: var(--sp-2);
}

/* ---------- the frame: sidebar -> leaflet (300/44, never 0) -> function page ---------- */
.module-frame {
  display: grid;
  grid-template-columns: var(--leaflet-rail-w) minmax(0, 1fr);
  height: 100%;
  min-height: 0;
  position: relative;
}

.module-frame.leaflet-open {
  grid-template-columns: var(--leaflet-w) minmax(0, 1fr);
}

.module-frame.leaflet-open .leaflet-rail {
  display: none;
}

.module-frame:not(.leaflet-open) .leaflet {
  display: none;
}

/* Leaflet fold/expand animates; the pane re-proportion below deliberately does not. */
@media (prefers-reduced-motion: no-preference) {
  .module-frame {
    transition: grid-template-columns var(--dur) var(--ease);
  }
}

.leaflet-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 12px 0;
  border: 0;
  border-right: 1px solid var(--line-soft);
  background: var(--panel-sunk);
  color: var(--muted-strong);
  cursor: pointer;
  font: inherit;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.leaflet-rail:hover {
  background: var(--accent-tint);
  color: var(--accent);
}

.leaflet-rail .ic {
  width: 22px;
  height: 22px;
}

.rail-text {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.leaflet {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
  background: var(--panel-sunk);
  border-right: 1px solid var(--line-soft);
  padding: 16px 20px 20px;
  scrollbar-width: thin;
  scrollbar-color: #CFCBC3 transparent;
}

.leaflet-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: var(--sp-3);
}

.leaflet-head h3 {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  margin: 0;
}

.leaflet-lede {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin-bottom: var(--sp-4);
}

.steprail {
  display: flex;
  flex-direction: column;
  margin-bottom: var(--sp-4);
}

.step {
  display: flex;
  gap: var(--sp-3);
}

.step-mark {
  flex: 0 0 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.step-dot {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: var(--fw-semibold);
  flex: 0 0 auto;
  box-shadow: var(--elev-1);
}

.step-line {
  flex: 1;
  width: 2px;
  background: var(--line-soft);
}

.step-txt {
  padding-bottom: 14px;
}

.step-t {
  font-size: 14px;
  font-weight: var(--fw-semibold);
  color: var(--ink);
  line-height: 1.35;
}

.step-d {
  font-size: var(--fs-sm);
  color: var(--muted-strong);
  line-height: 1.45;
}

.leaflet-cost {
  display: flex;
  gap: var(--sp-2);
  align-items: flex-start;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  padding: 10px 12px;
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  margin-bottom: var(--sp-3);
  box-shadow: var(--elev-1);
}

.leaflet-cost .ic {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  color: var(--accent);
  margin-top: 2px;
}

.leaflet-adv {
  display: flex;
  gap: var(--sp-2);
  align-items: flex-start;
  font-size: 12px;
  color: var(--muted-strong);
  line-height: 1.45;
}

.leaflet-adv .ic {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  margin-top: 2px;
}

/* ---------- the function page: header row + the two panes ---------- */
.module-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
  padding: 16px 20px;
  gap: var(--sp-3);
}

.module-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
}

.module-title {
  font-size: var(--fs-display);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
  margin: 0;
}

.spacer {
  margin-left: auto;
}

/* Visually hidden but still reachable by assistive tech (contract E1) -- the
   standard clip-based technique, not display:none (which would remove it
   from the accessibility tree too). There is no utility for this in the
   sheet today. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.sel {
  height: 32px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink-soft);
  font: inherit;
  font-size: var(--fs-sm);
  padding: 0 26px 0 10px;
  appearance: none;
  box-shadow: var(--inset-1);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235E7075' stroke-width='2' stroke-linecap='round'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat: no-repeat;
  background-position: right 6px center;
  background-size: 14px;
}

/* LFM multi-log (contract E2): the log switch. A plain .sel, dressed to read
   as the pane's own title -- the log's name IS the heading, so it is set at
   heading weight/colour, not the muted 13px of an ordinary form control.
   Checked against .sel above: every other .sel rule (height, border, the
   drawn chevron, padding-right for the arrow) is inherited unchanged; only
   size/weight/colour/truncation are added here, nothing overridden. */
.sel-log {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-right: 28px;
}

/* D14/E3: the one-log state -- same 15/600 type and position as .sel-log,
   without the box, so there is no dropdown that opens to a single dead
   item. Swapping between this and .sel-log is the entire "a box grows
   around the words" transition; nothing else in the header moves. */
.logtitle {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  /* Fixed-width flex items shrink under flex-shrink's default of 1 unless
     told not to -- E6 requires the Manage-logs icon button to hold its size
     at the 340px pane floor while the log-name control beside it (which
     carries min-width:0 -- see .sel-log/.logtitle) is the one that gives. */
  flex-shrink: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted-strong);
  cursor: pointer;
  position: relative;
}

.iconbtn:hover:not(:disabled) {
  background: #fff;
  border-color: var(--line-soft);
  box-shadow: var(--elev-1);
}

.iconbtn .ic {
  width: 18px;
  height: 18px;
}

.iconbtn.danger {
  color: var(--danger);
}

.iconbtn.save {
  color: var(--accent);
}

.ghost {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink-soft);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  box-shadow: var(--elev-1);
}

.ghost .ic {
  width: 16px;
  height: 16px;
}

.resort-control {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}

/* Pane widths follow the STAGE, not the leaflet (contract C). Default =
   log-first: dropping a file / pasting, and the log has something in it. */
.panes {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  gap: var(--pane-gap);
  grid-template-columns: var(--work-min) minmax(0, 1fr);
}

/* work-first: the confirm list is up, OR the log is empty. Instant, never
   animated (contract C5) -- no transition rule on .panes, deliberately. */
.panes.work-first {
  grid-template-columns:
    minmax(var(--work-min), min(var(--work-max), calc(100% - var(--log-min) - var(--pane-gap))))
    minmax(0, 1fr);
}

.pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-1);
  overflow: hidden;
}

/* LFM multi-log (contract L1/L2): 8px 16px + a 48px floor, not the old plain
   11px 16px -- the log header now carries a 32px control (the switch/manage
   button) that needs room. Applied to BOTH panes (this rule is unscoped) so
   their bodies stay level -- if only the log header changed, the two pane
   bodies would start 7px apart. Net cost across the whole round: 1.2px
   (46.8px -> 48px), the entire height budget spent (§L in the build contract). */
.pane-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 8px 16px;
  min-height: 48px;
  background: #fff;
  border-bottom: 1px solid var(--line-soft);
}

.pane-head h2 {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  margin: 0;
}

.pane-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 14px 16px;
  scrollbar-width: thin;
  scrollbar-color: #CFCBC3 transparent;
}

.pane-work .pane-body {
  background: var(--surface-work);
}

.pane-log .pane-body {
  background: var(--surface-log);
}

.pane-foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 10px 16px;
  border-top: 1px solid var(--line-soft);
  background: var(--panel-sunk);
}

.flabel {
  display: block;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink-soft);
  margin-bottom: 5px;
}

/* Inputs are PRESSED IN (contract F1/F5 -- depth via tone/edge/shadow only). */
.tarea-input {
  width: 100%;
  min-height: 150px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-body);
  background: #fff;
  box-shadow: var(--inset-1);
  resize: vertical;
}

.tarea-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint), var(--inset-1);
}

.tarea-input.lfm-file-drop-hover {
  border-color: var(--accent);
  background: var(--accent-tint);
  box-shadow: inset 0 0 0 2px rgba(23,107,95,0.22);
}

.uprow {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
}

.pbtn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 38px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font: inherit;
  font-size: 14px;
  font-weight: var(--fw-semibold);
  cursor: pointer;
  box-shadow: var(--elev-1);
}

.pbtn:hover:not(:disabled) {
  background: var(--accent-dark);
}

.pbtn .ic {
  width: 17px;
  height: 17px;
}

.meta {
  font-size: var(--fs-sm);
  color: var(--muted-strong);
  font-variant-numeric: tabular-nums;
}

.pill {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 1px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: var(--fw-semibold);
  background: #fff;
  color: var(--ink-soft);
  border: 1px solid var(--line-soft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  /* E6: the log pane's count pill must never shrink or wrap at the 340px
     floor -- same reasoning as .iconbtn above. */
  flex-shrink: 0;
}

.pill.ok {
  background: var(--ok-tint);
  color: var(--ok);
  border-color: #CFE3DF;
}

/* Stage A collapsed to one line, once Extract has succeeded (contract C, mockup screens 2-5). */
.stage-a-collapsed {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 12px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: #fff;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  margin-bottom: var(--sp-3);
  box-shadow: var(--elev-1);
}

.stage-a-collapsed .ic {
  width: 16px;
  height: 16px;
  color: var(--muted-strong);
  flex: 0 0 auto;
}

.linkbtn {
  border: 0;
  background: none;
  color: var(--accent-dark);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  text-decoration: underline;
  cursor: pointer;
  margin-left: auto;
}

.subhead {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.subhead h3 {
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  margin: 0;
}

/* A 3px left edge means "this block has a category" (contract F3). */
.grouphdr {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  background: #fff;
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink-soft);
  margin: 10px 0 6px;
  box-shadow: var(--elev-1);
}

.grouphdr:first-child {
  margin-top: 0;
}

.fstrip {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 8px 12px;
  margin-bottom: var(--sp-3);
  border: 1px solid var(--warn-line);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-md);
  background: var(--warn-tint);
  color: #7A4A00;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}

.fstrip .ic {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
}

.fstrip .chev {
  margin-left: auto;
  width: 15px;
  height: 15px;
  transition: transform var(--dur-fast) var(--ease);
}

.chev.open {
  transform: rotate(90deg);
}

/* ---------- inline confirm strip -- replaces window.confirm (contract G2) ---------- */
/* LFM multi-log collision, found by live-testing H14 (contract's own
   "check every rule against shell.css, not only the design" lesson): the
   mockup's own .confirm-strip already carries flex-wrap:wrap -- the
   SHIPPED rule below never had it, which every existing (short) confirm
   message happened to never expose. H14's message is longer (it quotes the
   log name AND its line count), and without wrapping the row squeezed
   "Delete forever" until ITS OWN text wrapped ("Delete" / "forever") instead
   -- ugly, and specific to nothing else changed here. flex-wrap:wrap lets
   the message take its own line and the buttons drop cleanly to the next
   one when a strip is this narrow; every existing (shorter) confirm strip
   is completely unaffected, since it already fit on one line. */
.confirm-strip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border-radius: var(--r-xs);
  background: var(--danger-tint);
  border-left: 3px solid var(--danger);
  color: #8A1C13;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  margin-top: var(--sp-2);
  flex-wrap: wrap;
}

.confirm-strip.neutral {
  background: var(--accent-tint);
  border-left-color: var(--accent);
  color: var(--accent-dark);
}

.cs-btn {
  height: 28px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--danger);
  color: #fff;
  font: inherit;
  font-size: 12.5px;
  font-weight: var(--fw-semibold);
  cursor: pointer;
  margin-left: auto;
  /* Same collision as .confirm-strip's own flex-wrap fix just above: the
     STRIP may now wrap, but this one button's own label must still never
     break onto two lines ("Delete" / "forever"). */
  white-space: nowrap;
  flex-shrink: 0;
}

.cs-btn.neutral {
  background: var(--accent);
}

.cs-cancel {
  border: 0;
  background: none;
  font: inherit;
  font-size: 12.5px;
  font-weight: var(--fw-semibold);
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
}

/* ---------- inline rename editor -- replaces window.prompt (contract G1) ---------- */
.edit-wrap {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}

.edit-input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: var(--fs-body);
  padding: 5px 8px;
  border: 1px solid var(--accent);
  border-radius: var(--r-xs);
  box-shadow: 0 0 0 3px var(--accent-tint), var(--inset-1);
  color: var(--ink);
  background: #fff;
}

.edit-hint {
  font-size: 12px;
  color: var(--muted-strong);
  margin-top: 4px;
  display: flex;
  gap: var(--sp-3);
  font-variant-numeric: tabular-nums;
}

/* ---------- LFM multi-log: static note block (contract §H) ----------------
   Same house 3px left-edge-means-a-category language as .grouphdr/.fstrip,
   but with NO chevron and cursor:default -- .fstrip's chevron is the only
   thing that tells the user it opens something, so a lookalike block that
   does nothing on click must not borrow it (contract L6). Used for: the cap
   message (H6), the last-log message (H7), the combined-not-re-sorted banner
   (H10), the added-and-undo strip (H11) and the empty-archive message (H18).
   Three colour variants matching the sheet's existing warn/ok/neutral tokens
   -- .info deliberately reuses --accent/--accent-tint (there is no separate
   "informational teal" token; H9/H11/H10-adjacent teal all read off the same
   accent pair already used everywhere else money-adjacent is neutral, e.g.
   .confirm-strip.neutral just above). */
.note {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: 8px 12px;
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  line-height: 1.45;
  cursor: default;
  margin-bottom: var(--sp-3);
}

.note .ic {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  margin-top: 1px;
}

.note.warn {
  background: var(--warn-tint);
  border: 1px solid var(--warn-line);
  border-left: 3px solid var(--warn);
  color: #7A4A00;
}

.note.info {
  background: var(--accent-tint);
  border: 1px solid #CFE3DF;
  border-left: 3px solid var(--accent);
  color: var(--accent-dark);
}

.note.plain {
  background: #fff;
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--line);
  color: var(--muted-strong);
}

.note b {
  font-weight: var(--fw-semibold);
}

.note .note-body {
  min-width: 0;
  flex: 1 1 auto;
}

.note .note-sub {
  display: block;
  font-weight: 400;
  margin-top: 3px;
  color: inherit;
  opacity: 0.85;
}

/* The Undo button on H11's strip, and the small dismiss on H7's last-log
   note (the only .note that ever gets a close control -- H10 explicitly
   never does, contract line H10). */
.note .ghost {
  align-self: center;
  flex: 0 0 auto;
}

.note .iconbtn {
  align-self: flex-start;
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
}

.note .iconbtn .ic {
  width: 14px;
  height: 14px;
  margin-top: 0;
}

/* ---------- LFM multi-log: the Manage logs panel (contract §G) ------------
   Lives INSIDE the log pane body in place of the tree (G1) -- not a
   popover, not a modal, nothing floats, nothing here changes the pane
   widths (G3). container-type below is what lets .mrow reflow at ~400px of
   PANE width (G13) without any JavaScript measuring anything. */
.mpanel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  container-type: inline-size;
  container-name: mpanel;
}

.mpanel-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.mpanel-head h3 {
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  margin: 0;
}

.mmeta {
  font-size: 12.5px;
  color: var(--muted-strong);
  font-variant-numeric: tabular-nums;
}

.mcap {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-strong);
  margin-top: var(--sp-1);
}

.mrow {
  padding: 8px 10px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: #fff;
  box-shadow: var(--elev-1);
  margin-bottom: 6px;
}

.mrow-top {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.mname {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mrow-meta {
  font-size: 12.5px;
  color: var(--muted-strong);
  margin-top: 1px;
  font-variant-numeric: tabular-nums;
}

/* G13: the count is rendered TWICE -- once inline in .mrow-top (default,
   wide) and once inside .mrow-meta (narrow only) -- so the reflow at ~400px
   of pane width is pure CSS, no JavaScript measuring anything. Exactly one
   of the two is ever visible at a given container width. */
.mrow-count-meta {
  display: none;
}

.mact {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--line-soft);
}

.mact .ghost {
  height: 28px;
  font-size: 12.5px;
}

.mfield {
  margin-bottom: var(--sp-2);
}

.mcard {
  padding: 10px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: #fff;
  box-shadow: var(--elev-1);
}

.mcard .sel {
  width: 100%;
}

.mcard p {
  font-size: var(--fs-sm);
  color: var(--muted-strong);
  line-height: 1.45;
  margin-bottom: var(--sp-3);
}

/* G13: below ~400px of PANE width -- checked against the pane's own inline
   size via container-type on .mpanel above, not the viewport. */
@container mpanel (max-width: 400px) {
  .mrow-count-inline {
    display: none;
  }
  .mrow-count-meta {
    display: inline;
  }
}

/* A red TEXT button -- words on the normal .ghost body, not a filled red
   block, because the first press only opens the confirm strip (contract
   G10/L4). Checked against .ghost above: only color/border-color differ,
   everything else (height, padding, radius, shadow) is inherited. */
.ghost.danger {
  color: var(--danger);
  border-color: #E7C9C5;
}

.adv {
  display: flex;
  gap: var(--sp-2);
  align-items: flex-start;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted-strong);
}

.adv .ic {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  margin-top: 2px;
}

.empty-h {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  margin-bottom: var(--sp-2);
}

.empty-p {
  font-size: var(--fs-sm);
  color: var(--muted-strong);
  line-height: 1.55;
  max-width: 52ch;
  margin-bottom: var(--sp-4);
}

.example-cap {
  font-size: 11px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-strong);
  margin-bottom: 6px;
}

.example {
  opacity: 0.5;
  pointer-events: none;
}

/* ---------- one shared body-level tooltip element (contract D8) ---------- */
.tip {
  position: fixed;
  z-index: 200;
  max-width: 220px;
  padding: 6px 9px;
  border-radius: var(--r-xs);
  background: #22343C;
  color: #fff;
  font-size: 12px;
  font-weight: var(--fw-medium);
  line-height: 1.35;
  box-shadow: var(--elev-3);
  pointer-events: none;
  opacity: 0;
  transition: opacity 90ms var(--ease);
}

.tip.is-on {
  opacity: 1;
}

.tip::after {
  content: '';
  position: absolute;
  left: var(--tip-arrow, 50%);
  margin-left: -5px;
  top: -10px;
  border: 5px solid transparent;
  border-bottom-color: #22343C;
}

.tip.flip-up::after {
  top: auto;
  bottom: -10px;
  border-bottom-color: transparent;
  border-top-color: #22343C;
}

@media (hover: none) {
  .tip {
    display: none !important;
  }
}

/* ---------- responsive: below 900px the frame stacks (design §12) ---------- */
@media (max-width: 899px) {
  .module-frame,
  .module-frame.leaflet-open {
    grid-template-columns: var(--leaflet-rail-w) minmax(0, 1fr);
  }

  /* K2 fix (carried from Round 4): the grid-column rule above shrinks the
     leaflet-open state's column to the 44px rail width, but on its own that
     only squeezes the .leaflet panel into 44px -- it stays the visible
     element (.module-frame.leaflet-open .leaflet is still display:flex),
     so its heading/steps/cost line render crushed into a sliver instead of
     folding. Design §12 already calls for this width: "Leaflet forces
     folded (the rail stays)." So force the fold itself -- swap which of
     .leaflet / .leaflet-rail is visible -- instead of just narrowing the
     column both were sharing. Same selectors/specificity as the two base
     rules at .leaflet/.leaflet-rail visibility (module-frame:not(.leaflet-open)
     .leaflet, module-frame.leaflet-open .leaflet-rail) but declared later
     inside this media query, so they win only while it is active; the
     leaflet-open class itself, and localStorage, are left untouched so the
     user's remembered choice still applies the moment the viewport widens
     back past 899px. */
  .module-frame.leaflet-open .leaflet {
    display: none;
  }

  .module-frame.leaflet-open .leaflet-rail {
    display: flex;
  }

  .panes,
  .panes.work-first {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: minmax(260px, auto);
  }

  .module-body {
    overflow-y: auto;
  }
}

/* ---------- vertical squeeze for short screens (design §5) ---------- */
@media (max-height: 700px) {
  .module-body {
    padding: 12px 16px;
    gap: 10px;
  }

  .module-head {
    min-height: 36px;
  }

  .pane-head {
    padding: 9px 14px;
  }

  .pane-body {
    padding: 10px 14px;
  }

  .tarea-input {
    min-height: 104px;
  }

  .view h1,
  .module-title {
    font-size: 18px;
  }
}

.lfm-export-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: var(--sp-3) 0;
}

/* Hidden on screen; the @media print block below is the only thing that shows it. */
#lfmPrintable {
  display: none;
}

/* Scoped to body.lfm-printing (added only while the log's Print button runs) so
   Ctrl+P on any other view still prints normally instead of a blank page. */
@media print {
  body.lfm-printing .boot-spinner,
  body.lfm-printing .app-shell {
    display: none !important;
  }

  body.lfm-printing #lfmPrintable {
    display: block !important;
    color: #000;
    font-family: Arial, Helvetica, sans-serif;
  }

  #lfmPrintable h1 {
    font-size: 18pt;
    margin: 0 0 8pt;
  }

  #lfmPrintable .lfm-print-meta {
    font-size: 10pt;
    color: #333;
    margin: 0 0 2pt;
  }

  #lfmPrintable .lfm-print-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 12pt;
    font-size: 10pt;
  }

  #lfmPrintable .lfm-print-table th,
  #lfmPrintable .lfm-print-table td {
    border: 1px solid #000;
    padding: 4pt 6pt;
    text-align: left;
    vertical-align: top;
  }

  #lfmPrintable .lfm-print-table th {
    background: #eee;
  }

  #lfmPrintable .lfm-print-disclaimer {
    font-size: 9pt;
    color: #444;
    margin-top: 12pt;
  }

  /* Belt-and-braces (contract F6): a plain Ctrl+P on any other view still
     gets a clean sheet -- no shadows, no ink-wasting tints. The
     body.lfm-printing rules above are the only thing this file relies on
     for the log export itself; these two rules cannot change that output. */
  * {
    box-shadow: none !important;
  }

  .pane,
  .pane-body,
  .leaflet,
  .leaflet-rail,
  .module-body,
  .app-shell {
    background: #fff !important;
  }

  .pane,
  .drow,
  .tnode {
    border-color: #999 !important;
  }

  .sidebar,
  .leaflet-rail {
    display: none !important;
  }
}

.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-sm);
}

.status-badge.ok {
  color: var(--ok);
}

.status-badge.failed {
  color: var(--danger);
}

.row-status {
  flex: 0 0 auto;
}

.history-detail-meta {
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  color: var(--ink-soft);
}

.history-detail-meta .status-badge {
  margin-left: var(--sp-1);
}

.detail-block {
  margin-top: var(--sp-5);
}

/* Round 3 item 2: a combined Extract+Merge history entry renders as one
   panel per stage instead of the single fixed input/result wells above. */
.history-stage-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  margin-top: var(--sp-4);
}

.history-stage-meta {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  color: var(--ink-soft);
}

.history-stage-meta .status-badge {
  margin-left: var(--sp-1);
}

.history-loading {
  padding: var(--sp-5);
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  background: rgba(255,255,255,0.42);
  color: var(--muted-strong);
}

.section-header,
.metric-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

.section-header {
  justify-content: space-between;
  margin-bottom: var(--sp-4);
}

/* Right-hand cluster in a section header (e.g. count pill + Refresh button). */
.section-header-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

/* Collapsible disclosure groups (R8/R9): the summary shows the heading + count,
   the body is revealed on open. Collapsed by default (no `open` attribute). */
.lfm-collapse {
  margin-top: var(--sp-3);
}

.lfm-collapse + .lfm-collapse {
  margin-top: var(--sp-2);
}

.lfm-collapse-summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  user-select: none;
}

.lfm-collapse-summary::-webkit-details-marker {
  display: none;
}

/* Custom caret that rotates when the group is open. */
.lfm-collapse-summary::before {
  content: '';
  flex: 0 0 auto;
  width: 0;
  height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.15s ease;
}

.lfm-collapse[open] > .lfm-collapse-summary::before {
  transform: rotate(90deg);
}

.lfm-collapse-body {
  margin-top: var(--sp-2);
  /* When a group is reopened, cap its height and give it its own scrollbar so
     long lists (version history, grouping decisions) scroll in place instead of
     stretching the whole panel down. (Round-2 UI item 3) */
  max-height: 420px;
  overflow-y: auto;
  padding-right: var(--sp-2);
}

/* Before/after diff for Analyzer proposals (R5): added lines green, removed
   lines red/struck, long unchanged runs collapsed to a marker. */
.lfm-diff {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--fs-sm);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-x: auto;
}

.lfm-diff-line {
  padding: 1px 6px;
  border-radius: 3px;
}

.lfm-diff-add {
  background: var(--ok-tint);
  color: var(--ok);
}

.lfm-diff-del {
  background: var(--danger-tint);
  color: var(--danger);
  text-decoration: line-through;
}

.lfm-diff-gap {
  color: var(--muted-strong);
  text-align: center;
  padding: 2px 0;
  font-size: 11px;
}

/* Confirm-what-to-log rows (R12): the original RFI text is the primary content --
   black, larger, left-aligned next to the checkbox (not pushed to the right). */
.lfm-confirm-details {
  flex: 1;
  text-align: left;
}

.lfm-confirm-source {
  color: var(--ink);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
  line-height: var(--lh-body);
}

/* Nested log drill-down tree (R2/R3): Discipline > Issues Type > Detailed. Each
   level is a native <details>, collapsed by default, with a rotating caret and
   its own outline number (I / A / 1) plus a frequency count pill.
   Depth + tint-chip spine + tabular numerals added in UI Round 4 (contract F). */
.lfm-tree-node {
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: #fff;
  box-shadow: var(--elev-1);
}

.lfm-tree-node + .lfm-tree-node {
  margin-top: var(--sp-2);
}

.lfm-tree-summary {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 11px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.lfm-tree-summary::-webkit-details-marker {
  display: none;
}

.lfm-tree-summary::before {
  content: '';
  flex: 0 0 auto;
  width: 0;
  height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  color: var(--muted-strong);
  transition: transform 0.15s ease;
}

.lfm-tree-node[open] > .lfm-tree-summary::before {
  transform: rotate(90deg);
}

.lfm-tree-num {
  flex: 0 0 auto;
  min-width: 2.2em;
  font-weight: var(--fw-semibold);
  color: var(--accent-dark);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

/* Level-1 (Discipline) numeral sits in a tint chip -- gives the tree a spine. */
.lfm-tree-discipline > .lfm-tree-summary > .lfm-tree-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 22px;
  background: var(--accent-tint);
  border-radius: var(--r-xs);
  font-size: 12.5px;
}

.lfm-tree-label {
  flex: 1;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h3);
  color: var(--ink);
  line-height: 1.3;
}

.lfm-tree-count {
  flex: 0 0 auto;
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

.lfm-tree-body {
  padding: 0 11px 10px 20px;
}

/* Discipline (level 1) reads a touch larger than Issues Type (level 2). */
.lfm-tree-discipline > .lfm-tree-summary .lfm-tree-label {
  font-size: 16px;
}

.lfm-tree-issue {
  border-style: dashed;
  box-shadow: none;
}

.lfm-tree-issue > .lfm-tree-summary .lfm-tree-label {
  font-size: var(--fs-h3);
  color: var(--ink-soft);
}

.dev-grid,
.two-col {
  display: grid;
  gap: var(--sp-4);
}

.dev-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.dev-card {
  min-width: 0;
}

.dev-card-wide {
  grid-column: 1 / -1;
}

.two-col {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.compact-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.compact-list {
  margin-top: var(--sp-4);
}

.compact-empty {
  padding: var(--sp-4);
}

.field-select,
.text-input {
  display: block;
  width: 100%;
  min-height: 40px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink);
}

.field-select:focus,
.text-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

.status-pill,
.metric-pill {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--ok-tint);
  color: var(--ok);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.status-pill.muted,
.metric-pill {
  background: var(--panel-sunk);
  color: var(--ink-soft);
  border: 1px solid var(--line-soft);
}

/* Compact green "Live" marker on the published version in Version history (R6).
   Inherits the base (non-muted) .status-pill green tint. */
.live-pill {
  min-height: 0;
  padding: 2px 8px;
  margin-left: var(--sp-2);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
}

.provider-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--panel-sunk);
}

.provider-row + .provider-row {
  margin-top: var(--sp-2);
}

/* Inline price editor per provider row (R10) — collapsed by default, toggled
   by the row's "Edit prices" button. */
.lfm-price-edit {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

.lfm-price-edit.hidden {
  display: none;
}

.lfm-price-input {
  width: 140px;
}

/* Small button cluster (Edit prices / Delete) at the end of a provider row. */
.lfm-row-actions {
  display: flex;
  flex: 0 0 auto;
  gap: var(--sp-2);
}

.metric-row {
  margin-top: var(--sp-3);
}

@media (max-width: 980px) {
  .dev-grid,
  .two-col {
    grid-template-columns: 1fr;
  }
}

.provider-row input[type="checkbox"] {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
}

/* Confirm-list group header: a 3px left edge means "this block has a
   category" (contract F3), restyled as a --panel-sunk bar with a teal edge. */
.lfm-group-header {
  background: #fff;
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  padding: 7px 10px;
  box-shadow: var(--elev-1);
}

.lfm-group-header .row-title {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink-soft);
}

/* Confirm-what-to-log rows read as cards on the work surface. */
.lfm-confirm-row {
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  box-shadow: var(--elev-1);
  align-items: flex-start;
}

.lfm-detail-row-wrap {
  margin-top: var(--sp-2);
}

.lfm-log-controls {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}

/* Ticket 11 H1 follow-up: while a re-sort is running, lock the log's own
   hand-correction controls (rename / delete / merge / split / move / expand).
   A manual edit mid-job would be silently reverted when the re-sort commits
   from its begin-time snapshot, so we prevent the edit rather than lose it.
   Scoped to the log list only -- the re-sort button lives outside it and is
   disabled separately. Toggled by runLfmResort (added at start, removed in its
   finally so it clears on both success and failure). */
.lfm-log-controls-locked {
  pointer-events: none;
  opacity: 0.55;
}

.lfm-recency-control {
  min-width: 200px;
}

/* Detail row reads as a card ON TOP of the log surface (contract F1), not a
   sunk provider-row -- overrides the shared .provider-row look for this one
   context only; the admin developer panel's .provider-row is untouched. */
.lfm-detail-row {
  display: block;
  padding: 8px 10px;
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  box-shadow: var(--elev-1);
  transition: box-shadow var(--dur-fast) var(--ease);
}

.lfm-detail-row:hover,
.lfm-detail-row:focus-within {
  box-shadow: var(--elev-2);
}

.lfm-detail-row .dnum {
  flex: 0 0 auto;
  min-width: 1.5em;
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--accent-dark);
  font-variant-numeric: tabular-nums;
}

.lfm-detail-row .drow-top {
  display: flex;
  align-items: flex-start;
  gap: 9px;
}

.lfm-detail-row .dtext {
  flex: 1;
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: 1.45;
  color: var(--ink);
}

.lfm-detail-row .dmeta {
  font-size: var(--fs-sm);
  color: var(--muted-strong);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

.lfm-detail-row .cnt {
  font-size: 12.5px;
  font-weight: var(--fw-semibold);
  color: var(--muted-strong);
  margin-right: 6px;
}

/* The row action buttons reveal on hover/focus of the row (contract D4.4-1 /
   D10) -- they stay in the DOM and focusable at all times so keyboard and
   screen-reader users still reach them by tabbing; only OPACITY hides them,
   never display:none or visibility:hidden. */
.lfm-detail-actions {
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-top: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--line-soft);
  opacity: 0;
  transition: opacity 100ms var(--ease);
}

.lfm-detail-row:hover .lfm-detail-actions,
.lfm-detail-row:focus-within .lfm-detail-actions {
  opacity: 1;
}

@media (hover: none) {
  .lfm-detail-actions {
    opacity: 1;
  }
}

.lfm-merge-select {
  width: auto;
  min-height: 28px;
  height: 28px;
  padding: 0 24px 0 8px;
  font-size: 12.5px;
  max-width: 170px;
}

.danger-text {
  color: var(--danger);
}

.lfm-occurrence-panel {
  margin: var(--sp-2) 0 0 var(--sp-4);
  padding-left: var(--sp-3);
  border-left: 2px solid var(--line-soft);
}

.lfm-occurrence-panel .provider-row + .provider-row {
  margin-top: var(--sp-2);
}

/* Zebra-striped occurrence panel -- the one genuinely tabular surface
   (contract F3). Overrides the sunk .provider-row background for this
   context only. */
.lfm-occurrence-row {
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  background: transparent;
  border: 0;
  border-radius: var(--r-xs);
  padding: 6px 10px;
}

.lfm-occurrence-row:nth-child(even) {
  background: rgba(24,40,48,.025);
}

.lfm-occurrence-row input[type="checkbox"] {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 0;
}

.lfm-split-row {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--sp-3);
}

/* The "Move to..." box + Move + Delete buttons kept together as one group, so
   Move sits right beside the box and Delete right after it. (Round-2 UI item 9) */
.lfm-occurrence-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 0 auto;
}

/* A secondary button in the danger colour, for destructive actions like
   deleting a single occurrence. Pair with .secondary-button. */
.danger-button {
  color: var(--danger);
  border-color: var(--danger-tint);
}

.danger-button:hover {
  background: var(--danger-tint);
}
