/* FIN-VIS — app-only UI (NOT part of the portable design system).
 *
 * Loaded by index.html AFTER style.css, so these rules can extend or
 * override the vendored ehsto.dev primitives. Anything a consuming project
 * would want lives in style.css; FIN-VIS-specific chrome lives here.
 *
 * Sections: header filters · view intro · tabs · metric cards · progress
 *           · charts · editable transactions table */

/* ─── NATIVE CONTROL THEMING ─────────────────────────────── */
/* Tell the browser the UI's colour scheme so native controls — the date-input
 * calendar popup, scrollbars, spin-buttons — render dark in the dark theme and
 * light in the light theme, instead of always-light. (Worth upstreaming to
 * ehsto.dev/style.css.) */
html[data-theme="dark"] {
  color-scheme: dark;
}

html[data-theme="light"] {
  color-scheme: light;
}

/* ─── HEADER FILTERS (FY + entity, top-right) ─────────────── */
/* The global FY / entity selectors live in the header, left of the theme
 * toggle. Compact, content-width selects. */
/* Mobile-first: on phones the filters drop to their own full-width row under
 * the brand; at ≥768px they sit inline, left of the theme toggle. */
.header-filters {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-9);
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.header-filters::-webkit-scrollbar {
  height: 0;
}

@media (min-width: 768px) {
  .header-filters {
    overflow-x: visible;
  }
}

/* ─── DASHBOARD: METRIC CARDS ────────────────────────────── */
.metric-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-9);
  margin: var(--space-18) 0;
}

.metric {
  border: 1px solid var(--color-background-brighter);
  background-color: var(--color-background);
  padding: var(--space-12) var(--space-15);
  container-type: inline-size;
}

.metric__label {
  font-family: var(--font-family-01);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-darker);
}

.metric__value {
  font-family: var(--font-family-02);
  /* Scale to the metric's width so long values always fit; capped at 1.5rem. */
  font-size: clamp(0.8rem, 15cqi, 1.5rem);
  font-weight: 600;
  color: var(--color-foreground-brighter);
  margin-top: var(--space-6);
  white-space: nowrap;
}

/* Muted secondary part of a value, e.g. the " / 120" total in Unclassified. */
.metric__value-sub {
  color: var(--color-foreground);
}

.metric__value--income {
  color: var(--color-accent-01);
}

.metric__value--expense {
  color: var(--color-accent-02);
}

.metric__value--warn {
  color: var(--color-accent-03);
}

.metric__sub {
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  color: var(--color-foreground-darker);
  margin-top: var(--space-3);
}

/* ─── PROGRESS BAR (GST threshold) ───────────────────────── */
.progress {
  height: 4px;
  background-color: var(--color-background-brighter);
  margin-top: var(--space-9);
}

.progress__bar {
  height: 4px;
  background-color: var(--color-accent-01);
  transition: width 0.3s ease;
}

.progress__bar--over {
  background-color: var(--color-accent-03);
}

/* ─── SHOW ALL / UNCLASSIFIED RADIOS (Transactions search) ── */
.tx-show-opt {
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  margin: 0;
  font-size: var(--fs-small);
  cursor: pointer;
  text-transform: capitalize;
  letter-spacing: 0.02rem;
  color: var(--color-foreground);
  opacity: 0.6;
}

/* ─── WIDE TABLE SCROLL (P&L monthly grid, wide tables) ──── */
/* Tables with many columns scroll horizontally on narrow viewports instead
 * of overflowing the framed main. */
.table-scroll {
  overflow-x: auto;
}

/* ─── DASHBOARD CHARTS ───────────────────────────────────── */
.charts-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-9);
  margin: var(--space-18) 0;
}

@media (min-width: 1024px) {
  .charts-grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* Chart.js needs a sized, positioned container to be responsive. */
.chart-box {
  position: relative;
  height: 240px;
}

/* Segmented control (e.g. the Income | Expenses switch on the category chart
   header). Two flush buttons in a bordered pill; the active one is filled. */
.seg-toggle {
  display: inline-flex;
  flex: 0 0 auto;
  border: 1px solid var(--color-background-brighter);
  border-radius: 2px;
  overflow: hidden;
}

.seg-toggle__btn {
  border: 0;
  background: transparent;
  color: var(--color-foreground);
  font-family: var(--font-family-01);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  padding: var(--space-3) var(--space-9);
  cursor: pointer;
  transition: all 0.2s ease;
  opacity: 0.72;
}

.seg-toggle__btn + .seg-toggle__btn {
  border-left: 1px solid var(--color-background-brighter);
}

.seg-toggle__btn:hover {
  color: var(--color-foreground);
}

.seg-toggle__btn.is-active {
  background-color: var(--color-background-darker);
  color: var(--color-foreground-brighter);
  border: 1px solid var(--color-foreground);
}

/* ─── HEADER ON SMALL SCREENS ────────────────────────────── */
/* Let the demo badge + toggle wrap under the brand rather than overflow. */
.header__actions {
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* ─── TABS (paper-folder) ──────────────────────────────────── */
/* One tab model, identical whether the bar sits below the header or in it:
 *  - A separator line runs under the whole tab row = the top of the content
 *    page. Below-header it's an inset box-shadow on #tabs (see below); in-header
 *    it's the header's own bottom border.
 *  - Inactive tabs are plain transparent labels, so the separator shows through
 *    beneath them (they read as sitting ON the line).
 *  - The active tab has a foreground-darker outline on three sides and its
 *    opaque body covers the separator in its slice, so it merges into the page
 *    below — a paper folder tab. Below-header the cover is its background-darker
 *    bottom border; in-header it's the ::after "neck" bridging to the page. */
#tabs {
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: flex-end;
  margin: 0;
  gap: 0;
  background-color: var(--color-background-darker);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* Mobile: sticky bar that slides out of view on scroll-down (toggled by JS).
     Sticks just below the 42px sticky header (which owns top:0 / z-index:100),
     so it isn't parked behind it. */
  position: sticky;
  top: 42px;
  z-index: 20;
  transition: transform 0.2s ease;
}

#tabs.is-tabs-hidden {
  transform: translateY(-100%);
}

#tabs::-webkit-scrollbar {
  height: 0;
}

/* Inactive tab: plain text — transparent border and background, so the separator
 * line shows through beneath it (it reads as a label sitting on the line). Used
 * in both states; the in-header rules only override what actually differs.
 * margin-bottom:0 cancels style.css's generic >=768 nav `margin-bottom:-1px`. */
#tabs button {
  width: auto;
  flex: 0 0 auto;
  white-space: nowrap;
  margin-bottom: 0;
  border: 1px solid transparent;
  background-color: transparent;
  color: var(--color-foreground);
  padding: var(--space-6) var(--space-9);
  border-radius: 2px 2px 0 0;
}

#tabs button:hover {
  color: var(--color-foreground-brighter);
}

/* Active tab: foreground-darker outline on three sides; its BOTTOM border is the
 * page colour (background-darker), so it covers the separator line in this tab's
 * slice and the tab reads as connected to the page below — a paper folder tab.
 * Same appearance as the in-header active tab. */
#tabs button.is-active {
  background-color: var(--color-background);
  color: var(--color-foreground-brighter);
  font-weight: 600;
  border: 1px solid var(--color-foreground-darker);
  border-bottom-color: var(--color-background-darker);
  position: relative;
  z-index: 1;
}

/* The content page below the tabs. Its top edge is the #tabs separator line
 * (below-header) or the header border (in-header), so the panel itself carries
 * no top border — just the subtle side/bottom frame. */
#tabs~.panel.is-active {
  border: 1px solid var(--color-background-brighter);
  border-top: none;
}

/* Always-on separator line under the tab row = the top of the content page. It
   lives on the #tabs container (not the panel), so it exists independently of
   whatever view is mounted. The container is inset by main's 6px padding, so the
   line spans the full site width minus a 6px margin each side. In-header the
   header's own border is the separator, so this is scoped to the below state.

   It's an INSET box-shadow (not a border-bottom) on purpose: #tabs uses
   overflow-x:auto for horizontal scroll on narrow screens, which per spec forces
   overflow-y to clip too — that would clip a bottom-overlapping active tab. An
   inset shadow paints inside the content box, so it's never clipped, and child
   tabs paint over it: inactive (transparent) tabs let it show; the active tab's
   opaque body covers it in its slice, connecting it to the page below. */
body:not(.tabs-in-head) #tabs {
  box-shadow: inset 0 -1px 0 var(--color-foreground-darker);
}

/* The tab surfaces are defined once on the base #tabs rules (above) and win at
   every width by specificity (#tabs beats style.css's generic `nav`), so the
   below-header tabs look identical from mobile up until they move into the
   header — no per-breakpoint override, no 768px jump. In-header rules
   (body.tabs-in-head, below) win by their own higher specificity. */

/* In-header tabs: whenever there's room, JS moves the tab bar into the header
   (between brand and filters) and sets `body.tabs-in-head`. This is decided by
   measuring available space, not a fixed breakpoint — see placeTabs(). The
   header gains a 1px bottom border; the active tab extends a "neck" (::after)
   down to that border and paints over it (breaking the line), while the section
   below drops its top border — so the active tab opens straight into the
   current page. --tab-neck = distance from the tab's bottom edge to the
   header's bottom border; nudge it if the header height/padding changes. */
body.tabs-in-head header {
  border-bottom: 1px solid var(--color-foreground-darker);
}

body.tabs-in-head header #tabs {
  position: static;
  top: auto;
  transform: none;
  z-index: auto;
  background-color: transparent;
  overflow: visible;
  /* Natural width, never shrink — so the tab buttons can't be squeezed into
     (overlapping) .header-filters. Filters take the remaining space and scroll
     if tight; placeTabs() drops the whole bar below the header before it comes
     to that. */
  flex: 0 0 auto;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: start;
  gap: var(--space-6);
  --tab-neck: 6px;
}

/* Filters: fill the space to the right of the tabs, but NEVER shrink below their
   content. If they shrank, the right-aligned filter content would spill left
   over the tabs (overflow-x is visible at >=768). Non-shrinking means the header
   simply overflows when it's too tight, which placeTabs() detects and responds
   to by dropping the tab bar below the header — so tabs and filters can't
   overlap at any width. */
body.tabs-in-head header .header-filters {
  flex: 1 0 auto;
  min-width: 0;
}

/* In-header tab buttons inherit the shared base #tabs button styles; only the
   active tab differs — it sits above the sticky header and drops its bottom
   border so the ::after neck (not a border) bridges it to the page. */
body.tabs-in-head header #tabs button.is-active {
  z-index: 101;         /* above the sticky header (z-100) so the neck paints over its border */
  border-bottom: none;  /* the ::after neck handles the connect instead */
}

/* Neck: same-width fill from the tab's bottom down to the header's bottom
   border, covering the border line so the tab reads as open. */
body.tabs-in-head header #tabs button.is-active::after {
  content: "";
  position: absolute;
  left: -1px;
  right: -1px;
  top: 100%;
  height: var(--tab-neck);
  background-color: var(--color-background);
  border-left: 1px solid var(--color-foreground-darker);
  border-right: 1px solid var(--color-foreground-darker);
}

/* Section drops its top border so the active tab opens straight into it. */
body.tabs-in-head main > .panel.is-active {
  border: 1px solid var(--color-background-brighter);
  border-top: none;
  margin-top: 0;
}

/* ─── EDITABLE TRANSACTIONS TABLE (custom, no library) ────── */
/* Sortable headers; each cell is a borderless input/select so the table
 * reads like a spreadsheet. Built on the .data-table--02 primitive. */

/* Drop the design-system table's default top margin for this table only. */
.tx-table {
  margin-top: 0;
}

/* P&L section headings (Income / Expenses): left-aligned + uppercase. The cell
   spans the row so it's also :last-child, which the base table rule would
   otherwise centre. */
.pnl-section td {
  text-align: left;
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  padding: var(--space-12) var(--space-9);
  color: var(--color-foreground-brighter);
}

/* Section totals (Total income / Total expenses) row — emphasised values with
   extra space above to set the total apart from the rows it sums. */
.pnl-total td {
  color: var(--color-foreground-brighter);
  padding-top: var(--space-18);
}

.tx-table th.tx-sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

.tx-table .sort-arrow {
  color: var(--color-accent-01);
  font-size: 0.6rem;
}

/* Let the inputs fill the cell; padding moves onto the controls. */
.tx-table td {
  padding: 0;
}

.tx-table input:not([type="checkbox"]),
.tx-table select {
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--color-foreground);
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  font-weight: 400;
  letter-spacing: 0;
  opacity: 1;
  padding: var(--space-3) var(--space-9);
}

/* Override the design-system input focus (which bumps font-size/weight) so
 * the grid rows don't jump while editing. */
.tx-table input:focus,
.tx-table select:focus {
  outline: 1px solid var(--color-accent-01);
  outline-offset: -1px;
  background-color: var(--color-background-darker);
  color: var(--color-foreground-brighter);
  font-size: var(--fs-small);
  font-weight: 400;
}

.tx-table td.num input {
  text-align: right;
}

/* Column widths: the data columns with a known/bounded content width get a fixed
   size so Description (and Merchant) absorb the slack. Date never wraps. */
.tx-table th[data-sort="date"],
.tx-table td[data-col="date"] {
  width: 104px;
  white-space: nowrap;
}

.tx-table th[data-sort="type"],
.tx-table td[data-col="type"] {
  width: 92px;
}

.tx-table th[data-sort="amount"],
.tx-table td[data-col="amount"] {
  max-width: 80px;
}

.tx-table th[data-sort="entity_id"],
.tx-table td[data-col="entity_id"] {
  width: 120px;
}

.tx-table th[data-sort="category"],
.tx-table td[data-col="category"] {
  width: 132px;
}

.tx-table th[data-sort="account_number"],
.tx-table td[data-col="account_number"] {
  width: 116px;
}

/* Evidence: collapse to its content (icon / header label) — as narrow as it goes. */
.tx-table th.tx-ev-head,
.tx-table td.tx-ev-cell {
  width: 1%;
  white-space: nowrap;
}

/* Loading row shown while the transactions body renders. */
.tx-loading td {
  text-align: center;
  padding: var(--space-30);
  color: var(--color-foreground-darker);
  border: none;
}

.tx-spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-right: var(--space-6);
  border: 2px solid var(--color-background-brighter);
  border-top-color: var(--color-accent-01);
  border-radius: 50%;
  vertical-align: middle;
  animation: tx-spin 0.7s linear infinite;
}

@keyframes tx-spin {
  to { transform: rotate(360deg); }
}

/* A hand-edited cell — marked so it's clear rules won't overwrite it. */
.tx-table td.is-manual {
  box-shadow: inset 2px 0 0 var(--color-accent-03);
}

/* Bin/trash icon button for delete actions (replaces the word "delete"). */
.tx-del {
  color: var(--color-foreground-darker);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-9);
  transition: color 0.2s ease;
}

.tx-del:hover {
  color: var(--color-accent-02);
}

/* Pencil/edit icon button (sits left of the bin in the bulk-edit rule list). */
.tx-edit {
  color: var(--color-foreground-darker);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-6);
  transition: color 0.2s ease;
}

.tx-edit:hover {
  color: var(--color-foreground-brighter);
}

/* Header layout: label + sort arrow on the left, ▾ menu button pushed right. */
.tx-table th .th-inner {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  width: 100%;
}

.tx-table th .col-menu-btn {
  margin-left: auto;
  background: transparent;
  border: 0;
  padding: 0 2px;
  color: var(--color-foreground-darker);
  cursor: pointer;
  font-size: 0.7rem;
  line-height: 1;
}

.tx-table th .col-menu-btn:hover {
  color: var(--color-foreground-brighter);
}

/* Column with an active filter — mark the ▾ in the accent colour. */
.tx-table th .col-menu-btn.has-filter {
  color: var(--color-accent-01);
}

/* ─── COLUMN MENU POPOVER (sort + filter) ────────────────── */
.col-menu {
  position: fixed;
  display: none;
  z-index: 200;
  min-width: 200px;
  max-width: 260px;
  background-color: var(--color-background);
  border: 1px solid var(--color-foreground-darker);
  padding: var(--space-9);
  font-family: var(--font-family-01);
}

.col-menu.is-open {
  display: block;
}

.col-menu__sort {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.col-menu__btn {
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--color-foreground);
  padding: var(--space-3) var(--space-6);
  cursor: pointer;
  font-size: var(--fs-small);
}

.col-menu__btn:hover {
  background-color: var(--color-background-darker);
  color: var(--color-foreground-brighter);
}

.col-menu__filter {
  border-top: 1px solid var(--color-background-brighter);
  margin-top: var(--space-9);
  padding-top: var(--space-9);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
}

.col-menu__label {
  width: 100%;
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-darker);
}

.col-menu__filter select,
.col-menu__filter input[type="text"],
.col-menu__filter input[type="number"],
.col-menu__filter input[type="date"] {
  width: 100%;
}

.col-menu__list {
  width: 100%;
  max-height: 160px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.col-menu__opt {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-small);
  text-transform: none;
  letter-spacing: 0;
  color: var(--color-foreground);
  margin: 3px;
}

.col-menu__actions {
  display: flex;
  gap: var(--space-6);
  margin-top: var(--space-9);
}

/* Region info (FY range, currency, tax threshold) below the selector. */
.fy-range {
  font-family: var(--font-family-01);
  font-size: var(--fs-small);
  line-height: 2.1;
  color: var(--color-foreground);
  margin-bottom: var(--space-12);
}

/* ─── TRANSACTIONS: search bar (card header) + filter chips ── */

.tx-filters {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex-wrap: wrap;
}

.tx-filters__label {
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-darker);
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  font-size: var(--fs-small);
  color: var(--color-foreground);
  background-color: var(--color-background-darker);
  border: 1px solid var(--color-background-brighter);
  padding: var(--space-3) var(--space-6);
}

.filter-chip__x {
  background: transparent;
  border: 0;
  color: var(--color-foreground-darker);
  cursor: pointer;
  font-size: 0.95rem;
  line-height: 1;
  padding: 0;
}

.filter-chip__x:hover {
  color: var(--color-accent-02);
}

.filter-chip--clear {
  background: transparent;
  border: 0;
  cursor: pointer;
  font-size: var(--fs-small);
  font-style: italic;
  color: var(--color-foreground-darker);
  padding: 0 var(--space-6);
}

.filter-chip--clear:hover {
  color: var(--color-foreground-brighter);
}

/* ─── TRANSACTIONS: two-up card row (dataset · bulk editor) ─ */
/* Stacked on mobile; 50/50 side by side from desktop up. */
.tx-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-18);
  margin-bottom: var(--space-12);
  align-items: start;
}

/* Mobile: flatten both columns into one stacked flow (display: contents) so the
   search card can be pushed last — directly above the table — via order. */
.tx-cards__col {
  display: contents;
}

@media (min-width: 1024px) {
  .tx-cards {
    grid-template-columns: 1fr 1fr;
  }

  /* Restore the two columns (Bulk editor above Category manager on the right). */
  .tx-cards__col {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
}

/* ─── Transactions table card (toolbar header + table) ────── */
.tx-table-card {
  overflow: hidden;
}

/* Toolbar + subbar form one header block; the divider sits at the block's
   bottom so the subbar reads as part of the toolbar (not a separate strip). */
.tx-header {
  border-bottom: 1px solid var(--color-background-brighter);
}

.tx-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex-wrap: wrap;
  padding: var(--space-12) var(--space-15);
}

.tx-title {
  margin: 0;
  display: none;
}

/* hidden on mobile */
.tx-toolbar__tools {
  display: flex;
  align-items: center;
  gap: var(--space-18);
  flex: 1 1 auto;
  flex-wrap: wrap;
}

/* Count + All/Unclassified share a row. Mobile: full-width row below the search —
   show-opts far left, count far right. Desktop: count just right of the search,
   then show-opts (reset below). */
.tx-toolbar__filters {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  width: 100%;
  flex-direction: row-reverse;
  justify-content: space-between;
}

/* The icon and input sit side-by-side inside a field-styled wrapper, so the icon
   can never overlap the text. The wrapper carries the border/background; the
   input itself is borderless. */
.tx-search {
  flex: 1 1 200px;
  flex-direction: row-reverse;
  max-width: 340px;
  display: flex;
  align-items: center;
  gap: var(--space-6);
  padding: 0 var(--space-9);
  background-color: var(--color-background-darker);
  border: 1px solid var(--color-background-brighter);
}

.tx-search__ico {
  flex: none;
  display: inline-flex;
  color: var(--color-foreground);
  opacity: 0.72;
}

/* Two classes → beats input[type="search"] so these actually apply. */
.tx-search .tx-search__input {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  border: 0;
  padding: var(--space-9) 0;
  background: transparent;
}

.tx-show-opts {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex: none;
}

.tx-subbar {
  display: flex;
  align-items: center;
  gap: var(--space-9);
  padding: 0 var(--space-15) var(--space-12);
  /* hugs the toolbar above it */
}

.tx-subbar[hidden] {
  display: none;
}

/* Bulk-action bar — appears when rows are selected. One inline row:
   [N selected]  Set [field] to [value] [Apply] …… [Delete][Clear]. */
.tx-bulkbar {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex-wrap: wrap;
  padding: var(--space-21) var(--space-15);
  background: var(--color-background-darker);
  border-bottom: 1px solid var(--color-background-brighter);
}

.tx-bulkbar[hidden] {
  display: none;
}

.tx-bulkbar__count {
  font-weight: 600;
  flex: none;
  white-space: nowrap;
}

.tx-bulkbar__set {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex-wrap: wrap;
}

.tx-bulkbar__end {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex: none;
  margin-left: auto;
}

/* Stop the global width:100% from blowing the controls full-width. */
.tx-bulkbar__set select {
  width: auto;
}

.tx-bulkbar__set input {
  width: 12em;
  max-width: 40vw;
}

.tx-bulkbar .rb-mut {
  flex: none;
}

/* Selection column. */
.tx-select {
  width: var(--space-30);
  text-align: center;
  vertical-align: middle;
}

.tx-select input[type="checkbox"] {
  vertical-align: middle;
  margin: 0;
}

.tx-table tbody tr.is-selected td {
  background: rgba(var(--color-accent-01-rgb), 0.08);
}

@media (min-width: 768px) {
  .tx-title {
    display: block;
  }

  .tx-toolbar__tools {
    justify-content: flex-end;
  }

  /* search + filters on the right */
  /* Count back to just right of the search, then show-opts. */
  .tx-toolbar__filters {
    width: auto;
    flex-direction: row;
    justify-content: flex-start;
  }
}

/* Collapsible card headers are fully clickable. */
.card:has(.card__toggle) .card__header {
  cursor: pointer;
}

/* ─── CATEGORY MANAGER CARD ──────────────────────────────── */
/* Two columns (Personal | Business) to keep the card short. A column whose
 * entity type doesn't exist is greyed out. */
.cat-mgr__cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-60);
  margin-top: var(--space-21);
}

@media (min-width: 600px) {
  .cat-mgr__cols {
    grid-template-columns: 1fr 1fr;
  }
}

/* Transaction count per category, right-aligned in each row. */
.cat-mgr__count {
  flex: 0 0 auto;
  min-width: 2ch;
  text-align: right;
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  color: var(--color-foreground-darker);
}

.cat-mgr__head {
  display: flex;
  align-items: center;
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground);
  margin-bottom: var(--space-18);
}

.cat-mgr__col.is-disabled {
  opacity: 0.4;
}

.cat-mgr {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin-bottom: var(--space-9);
}

.cat-mgr__row {
  display: flex;
  align-items: center;
  gap: var(--space-9);
}

.cat-swatch {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid var(--color-background-brighter);
}

.cat-mgr__sortbtn {
  border: 0;
  background: transparent;
  color: var(--color-foreground-darker);
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  transition: opacity 0.2s ease, color 0.2s ease;
}

.cat-mgr__sortbtn:hover,
.cat-mgr__sortbtn.is-active {
  opacity: 0.6;
  color: var(--color-foreground);
}

/* A–Z / Z–A text toggle sits next to the column label. */
.cat-mgr__sort-name {
  font-size: var(--fs-small);
  letter-spacing: 0.04rem;
  margin-left: auto;
}

/* Count arrow floats to the right, centred over the count column. */
.cat-mgr__sort-count {
  margin: 0 -3px 0 var(--space-12);
  min-width: 2ch;
  justify-content: center;
}

.sort-arrow {
  width: 13px;
  height: 13px;
}

.cat-mgr__field {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
}

.cat-mgr__name,
.cat-mgr__new {
  flex: 1 1 auto;
  min-width: 0;
}

/* Leave room for the delete control so the name text doesn't run under it. */
.cat-mgr__field .cat-mgr__name {
  padding-right: var(--space-18);
}

/* Delete-category control — only rendered for unused categories, floated inside
   the name input's right edge (so it reads as part of the field). */
.cat-mgr__del {
  position: absolute;
  right: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  width: 1.3em;
  height: 1.3em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--color-foreground-darker);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.2s ease;
}

.cat-mgr__del:hover {
  color: var(--color-accent-02);
}

/* ─── CSV COLUMN-MAPPING PANEL ───────────────────────────── */
.map-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-9);
  margin-bottom: var(--space-6);
}

.map-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}

.map-row__label {
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-darker);
}

.map-row__ctrl select {
  width: auto;
  min-width: 12ch;
}

.map-row__ctrl label {
  font-size: var(--fs-small);
}

@media (min-width: 768px) {
  .map-row {
    grid-template-columns: 200px 1fr;
    align-items: center;
    gap: var(--space-12);
  }
}

/* ─── RULES: list + Shopify-style condition builder ──────── */
/* Cap the rules list and scroll it vertically, with the scrollbar hidden
   (same treatment as the transactions table). */
#rules-list {
  max-height: 150px;
  background: var(--color-background-darker);
  overflow-y: auto;
  scrollbar-width: none;
}

#rules-list::-webkit-scrollbar {
  width: 0;
}

/* No darker box when there are no rules to show. */
#rules-list.is-empty {
  background: transparent;
}

/* Bottom fade hint — only shown (via .has-more, toggled in JS) when the list
   overflows and isn't scrolled to the bottom, so the user sees there's more. */
.rules-list-wrap {
  position: relative;
  margin: var(--space-21) 0px 0px 0px;
}

.rules-list-wrap::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--space-18);
  background: linear-gradient(to top, var(--color-background-darker), transparent);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.rules-list-wrap.has-more::after {
  opacity: 1;
}

.rule-row {
  display: flex;
  align-items: center;
  gap: var(--space-9);
  padding: var(--space-3) 0 0 var(--space-9);
  border-bottom: 1px solid var(--color-background-brighter);
}

.rule-row__text {
  flex: 1;
  font-size: var(--fs-small);
}

.rule-row.is-disabled .rule-row__text {
  opacity: 0.45;
  text-decoration: line-through;
}

.rule-builder {
  margin-top: var(--space-3);
  padding-top: var(--space-30);
  width: 100%;
}

/* WHEN (left) and THEN (right); stacked on mobile. A named grid so the centre
 * divider spans only the conditions / action row — NOT the eyebrows or the
 * "+ Add condition" button — so its height tracks the conditions box as rows
 * are added. The Create-rule row sits below this grid. */
.rule-builder__cols {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "whenhead"
    "whenbody"
    "whenfoot"
    "thenhead"
    "thenbody"
    "thenfoot";
  gap: var(--space-9);
}

.rb-a-whenhead {
  grid-area: whenhead;
}

.rb-a-whenbody {
  grid-area: whenbody;
}

.rb-a-whenfoot {
  grid-area: whenfoot;
}

.rb-a-thenhead {
  grid-area: thenhead;
}

.rb-a-thenbody {
  grid-area: thenbody;
}

.rb-a-thenfoot {
  grid-area: thenfoot;
}

.rule-builder__arrow {
  grid-area: arrow;
}

/* In the grid, vertical spacing is the grid gap — drop the element margins. */
.rule-builder__cols .rule-builder__eyebrow {
  margin-bottom: 0;
}

.rule-builder__cols .rb-a-whenfoot,
.rule-builder__cols .rb-a-thenfoot {
  margin-top: 0;
}

/* Divider hidden while the columns are stacked (mobile). */
.rule-builder__arrow {
  display: none;
}

@media (min-width: 768px) {
  .rule-builder__cols {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-areas:
      "whenhead .     thenhead"
      "whenbody arrow thenbody"
      "whenfoot .     thenfoot";
    column-gap: var(--space-42);
    row-gap: var(--space-21);
    align-items: start;
  }
}

@media (min-width: 1024px) {
  .rule-builder__cols {
    column-gap: var(--space-21);
    row-gap: var(--space-21);
  }
}

@media (min-width: 1230px) {
  .rule-builder__cols {
    column-gap: var(--space-42);
    row-gap: var(--space-21);
  }
}

/* A 1px line in two segments with the right arrow (WHEN -> THEN) in the gap.
 * align-self: stretch fills the conditions/action row, so the divider's height
 * tracks the conditions box. Desktop only — hidden while the columns stack. */
@media (min-width: 768px) {
  .rule-builder__arrow {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    gap: var(--space-6);
    color: var(--color-foreground-darker);
  }

  .rule-builder__arrow::before,
  .rule-builder__arrow::after {
    content: "";
    width: 1px;
    flex: 1 1 auto;
    min-height: var(--space-12);
    background-color: var(--color-background-brighter);
  }
}

/* WHEN / THEN eyebrow labels group the builder into two readable zones. */
.rule-builder__eyebrow {
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-brighter);
  margin-bottom: var(--space-6);
  border-top: 1px solid var(--color-foreground);
  padding-top: 12px;
}

/* Conditions sit on a recessed surface so the WHEN block reads as one group. */
.rule-builder__conds {
  padding: 0;
}

.rb-a-whenfoot,
.rb-a-thenfoot {
  border: 0;
  font-style: italic;
  color: var(--color-foreground);
  opacity: 0.6;
  margin-top: var(--space-9);
}

/* Each condition's inputs stack vertically (field, operator, value), with a
 * small right-aligned remove control at the end of the stack. */
.cond-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.cond-row+.cond-row {
  margin-top: var(--space-9);
  padding-top: var(--space-9);
  border-top: 1px solid var(--color-background-brighter);
}

.cond-row select,
.cond-row input,
.cond-row .ui-select {
  width: 100%;
  min-width: 0;
}

.cond-row .cond-remove {
  align-self: flex-end;
}

/* Give every bulk-editor control the same themed surface. The design system
 * styles <select> and typed inputs but NOT input[type="text"] (used by text
 * conditions and the category combobox), so set it explicitly here. */
.rule-builder select,
.rule-builder input {
  background-color: var(--color-background-darker);
  border: 1px solid var(--color-background-brighter);
  color: var(--color-foreground);
}

/* Uniform height for every control so the rows line up as a clean grid. */
.rule-builder select,
.rule-builder input,
.rule-builder .ui-select,
.rule-builder .cat-trigger {
  min-height: 30px;
  box-sizing: border-box;
}

/* Cancel the design-system focus "size-bump" (font grows on focus) here — it
 * makes controls resize and, in the category popover, reflows the whole menu. */
.rule-builder select:focus,
.rule-builder input:focus,
.cat-new__input:focus {
  font-size: var(--fs-input);
  font-weight: 200;
}

/* THEN action: two stacked rows, each with its "set" / "to" label on the left
 * and the control filling the rest of the row. */
.rule-builder__action {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  font-size: var(--fs-small);
}

/* Each THEN action is a set/to pair; separate multiples with a hairline. */
.then-action {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.then-action+.then-action {
  margin-top: var(--space-9);
  padding-top: var(--space-9);
  border-top: 1px solid var(--color-background-brighter);
}

.then-row {
  display: flex;
  align-items: center;
  gap: 0;
}

.then-row .rb-mut {
  flex: 0 0 2.5em;
}

.then-row select,
.then-row input,
.then-row .ui-select,
.then-row .cat-trigger {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
}

/* Custom styled select trigger — looks like a themed <select>, opens a pinned
 * popover instead of the native dropdown. Shared by the field/operator/value
 * selects (.ui-select) and the category action value (.cat-trigger). */
.ui-select,
.cat-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-9);
  width: auto;
  text-align: left;
  font-family: var(--font-family-02);
  font-size: var(--fs-input);
  font-weight: 200;
  letter-spacing: 0.06rem;
  text-transform: uppercase;
  color: var(--color-foreground);
  background-color: var(--color-background-darker);
  border: 1px solid var(--color-background-brighter);
  padding: var(--space-6) var(--space-9);
  cursor: pointer;
}

.ui-select__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Create-new row at the top of the category popover. */
.cat-new {
  display: flex;
  gap: var(--space-6);
  padding: var(--space-9);
  border-bottom: 1px solid var(--color-background-brighter);
}

.cat-new__input {
  flex: 1 1 auto;
  min-width: 0;
  background-color: var(--color-background-darker);
  border: 1px solid var(--color-background-brighter);
  color: var(--color-foreground);
  padding: var(--space-6) var(--space-9);
  font-family: var(--font-family-02);
  font-size: var(--fs-input);
}

.rb-mut {
  font-size: var(--fs-small);
  text-transform: uppercase;
  color: var(--color-foreground);
  opacity: 0.6;
}

/* Live match count + blank-field suggestion chips under the rule builder. */
.rule-preview {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6) var(--space-12);
  margin-top: var(--space-12);
  font-size: var(--fs-small);
}

.rule-preview__count strong {
  color: var(--color-accent-01);
}

.rule-preview__chips {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
}

.rule-chip {
  background: transparent;
  border: 1px solid var(--color-background-brighter);
  color: var(--color-foreground);
  border-radius: 2px;
  cursor: pointer;
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  padding: var(--space-3) var(--space-9);
  transition: all 0.2s ease;
}

.rule-chip:hover {
  border-color: var(--color-accent-01);
  color: var(--color-accent-01);
}

.cond-remove {
  background: transparent;
  border: 1px solid var(--color-background-brighter);
  color: var(--color-foreground-darker);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 var(--space-6);
}

.cond-remove:hover {
  color: var(--color-accent-02);
  border-color: var(--color-accent-02);
}

/* ─── DATE-RANGE PICKER (period) ─────────────────────────── */
.period-menu {
  position: fixed;
  display: none;
  z-index: 200;
  background-color: var(--color-background-darker);
  border: 1px solid var(--color-background-brighter);
  font-family: var(--font-family-01);
}

.period-menu.is-open {
  display: block;
}

.period-preset {
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--color-foreground);
  padding: var(--space-6) var(--space-9);
  cursor: pointer;
  font-size: var(--fs-small);
  white-space: nowrap;
}

.period-preset:hover {
  background-color: var(--color-background-darker);
  color: var(--color-foreground-brighter);
}

.period-preset.is-active {
  color: var(--color-accent-01);
}

.period-cal {
  padding: var(--space-9);
}

.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-6);
}

.cal-title {
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-brighter);
}

.cal-nav {
  background: transparent;
  border: 0;
  color: var(--color-foreground);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 var(--space-6);
}

.cal-nav:disabled {
  opacity: 0.25;
  cursor: default;
}

.cal-nav:hover {
  color: var(--color-foreground-brighter);
}

.cal-dow,
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 30px);
  gap: 2px;
}

.cal-dow span {
  text-align: center;
  font-size: 0.62rem;
  color: var(--color-foreground-darker);
  padding: var(--space-3) 0;
}

.cal-day {
  background: transparent;
  border: 0;
  color: var(--color-foreground);
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  height: 28px;
  cursor: pointer;
}

.cal-day:hover {
  background-color: var(--color-background-darker);
  color: var(--color-foreground-brighter);
}

.cal-day.in-range {
  background-color: var(--color-accent-01-darker);
  color: var(--color-foreground-brighter);
}

.cal-day.is-end {
  background-color: var(--color-accent-01);
  color: var(--color-background);
  font-weight: 600;
}

.cal-empty {
  height: 28px;
}

.period-menu__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-12);
  padding: var(--space-9);
  border-top: 1px solid var(--color-background-brighter);
  font-size: var(--fs-small);
}

.period-range {
  color: var(--color-foreground-darker);
  font-family: var(--font-family-02);
}

/* ─── SINGLE-DATE PICKER (shared) ────────────────────────── */
/* Readonly date fields look clickable and open the custom calendar. */
.datefield {
  cursor: pointer;
}

.date-pop {
  position: fixed;
  display: none;
  z-index: 210;
  background-color: var(--color-background);
  border: 1px solid var(--color-foreground-darker);
  padding: var(--space-9);
}

.date-pop.is-open {
  display: block;
}

.date-pop__foot {
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-background-brighter);
  text-align: right;
}

.date-pop__foot .cal-nav {
  font-size: var(--fs-small);
  padding: 0;
}

/* ─── PERIOD MENU: FY list view ──────────────────────────── */
.period-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: var(--space-9);
  min-width: 190px;
}

.period-list__label {
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-darker);
  padding: var(--space-3) var(--space-9) var(--space-6);
}

.period-list__empty {
  padding: var(--space-6) var(--space-9);
}

.period-preset--custom {
  border-top: 1px solid var(--color-background-brighter);
  margin-top: var(--space-6);
  padding-top: var(--space-9);
  color: var(--color-foreground-brighter);
}

/* ─── TRANSACTIONS: scroll region + sticky header ────────── */
/* The transactions grid scrolls within its own region so the column headers
 * stay frozen while paging through rows (spreadsheet behaviour). */
.tx-scroll {
  max-height: calc(100vh - 100px);
  overflow: auto;
}

.tx-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background-color: var(--color-background);
}

.metric__delta {
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  color: var(--color-foreground-darker);
  margin-top: var(--space-3);
}

.metric__delta--up {
  color: var(--color-accent-01);
}

.metric__delta--down {
  color: var(--color-accent-02);
}

/* ─── SETTINGS COG + PANEL ───────────────────────────────── */
.settings-cog {
  background: transparent;
  border: 0;
  color: var(--color-foreground);
  cursor: pointer;
  padding: var(--space-3);
  display: inline-flex;
  align-items: center;
  line-height: 1;
  opacity: 0.7;
  transition: color 0.2s ease, opacity 0.2s ease, transform 0.4s ease;
}

.settings-cog:hover {
  color: var(--color-foreground-brighter);
  opacity: 1;
  transform: rotate(90deg);
}

.settings-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 290;
  display: none;
}

.settings-backdrop.is-open {
  display: block;
}

.settings-panel {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: min(360px, 92vw);
  background-color: var(--color-background);
  border-left: 1px solid var(--color-foreground-darker);
  z-index: 300;
  transform: translateX(100%);
  transition: transform 0.25s ease;
  overflow-y: auto;
}

.settings-panel.is-open {
  transform: translateX(0);
}

.settings-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-15) var(--space-18);
  border-bottom: 1px solid var(--color-background-brighter);
  position: sticky;
  top: 0;
  z-index: 10;
  background-color: var(--color-background);
}

.settings-close {
  background: transparent;
  border: 0;
  color: var(--color-foreground);
  cursor: pointer;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0 var(--space-6);
}

.settings-close:hover {
  color: var(--color-foreground-brighter);
}

.settings-body {
  padding: var(--space-6) var(--space-18) var(--space-30);
}

.settings-section {
  padding: var(--space-15) 0;
  border-bottom: 1px solid var(--color-background-brighter);
}

.settings-section:last-child {
  border-bottom: 0;
}

.settings-section h4 {
  margin: 0 0 var(--space-9) 0;
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-darker);
}

.settings-section select {
  width: 100%;
}

/* ─── NAV-STYLE DROPDOWN TRIGGERS (period, entity, compare) ─ */
/* Borderless nav-dropdown look for the period button, entity select, and the
 * dashboard period/compare buttons — no border/outline in any state. */
.nav-trigger,
.nav-trigger:hover,
.nav-trigger:focus,
.nav-trigger:focus-visible,
.nav-trigger:active {
  border: 0;
  outline: 0;
  box-shadow: none;
  background-color: transparent;
}

.nav-trigger {
  font-family: var(--font-family-01);
  font-size: var(--fs-small);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground);
  cursor: pointer;
  padding: var(--space-3) var(--space-6);
  transition: color 0.2s ease;
}

.nav-trigger:hover {
  color: var(--color-foreground-brighter);
}

/* Native <select> variant — keep the dropdown arrow, drop border/box, and
 * cancel the design system's focus size-bump. */
select.nav-trigger {
  padding: var(--space-3) var(--space-24) var(--space-3) var(--space-6);
  min-width: auto;
  color: var(--color-foreground);
}

select.nav-trigger:focus {
  font-size: var(--fs-small);
  font-weight: 400;
  color: var(--color-foreground-brighter);
}

/* Dropdown chevron (matches the ehsto.dev select arrow). Scales with the
 * trigger's font-size via em units; muted like the dev arrow. */
.chev {
  color: var(--color-foreground);
  margin-left: 0.6em;
  vertical-align: middle;
}

/* FY selector shown at h1 scale inside the dataset card body (Transactions),
 * the shared dash-head (Dashboard + P&L), and the Export grid. */
#tx-period,
.dash-head__left .period-trigger,
.exp-grid .period-trigger,
.visuals-hero__controls .period-trigger {
  font-size: 2.1rem;
  font-weight: 600;
  line-height: 1.3;
  text-transform: none;
  letter-spacing: 0;
  color: var(--color-foreground-brighter);
  align-self: flex-start;
  padding: 0;
  margin-bottom: 0;
}

@media (min-width: 768px) {

  #tx-period,
  .dash-head__left .period-trigger,
  .exp-grid .period-trigger,
  .visuals-hero__controls .period-trigger {
    font-size: var(--fs-h1);
  }
}

/* Custom date ranges carry a longer YYMMDD—YYMMDD label; shrink the dashboard
   period button so it fits. Only when the period is a custom range (id beats the
   .dash-head__left .period-trigger size at every width). */
#dash-period.period-trigger--custom,
#vis-period.period-trigger--custom {
  font-size: 30px;
}

/* The chevron scales with font-size; at h1 that's oversized — halve it. */
#tx-period .chev,
.dash-head__left .period-trigger .chev,
.exp-grid .period-trigger .chev,
.visuals-hero__controls .period-trigger .chev {
  width: 0.32em;
  height: auto;
  margin-left: var(--space-21);
}

/* Entity selector sits below the period trigger in the dash-head, with an
 * "Entity" label at the same size as the selector text. */
.dash-head__entity {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

.dash-head__entity-label {
  font-family: var(--font-family-01);
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground);
  opacity: 0.8;
}

.dash-head__entity .entity-trigger {
  font-size: 15px;
}

/* Transactions dataset card reuses the dash-head left-column content but lays
   out independently (single-column card in the two-up tx-cards row). */
.tx-dataset {
  display: flex;
  flex-direction: column;
  gap: var(--space-18);
}

/* ─── RESPONSIVE / MOBILE ────────────────────────────────── */
/* Keep every fixed pop-over within the viewport and let tall ones scroll,
 * so nothing runs off the right edge or below the fold on small screens.
 * (mobile-first: these caps only bite on narrow viewports) */
.col-menu,
.period-menu,
.date-pop,
.settings-panel {
  max-width: calc(100vw - 12px);
}

.col-menu,
.period-menu,
.date-pop {
  max-height: 80vh;
  overflow-y: auto;
}

/* Header filters stay on one line (they scroll within their own area if the
 * screen is too tight — see .header-filters overflow-x). */
.header-filters {
  flex-wrap: nowrap;
}

.header-filters .nav-trigger {
  flex: 0 0 auto;
  white-space: nowrap;
  font-family: var(--font-family-01);
  font-size: 0.66rem;
  font-weight: 200;
  letter-spacing: 0.09em;
}

/* Keep the wordmark on one line; let the header actions wrap instead. */
.brand {
  white-space: nowrap;
}

/* ─── HEADER LAYOUT (brand · filters · cog) ──────────────── */
/* Mobile-first: brand (with .ehsto.com stacked below) + cog on the top row,
 * the period/entity filters on their own full-width row beneath. At >=768px
 * everything sits inline. Theme toggle now lives in Settings. */
/* Single row: brand · filters (fill the middle, scroll if tight) · cog.
 * The .ehsto.com secondary shows whenever it fits; JS (fitBrand) hides it only
 * when the filters would otherwise be clipped. */
header {
  flex-wrap: nowrap;
}

.brand {
  flex: 0 0 auto;
  white-space: nowrap;
}

.settings-cog {
  flex: 0 0 auto;
}

/* Active theme button in the settings panel. */
[data-set-theme].is-active {
  border-color: var(--color-accent-01);
  color: var(--color-foreground-brighter);
}

/* Selected theme icon in the settings panel. */
.settings-section .theme-toggle button.is-active {
  color: var(--color-foreground);
  opacity: 1;
}

/* ─── RADIO / CHECKBOX (custom, consistent everywhere) ───── */
/* Standard: 1px foreground border on a background-darker fill; border brightens
 * on hover; when checked a radio gets a foreground dot and a checkbox a
 * foreground tick. */
input[type="checkbox"],
input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  min-width: 12px;
  margin: 0;
  flex: 0 0 auto;
  border: 1px solid var(--color-foreground-darker);
  background-color: var(--color-background-darker);
  cursor: pointer;
  position: relative;
  transition: border-color 0.2s ease;
}

input[type="radio"] {
  border-radius: 50%;
  width: 9px;
  height: 9px;
  min-width: 9px;
}

input[type="checkbox"] {
  border-radius: 0px;
}

input[type="checkbox"]:hover,
input[type="radio"]:hover {
  border-color: var(--color-foreground-brighter);
}

/* Checked radio — a solid foreground dot. */
input[type="radio"]:checked::after {
  content: "";
  position: absolute;
  inset: 1px;
  border-radius: 50%;
  background-color: var(--color-foreground);
  border-color: var(--color-foreground-brighter);
}

/* Checked checkbox — a foreground tick. */
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 3px;
  top: -3px;
  width: 3px;
  height: 10px;
  border: solid var(--color-foreground);
  border-width: 0 1px 1px 0;
  transform: rotate(45deg);
}

/* ─── UNIFORM TABLE ROW HEIGHT ───────────────────────────── */
/* Every table row (header + body, all tables) is a fixed 30px. The vertical
 * cell padding is removed so the height actually holds — content stays centred
 * via the default vertical-align: middle. Horizontal padding is untouched. */
table tr {
  height: 30px;
}

table th,
table td {
  padding-top: 0;
  padding-bottom: 0;
}

/* Entities list sits above the Add-entity card — give it breathing room. */
#ent-list table {
  margin-bottom: var(--space-30);
}

/* ─── Dash head: period + entity + region info (left) · caption over the
   metric grid (right). Shared by the Dashboard and P&L views. ─ */
.dash-head {
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .dash-head {
    grid-template-columns: minmax(300px, 1fr) 2fr;
    align-items: start;
    gap: var(--space-18);
  }
}

.dash-head__left {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-21);
  margin: var(--space-18) 0 0 var(--space-15);
}

.dash-head__main {
  display: flex;
  flex-direction: column;
  margin-top: var(--space-18);
}

@media (min-width: 768px) {
  .dash-head__main {
    margin-top: 0;
  }
}

/* Host override: a hero-section inside the app's view panel subtracts the app
   chrome so one hero fills the screen without scrolling. When the tabs sit below
   the header (above the panel) the chrome includes the tab row; when they're in
   the header (body.tabs-in-head) it doesn't. Padding scales with width. Nudge
   these if the header/footer/tab heights change. */
#view-panel .hero-section {
  --hero-chrome: 136px;   /* header + tab row + footer + main padding */
  padding: 0;             /* mobile: framed columns use the full width */
}

/* Tabs in the header → the panel has no tab row above it. */
body.tabs-in-head #view-panel .hero-section {
  --hero-chrome: 96px;
}

@media (min-width: 768px) {
  #view-panel .hero-section {
    padding: var(--space-21);
  }
}

@media (min-width: 1151px) {
  #view-panel .hero-section {
    padding: var(--space-42);
  }
}

/* ─── Setup view — matches the Export hero ──────────────────── */
/* Two full-height sections: copy left, a bordered card right (folder tree +
   actions in section 1, entities list + add form in section 2). Content is
   vertically centred, exactly like the Export view. */
.setup-view .hero-section__content {
  max-width: 640px;
}

.setup-view .card {
  margin-bottom: var(--space-18);
}

.setup-view .card:last-child {
  margin-bottom: 0;
}

/* The entity form lives inside the bordered right column — drop its frame to
   avoid boxes-within-boxes (as on Export). */
.setup-tree {
  width: 100%;
  background: var(--color-background-darker);
  margin-bottom: var(--space-42);
}

.setup-sec--folder .ftree-ascii {
  margin-bottom: var(--space-15);
}

/* Add-entity form sits below the list, always visible (no collapsing card). */
.ent-form-block {
  margin-top: var(--space-42);
}

.ent-form-block .card__title {
  margin: 0 0 var(--space-12) 0;
}

/* Entity list — one flex card per entity. Single column on mobile; two columns
   (e.g. a business card + a personal card side by side) on desktop. */
.ent-cards {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  margin-top: var(--space-9);
}

@media (min-width: 768px) {
  .ent-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-18);
    margin-top: var(--space-21);
  }
}

.ent-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-foreground-darker);
}

/* Card header: "ENTITY 01" on the left, edit/delete grouped on the far right,
   with a full-width divider beneath (its border connects to the card edges
   because the card itself has no padding — each row pads its own content). */
.ent-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-12);
  padding: var(--space-12) var(--space-21);
  border-bottom: 1px solid var(--color-background-brighter);
}

.ent-card__title {
  font-family: var(--font-family-01);
  text-transform: uppercase;
  font-size: var(--fs-small);
  letter-spacing: 0.06rem;
  color: var(--color-foreground-brighter);
}

.ent-card__actions {
  display: flex;
  gap: var(--space-6);
  flex: 0 0 auto;
}

/* Drop the shared icon padding here so the bin sits flush at the card edge. */
.ent-card__actions .tx-del {
  padding: 0;
}

/* Field row: fixed-width label, then the value — both left-aligned. Label uses
   the same style as the add-entity form labels for consistency. */
.ent-card__field {
  display: flex;
  gap: var(--space-12);
  align-items: baseline;
  padding: var(--space-6) var(--space-21);
}

.ent-card__head + .ent-card__field {
  padding-top: var(--space-21);
}

.ent-card__field:last-child {
  padding-bottom: var(--space-12);
}

.ent-card__label {
  flex: 0 0 auto;
  width: 56px;
  color: var(--color-foreground);
  opacity: 0.9;
  font-family: var(--font-family-01);
  text-transform: uppercase;
  font-size: var(--fs-small);
  letter-spacing: 0.06rem;
}

.ent-card__value {
  min-width: 0;
  font-family: var(--font-family-02);
  font-size: var(--fs-table);
  word-break: break-word;
}

/* Inline edit: the input/select fills the value side of the field row. */
.ent-card__input {
  flex: 1 1 auto;
  min-width: 0;
}

/* ABN is greyed while Type is Personal (in both the card edit + the add form). */
.ent-card__input:disabled,
.ent-form__field input:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.ent-card--editing {
  border-color: var(--color-foreground-darker);
}

/* Bulk-editor showcase in Setup: a static example rule + a CTA to Transactions. */
.bulk-demo {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-18);
  width: 100%;
}

/* Bulk-editor grammar reference: When (field → operators) | Then (Set · options
   · To · options). Auto layout so the label columns stay tight. Scrolls
   horizontally on narrow screens rather than wrapping any text. */
.bulk-ref-scroll {
  width: 100%;
  overflow-x: auto;
}

.bulk-ref {
  width: 100%;
  margin: 0;
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
}

.bulk-ref th,
.bulk-ref td,
.bulk-ref .bulk-ref__opt,
.bulk-ref div {
  white-space: nowrap;
}

.bulk-ref th,
.bulk-ref td {
  padding: var(--space-12) var(--space-15);
  text-align: left;   /* override the design-system last-cell centring */
}

/* The When / Then group headers are centred over their columns, and split the
   table 50/50 (each spans two sub-columns). */
.bulk-ref th {
  text-align: center;
  width: 50%;
}

.bulk-ref td {
  vertical-align: middle;
  line-height: 1.5;
}

/* Stronger vertical divide between the When and Then groups (right edge of the
   When columns, in both the header and the body). */
.bulk-ref thead th:first-child,
.bulk-ref tbody td:nth-child(2) {
  border-right: 1px solid var(--color-foreground-darker);
}

/* Space between the header row and the content row. */
.bulk-ref tbody td {
  padding-top: var(--space-15);
}

.bulk-ref__field {
  white-space: nowrap;
}

/* Eyebrow label above the reference table. */
.bulk-ref__eyebrow {
  font-family: var(--font-family-01);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground);
}

/* Animated option items: the cycle brightens one When field + its operators at
   a time; the rest are dimmed for contrast. */
.bulk-ref__opt {
  opacity: 0.4;
  transition: opacity 0.6s ease-in-out, color 0.6s ease-in-out;
}

.bulk-ref__opt.is-hl {
  opacity: 1;
  color: var(--color-foreground-brighter);
}

/* Bold Set: / To: subheading inside the Then columns. */
.bulk-ref__sub {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-brighter);
  margin-bottom: var(--space-6);
}

/* Scripted rule-builder demo — reuses the real .rule-builder markup + styling.
   Playback only (no user interaction); the dropdown menu is anchored inside the
   card instead of the shared fixed popover. */
.rule-demo {
  position: relative;
  pointer-events: none;
}

.rule-demo__menu {
  position: absolute;
  min-height: 0;
  max-height: 130px;
  overflow: hidden;
}

/* Keep the preview line in flow when empty so the demo height never jumps. */
#rule-demo-preview.is-empty {
  visibility: hidden;
}

#rule-demo-btn.is-press {
  background: var(--color-accent-01);
  border-color: var(--color-accent-01);
  color: var(--color-background);
}

/* Supporting-documents right column: heading + linked count on one row. */
.setup-docs__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-12);
  margin-bottom: var(--space-12);
}

.setup-docs__head .card__title {
  margin: 0;
}

/* Data-folder view in the Connect panel — a horizontal ASCII tree: the root
   branches down to each FY_ folder, which branches right to its subfolders.
   Rendered as monospace <pre> so the connectors align exactly. */
/* Connected data folder's root name — green to signal the live connection. */
.ftree-root {
  color: var(--color-accent-01);
}

.ftree-ascii {
  /* Shrink-wrap to the art's width and centre the whole block within its
     container (centring per-line would break the box-drawing alignment).
     On a narrow screen the art is wider than the viewport, so max-width +
     overflow give it the full width with horizontal scroll. */
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-foreground);
  background: var(--color-background-darker);
  padding: var(--space-18);
  white-space: pre;
  overflow-x: auto;
}

@media (min-width: 768px) {
  .ftree-ascii {
    padding: var(--space-60) 0;
  }
}

/* ─── Export view ───────────────────────────────────────────── */
/* Built on the reusable .hero-section primitive (hero-section__title / __body /
   __eyebrow live with the component in style.css). */

/* Export right column: a label | value grid (table-like) for Period, Entity
   and Files. Labels top- and right-aligned against the value column. All
   line-heights are 1 and paddings zeroed so the Entity row (label + value both
   15px) lines up exactly; the Period and Files rows add a small top offset to
   the label to meet their larger value / button text. Values use the Arial
   stack, so these offsets are stable across browsers. */
.exp-grid {
  display: grid;
  /* Mobile: narrower label column + tighter horizontal gap so the value
     column keeps as much width as possible. Restored at ≥768px below. */
  grid-template-columns: 1fr 5fr;
  gap: var(--space-30) var(--space-18);
  align-items: start;
}

.exp-grid > .dash-head__entity-label {
  margin: 0;
  text-align: right;
  line-height: 1;
  font-size: 10px;   /* mobile; 15px from ≥768px */
}

@media (min-width: 768px) {
  .exp-grid {
    grid-template-columns: 1fr 3fr;
    gap: var(--space-30) var(--space-30);
  }
  .exp-grid > .dash-head__entity-label {
    font-size: 15px;
  }
}

.exp-grid__value {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-30);
  min-width: 0;
}

.exp-grid .period-trigger,
.exp-grid .entity-trigger {
  padding: 0;
  margin: 0;
  line-height: 1;
}

.exp-grid .entity-trigger {
  font-size: 15px;
}

/* Period value is h1-scale; drop its label to the FY cap line. */
.exp-grid__period-label {
  padding-top: 3px;
}

@media (min-width: 768px) {
  .exp-grid__period-label {
    padding-top: 5px;
  }
}

/* File buttons centre their text inside padding + a border; drop the label to
   the first button's text. */
.exp-grid__files-label {
  padding-top: 6px;
}

/* The Files value cell reuses .exp-actions but without its standalone top gap. */
.exp-grid__value.exp-actions {
  margin: 0;
  gap: var(--space-21);
}

/* Column of download buttons in the Export view. */
.exp-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-30);
  margin: var(--space-30) 0 0 0;
  padding: 0;
  max-width: 900px;
}

/* ─── Evidence column + attachment menu ─────────────────────── */
.tx-ev-cell {
  text-align: center;
  white-space: nowrap;
}

.tx-ev-head {
  text-align: center;
}

.tx-evidence {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-6);
  border: 0px solid var(--color-background-brighter);
  background: transparent;
  color: var(--color-foreground-darker);
  cursor: pointer;
  transition: all 0.2s ease;
}

.tx-evidence:hover {
  color: var(--color-foreground-brighter);
}

.tx-evidence.has-att {
  color: var(--color-accent-01);
}

.tx-ev__count {
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
}

.att-menu {
  min-width: 260px;
  max-width: 360px;
  padding: var(--space-6);
}

.att-menu__section+.att-menu__section {
  margin-top: var(--space-9);
  padding-top: var(--space-9);
  border-top: 1px solid var(--color-background-brighter);
}

.att-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.att-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-9);
}

.att-item__name {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
}

.att-item__type {
  flex: none;
  padding: 0 var(--space-6);
  border: 1px solid var(--color-background-brighter);
  color: var(--color-foreground-darker);
  font-family: var(--font-family-01);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.04rem;
}

.att-item__actions {
  flex: none;
  display: flex;
  gap: var(--space-6);
}

.att-cell__actions {
  display: flex;
  gap: var(--space-6);
}

.att-act {
  border: none;
  background: transparent;
  color: var(--color-accent-01);
  cursor: pointer;
  font-size: var(--fs-small);
  padding: 0;
}

.att-act:hover {
  text-decoration: underline;
}

.att-act--danger {
  color: var(--color-accent-02);
}

.att-pick {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

.att-empty {
  color: var(--color-foreground-darker);
  font-size: var(--fs-small);
}

.att-hint {
  line-height: 1.5;
}

.att-missing {
  padding: 0 var(--space-6);
  border: 1px solid var(--color-accent-03);
  color: var(--color-accent-03);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.04rem;
}

.att-row--missing td {
  color: var(--color-foreground-darker);
}

/* Add-entity form: three fields across on desktop, stacked on mobile. */
.ent-form {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6) var(--space-18);
}

/* Mobile: each field is a two-column row — label left, input right, both
   left-aligned (mirrors the entity cards above). Stacked again on desktop. */
.ent-form__field {
  display: grid;
  grid-template-columns: 1fr 5fr;
  align-items: center;
  gap: var(--space-12);
}

/* In the 2-col grid the label's own (stacked-layout) margins misplace it —
   pin it flush left and drop those margins. Restored on desktop below. */
.ent-form__field label {
  margin: 0;
  text-align: left;
  justify-self: start;
}

.ent-form__field > input,
.ent-form__field > select {
  width: 100%;
  min-width: 0;
}

@media (min-width: 768px) {
  .ent-form {
    grid-template-columns: 1fr 1fr 1fr;
  }
  .ent-form__field {
    display: flex;
    flex-direction: column;
    align-items: stretch;   /* reset the mobile grid's centring so labels sit left */
    gap: 0;
  }
  .ent-form__field label {
    margin: var(--space-12) 0 var(--space-6);
    justify-self: auto;
  }
}

.connect__ok {
  color: var(--color-accent-01);
}

.connect__warn {
  color: var(--color-accent-03);
}

/* ─── VISUALS (aerial ocean) ─────────────────────────────── */
/* Full-bleed WebGL canvas. The renderer is sandboxed to this view (see
   stopVisuals in app.js); this is only its box. */
.ocean-view {
  position: relative;
}

.ocean-canvas {
  display: block;
  width: 100%;
  height: min(62vh, 660px);
  min-height: 340px;
  background-color: #0a1522;
}

/* Visuals is a single hero-section. Two layouts:
   • MOBILE (base): a natural vertical stack in the order pickers → wave-field →
     legend, at content height, page scrolls (see the display:contents block below).
   • DESKTOP (≥768): locked to the viewport height, side by side — the right
     (ocean) column fills it, the left column (pickers + legend) scrolls internally.
   The viewport-fill / internal-scroll / height:100%-canvas behaviours are all
   gated to ≥768 so they never leak into the mobile stack. */
.visuals-hero {
  min-height: 0;   /* mobile: content height (overrides hero-section--fill) */
}

/* Mobile: the ocean column  Ordering is handled below. */
.visuals-hero__ocean {
  padding: var(--space-21) var(--space-18);
}

/* Scope controls (period + entity + context) — the dash-head__left layout
   without its outer top/left margins. */
.visuals-hero__controls {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-21);
}

/* Left column: content flows top-aligned. */
.visuals-hero__left {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
}

/* Right (ocean) column: content flows top-aligned, centred horizontally. */
.visuals-hero__ocean {
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
}

/* The wave-field (ocean canvas + timeline). Mobile: natural height — the canvas
   uses its own min(62vh,660px); the timeline sits beneath. */
.wave-field-container {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-9);
}

/* Mobile ordering: pickers → wave-field → legend. The pickers and legend both
   live in the left column, so it (and its content wrapper) collapse to
   display:contents — their children become direct flex items of the hero and can
   be ordered around the ocean column. The collapsed column no longer supplies the
   side padding, so the pickers + legend carry their own. */
.visuals-hero__left,
.visuals-hero__left > .hero-section__content {
  display: contents;
}
.visuals-hero__controls  { order: 1; padding: var(--space-21) var(--space-18) 0; }
.visuals-hero__ocean     { order: 2; }
.visuals-hero .wf-legend { order: 3; padding: 0 var(--space-18) var(--space-21); }

@media (min-width: 768px) {
  .visuals-hero {
    height: calc(100vh - var(--hero-chrome));
    min-height: calc(100vh - var(--hero-chrome));
  }
  /* Restore the single scrollable left column; drop the mobile ordering/padding. */
  .visuals-hero__left { display: flex; }
  .visuals-hero__left > .hero-section__content { display: block; }
  .visuals-hero__controls,
  .visuals-hero .wf-legend { order: 0; padding: 0; }
  .visuals-hero__ocean { order: 0; }
  .visuals-hero__left,
  .visuals-hero__ocean {
    padding: var(--space-30);
  }
  .visuals-hero__left {
    overflow-y: auto;   /* pickers + legend can exceed the locked height */
  }
  .visuals-hero__ocean {
    overflow: hidden;   /* fixed, never scrolls */
  }
  /* Canvas flexes to fill the locked column height. */
  .wave-field-container {
    flex: 1 1 auto;
    min-height: 0;
    max-width: 1200px;
  }
  .wave-field-container .ocean-view {
    flex: 1 1 auto;
    min-height: 0;
  }
  .wave-field-container .ocean-canvas {
    height: 100%;
    min-height: 0;
  }
}

/* Wave-field legend — collapsible wave-physics explanations of the mapping. */
.wf-legend {
  width: 100%;
  margin-top: var(--space-24);
}

.wf-legend__title {
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06rem;
  color: var(--color-foreground-darker);
  margin-bottom: var(--space-9);
}

.wf-item {
  border-top: 1px solid var(--color-background-brighter);
}

.wf-item:last-child {
  border-bottom: 1px solid var(--color-background-brighter);
}

.wf-item > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-9);
  padding: var(--space-9) 0;
  color: var(--color-foreground);
  font-size: var(--fs-body);
  transition: color 0.2s ease;
}

.wf-item > summary::-webkit-details-marker {
  display: none;
}

.wf-item > summary::after {
  content: '+';
  color: var(--color-foreground-darker);
  font-size: 1rem;
  line-height: 1;
}

.wf-item[open] > summary::after {
  content: '−';
}

.wf-item > summary:hover {
  color: var(--color-foreground-brighter);
}

.wf-item p {
  margin: 0 0 var(--space-12);
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-foreground);
  opacity: 0.8;
}

/* Sits in the flow just above the frame (a header line for the wave-field). */
.ocean-label {
  font-family: var(--font-family-02);
  font-size: var(--fs-small);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-foreground-darker);
  pointer-events: none;
}

/* Timeline scrubber (play/pause + week slider) and month drill-in chips. */
.ocean-controls {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin-top: 0;
}

/* Wave-line strip — a living side-on surface profile drawn on its own canvas
   above the cross-section, synced to the wave-field's animation clock. */
.ocean-waveline {
  position: relative;
  width: 100%;
  height: 46px;
}

.oxs-wave {
  display: block;
  width: 100%;
  height: 100%;
}

/* Cross-section — the side-on elevation of the same ocean, sharing the ruler.
   A flat surface line up top; the seabed (the FY running balance) hangs below,
   the gap between them being the water depth; where the seabed climbs to the
   surface the wave-field breaks. Drawn to the ruler's own xOf scale so x aligns
   exactly with the ticks + playhead; strokes are non-scaling so they stay 1px. */
.ocean-section {
  position: relative;
  width: 100%;
  height: 42px;
}

.oxs {
  display: block;
  width: 100%;
  height: 100%;
}

.oxs__surface {
  stroke: var(--color-foreground-darker);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.oxs__zero {
  stroke: var(--color-foreground-darker);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0.5;
  vector-effect: non-scaling-stroke;
}

.oxs__bed {
  fill: none;
  stroke: var(--color-foreground);
  stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
}

.oxs__water { fill: rgba(var(--color-foreground-rgb), 0.05); }

.oxs__earth { fill: rgba(var(--color-foreground-rgb), 0.12); }

.oxs-head {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  margin-left: -1px;
  left: 0;
  background-color: var(--color-foreground-brighter);
  pointer-events: none;
}

.oxs-band {
  position: absolute;
  top: 0;
  bottom: 0;
  background-color: rgba(var(--color-foreground-rgb), 0.07);
  border-left: 1px solid var(--color-foreground);
  border-right: 1px solid var(--color-foreground);
  pointer-events: none;
}

/* Transport: prev / play / next as a connected, square (no radius) button group,
   in the ehsto utility style — 1px borders, flat surfaces, subtle hover. */
.ocean-bar {
  display: flex;
  align-items: center;
  margin: var(--space-21) 0 0 0;
}

.ocean-btn {
  flex: 0 0 auto;
  width: 34px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--color-foreground-darker);
  background-color: transparent;
  color: var(--color-foreground);
  cursor: pointer;
  font-size: 0.8rem;
  line-height: 1;
  transition: all 0.2s ease;
}

.ocean-btn + .ocean-btn {
  border-left: 0;
}

.ocean-btn:hover {
  color: var(--color-foreground-brighter);
  background-color: var(--color-background-brighter);
}

/* Timeline ruler: day ticks hang DOWN from a top line (a repeating gradient, so
   it scales to any span with no per-day DOM); the 1st of each month is a taller
   major tick; a progress overlay lights the ticks in foreground-brighter up to
   the playhead; plus an active-month band, the playhead line, and centred month
   labels beneath. */
.ocean-timeline {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.otl-track {
  position: relative;
  height: 22px;
  border-top: 1px solid var(--color-background-brighter);
  cursor: pointer;
  touch-action: none;
  background-image: repeating-linear-gradient(90deg,
    var(--color-background-brighter) 0 1px, transparent 1px calc(100% / var(--otl-days)));
  background-position: left top;
  background-size: 100% 5px;
  background-repeat: no-repeat;
}

/* Bright copy of the ticks, clipped to the playhead position (the progress fill). */
.otl-progress {
  position: absolute;
  inset: 0;
  pointer-events: none;
  clip-path: inset(0 100% 0 0);
  background-image: repeating-linear-gradient(90deg,
    var(--color-foreground-brighter) 0 1px, transparent 1px calc(100% / var(--otl-days)));
  background-position: left top;
  background-size: 100% 5px;
  background-repeat: no-repeat;
}

/* Major tick — the 1st of each month, taller, hanging from the top line. */
.otl-major {
  position: absolute;
  top: 0;
  height: 13px;
  width: 1px;
  margin-left: -0.5px;
  background-color: var(--color-foreground-darker);
}

.otl-progress .otl-major {
  background-color: var(--color-foreground-brighter);
}

.otl-band {
  position: absolute;
  top: 0;
  bottom: 0;
  background-color: rgba(var(--color-foreground-rgb), 0.07);
  border-left: 1px solid var(--color-foreground);
  border-right: 1px solid var(--color-foreground);
  pointer-events: none;
}

.otl-head {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 2px;
  margin-left: -1px;
  left: 0;
  background-color: var(--color-foreground-brighter);
  pointer-events: none;
}

.otl-labels {
  position: relative;
  height: 15px;
}

.otl-month {
  position: absolute;
  top: 0;
  border: 0;
  background-color: transparent;
  color: var(--color-foreground-darker);
  font-family: var(--font-family-01);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.05rem;
  text-align: center;
  padding: 0;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  transition: color 0.2s ease;
}

.otl-month:hover {
  color: var(--color-foreground);
}

.otl-month.is-active {
  color: var(--color-foreground-brighter);
}

.otl-month[disabled] {
  cursor: default;
  opacity: 0.5;
}

