/* ==========================================================================
   DulyKept, one committed stylesheet. No build step, no CDN, no dark mode.

   "Chartroom": a dark instrument housing around a bright, tinted work table.

   The primary #506E7D is structure rather than decoration. It owns the
   sidebar, the borders, the shadows, the wash on the canvas and the fill of
   every meter, so the app reads as one continuous material instead of white
   boxes floating on grey. That structural blue-grey is deliberately
   unemotional, which is what buys the status colours their authority: when a
   workflow is stalled, the amber chip is the only warm thing in the data
   region and it lands.

   Surface logic, in five lines:
     1. Chrome is dark. The sidebar is the housing.
     2. The canvas between chrome and paper is tinted (#E7EDF1).
     3. Paper is white. Every number, name and date sits on paper.
     4. Depth, gradient and glass live in the chrome and on card headers.
     5. Lines are brand-tinted, never neutral grey; shadows carry a 1px seam.

   Orange marks locus, never state. It says where you are (active nav), what
   comes next (the queue heading), and when now is (the current month in the
   anniversary heatmap). It never appears beside a status chip, because at
   roughly 25 degrees from the amber "needs review" it would be misread.

   Order: tokens, reset, utilities, chrome, feedback, cards, tables, chips,
   buttons, forms, tiles, meters, timeline, calendar, lists, agent, editor,
   import, settings, overlays, empties, htmx, pages, responsive, motion,
   print.
   ========================================================================== */

:root {
  /* --- primary ramp, derived from #506E7D -------------------------------- */
  --brand-950: #131C21;
  --brand-900: #1B2831;
  --brand-850: #22313A;
  --brand-800: #2E414B;   /* 10.6:1 on white */
  --brand-700: #3E5661;   /*  7.8:1 on white, links and headings */
  --brand-600: #47616E;
  --brand:     #506E7D;   /*  5.4:1 on white, safe as text and as fill */
  --brand-500: #62889A;   /*  3.8:1 on white, 3.2:1 on canvas, focus ring */
  --brand-400: #86A6B5;
  --brand-300: #6F90A0;
  --brand-200: #B9C9D2;
  --brand-150: #CBDAE2;
  --brand-100: #DCE6EB;
  --brand-050: #F1F5F7;
  --brand-025: #F7FAFB;

  /* --- accent: orange, rationed by category and not by quantity ---------- */
  --accent:      #E0722B;   /* 3.2:1, graphics only */
  --accent-600:  #D2661F;
  --accent-700:  #B85C14;   /* 4.6:1, the only orange used as text */
  --accent-100:  #FBE7D6;
  --accent-050:  #FDF5EE;
  --accent-glow: rgba(224, 114, 43, .32);

  /* --- ink --------------------------------------------------------------- */
  --ink:          #17222A;
  --ink-2:        #4A5C68;   /* 5.9:1 on the canvas, safe anywhere */
  --ink-3:        #647784;   /* 4.7:1 on white, inside cards only */
  --ink-3-canvas: #566975;   /* 4.8:1 on the canvas, meta text outside cards */
  --ink-4:        #8A9AA6;   /* decorative marks only, never text */
  --muted:        #8A9AA6;

  /* --- four surfaces, all cool ------------------------------------------- */
  --canvas:    #E7EDF1;   /* the work table */
  --canvas-2:  #DFE7EC;   /* sunken: wells, inputs at rest, editor gutters */
  --surface:   #FFFFFF;   /* paper: cards, rows, table body */
  --surface-2: #F6F9FB;   /* card headers, zebra */

  /* --- lines: brand-tinted, never neutral grey --------------------------- */
  --line:        #DEE7EC;
  --line-2:      #EAF0F4;
  --line-strong: #C2D2DB;

  /* --- dark chrome: the thematic shade ----------------------------------- */
  /* #30515F is lighter than a true navy, so the dim text has to be lifted
     with it. #BECDD5 clears 4.6:1 against the lightest stop of the sidebar
     gradient and 6.1:1 against the darkest. */
  --sb-bg:       #30515F;
  --sb-text:     #DCE7EC;                 /* 7.4:1 on --sb-bg */
  --sb-text-dim: #BECDD5;                 /* 4.6:1 on the lightest stop */
  --sb-line:     rgba(255, 255, 255, .085);
  --sb-hover:    rgba(255, 255, 255, .06);
  --sb-active:   rgba(255, 255, 255, .10);

  /* --- status: mark for fills, base for text, line for edges ------------- */
  --ok:   #17754A;  --ok-mark:   #1E8E5A;  --ok-bg:   #E7F3ED;  --ok-line:   #B7DCC8;
  --warn: #8A5B07;  --warn-mark: #D9880A;  --warn-bg: #FBF0DC;  --warn-line: #EFD79B;
  --bad:  #B03030;  --bad-mark:  #CF4040;  --bad-bg:  #FAE9E9;  --bad-line:  #EEBDBD;

  /* Two extra families the annual cycle needs. Signature traffic is purple
     because it is neither done nor late, it is with someone else; filing and
     invoicing are teal because they are the registry half of the cycle.
     #6A45A8 is 6.8:1 on white and 5.9:1 on its own tint; #0F6E6E is 5.5:1 on
     white and 4.8:1 on its own tint. */
  --purple: #6A45A8;  --purple-mark: #7B57BC;  --purple-bg: #F2ECFA;  --purple-line: #DCCDF1;
  --teal:   #0F6E6E;  --teal-mark:   #14918F;  --teal-bg:   #E3F2F1;  --teal-line:   #B5DCDA;

  /* --- shadows, tinted with the chrome colour ---------------------------- */
  --shadow-1: 0 1px 2px rgba(27, 40, 49, .06), 0 1px 3px rgba(27, 40, 49, .08);
  --shadow-2: 0 2px 4px rgba(27, 40, 49, .06), 0 8px 20px rgba(27, 40, 49, .10);
  --shadow-3: 0 4px 10px rgba(27, 40, 49, .08), 0 20px 48px rgba(27, 40, 49, .18);
  --shadow-brand: 0 1px 2px rgba(27, 40, 49, .22), 0 4px 12px rgba(80, 110, 125, .28);
  /* A 1px light top edge. On a tinted canvas a shadow alone reads as blur;
     shadow plus seam reads as a physical object catching light from above. */
  --seam: inset 0 1px 0 rgba(255, 255, 255, .9);
  --ring: 0 0 0 3px rgba(80, 110, 125, .22);

  /* --- gradients, named so they cannot proliferate ----------------------- */
  /* Subtle: about a 5% luminance travel, a hair off-vertical so the falloff
     does not read as mechanical. */
  --g-sidebar:   linear-gradient(176deg, #365969 0%, #30515F 55%, #294654 100%);
  --g-card-head: linear-gradient(180deg, #F2F7FA 0%, #FFFFFF 100%);
  --g-tile:      linear-gradient(158deg, #FFFFFF 0%, #F1F6F9 100%);
  --g-primary:   linear-gradient(180deg, #587686 0%, #46626F 100%);
  --g-primary-h: linear-gradient(180deg, #62828F 0%, #4E6C7B 100%);
  --g-btn:       linear-gradient(180deg, #FFFFFF 0%, #F7FAFB 100%);
  --g-btn-h:     linear-gradient(180deg, #FFFFFF 0%, #EFF5F8 100%);
  --g-progress:  linear-gradient(90deg, #3E5661 0%, #62889A 100%);
  --g-wash:      radial-gradient(1100px 520px at 6% -10%, rgba(80, 110, 125, .16), transparent 62%);
  --g-dots:      radial-gradient(rgba(80, 110, 125, .15) 1px, transparent 1px);

  /* --- geometry ---------------------------------------------------------- */
  --radius: 12px;
  --radius-sm: 8px;
  --radius-xs: 6px;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px;

  --font: system-ui, -apple-system, "Segoe UI", Roboto, Inter, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --t: 150ms cubic-bezier(.2, .7, .3, 1);

  --sidebar-w: 236px;
  --rail-w: 60px;
  --topbar-h: 58px;
}

/* ==========================================================================
   Reset and primitives
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font: 400 14px/1.5 var(--font);
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* pre is in this list because every code and payload block in the app is a
   <pre>, and the browser's own 1em above and below it reads as a stray gap
   inside a card. */
h1, h2, h3, h4, h5, p, pre, dl, dd, figure, fieldset { margin: 0; }
fieldset { border: 0; padding: 0; min-width: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { vertical-align: middle; }

a { color: var(--brand-700); text-decoration: none; }
a:hover { color: var(--brand-800); text-decoration: underline; }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
table { border-collapse: collapse; width: 100%; }
code, pre, samp { font-family: var(--mono); }
hr { border: 0; border-top: 1px solid var(--line); margin: var(--s4) 0; }

:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
::selection { background: var(--brand-100); }

/* Columns of numbers have to align. A standalone display number does not,
   and tabular figures make 121 look loose at 30px. */
.table td, .table th, .num, .stat-value, .heat-count, .days-ok, .days-warn,
.days-bad, .nav-count, .progress-num, .pager, .ycell { font-variant-numeric: tabular-nums; }
.tile-value { font-variant-numeric: normal; }

/* ==========================================================================
   Utilities. Small on purpose: if a view needs more than these, the pattern
   belongs in this file as a component.
   ========================================================================== */

[hidden], .hidden { display: none !important; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
  position: absolute; left: -9999px; top: var(--s2); z-index: 100;
  background: var(--surface); padding: 10px 16px; border-radius: var(--radius-sm);
  box-shadow: var(--shadow-3); font-weight: 600;
}
.skip:focus { left: var(--s2); }

.muted { color: var(--ink-3); }
.muted-canvas { color: var(--ink-3-canvas); }
/* Mono text is nearly always a token, a key or a file name, so it breaks rather
   than pushing its container wider than the column it sits in. */
.mono { font-family: var(--mono); font-size: .92em; overflow-wrap: anywhere; }
.small { font-size: 12px; }
.xs { font-size: 11.5px; }
.strong { font-weight: 600; }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.break { word-break: break-word; overflow-wrap: anywhere; }
.center { text-align: center; }
.right { text-align: right; }
/* A number column reads as a column only if the digits line up on the right.
   The table rules below restate this at cell specificity; this one makes the
   same class work on a stat, a repeater cell or a definition value. */
.num { text-align: right; }
.cap { text-transform: capitalize; }
.upper { text-transform: uppercase; letter-spacing: .05em; }

.ok-text { color: var(--ok); }
.warn-text { color: var(--warn); }
.bad-text { color: var(--bad); }
.brand-text { color: var(--brand-700); }
.accent-text { color: var(--accent-700); }

.flex { display: flex; gap: var(--s2); align-items: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.flex-wrap { flex-wrap: wrap; }
.flex-start { align-items: flex-start; }
.flex-end { justify-content: flex-end; }
.flex-1 { flex: 1; min-width: 0; }
.stack { display: flex; flex-direction: column; gap: var(--s2); }
.stack-lg { display: flex; flex-direction: column; gap: var(--s4); }
.row { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--s4); }
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s3); }
.grid-4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--s3); }

.gap-1 { gap: var(--s1); } .gap-2 { gap: var(--s2); } .gap-3 { gap: var(--s3); }
.gap-4 { gap: var(--s4); } .gap-5 { gap: var(--s5); }
.mt-1 { margin-top: var(--s1); } .mt-2 { margin-top: var(--s2); }
.mt-3 { margin-top: var(--s3); } .mt-4 { margin-top: var(--s4); }
.mt-5 { margin-top: var(--s5); }
.mb-1 { margin-bottom: var(--s1); } .mb-2 { margin-bottom: var(--s2); }
.mb-3 { margin-bottom: var(--s3); } .mb-4 { margin-bottom: var(--s4); }
.mb-5 { margin-bottom: var(--s5); }
.m-0 { margin: 0; }

.w-full { width: 100%; }
.narrow { max-width: 620px; }
.readable { max-width: 72ch; }
.top { vertical-align: top; }
.pre-wrap { white-space: pre-wrap; word-break: break-word; }
.pre-line { white-space: pre-line; }

/* Column width hints, put on the th. Every table in the app is auto-layout, so
   these are preferences rather than limits: a column still grows to fit a chip
   or a header word, and the squeeze lands on the one column with no width (the
   name). Pick the step that fits the longest label the column can hold. */
.col-xs { width: 48px; }
.col-sm { width: 96px; }
.col-md { width: 160px; }
.col-lg { width: 280px; }
/* The floor for the column that takes the squeeze. A table with six or seven
   width hints on it has already spent the row, and the name column is what pays:
   below about this width a name and its description stop being a cell and become
   a column of single words. `.table-wrap` scrolls sideways instead. */
.col-grow { width: auto; min-width: 220px; }

/* A footer bar that stays in view while a long preview scrolls. */
.sticky-bar {
  position: sticky; bottom: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  background: rgba(255, 255, 255, .92);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 -1px 0 rgba(255, 255, 255, .7), 0 -8px 24px -10px rgba(27, 40, 49, .18);
}
@supports (backdrop-filter: blur(1px)) {
  .sticky-bar { backdrop-filter: saturate(150%) blur(12px); }
}

/* The whole block goes inert while a "same as registered" style checkbox is
   set. Paired with the disabled attribute that app.js also applies. */
.is-inert { opacity: .55; pointer-events: none; }

/* ==========================================================================
   Chrome: dark sidebar, tinted table
   ========================================================================== */

.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-w);
  flex: none;
  background: var(--g-sidebar);
  color: var(--sb-text);
  border-right: 1px solid rgba(0, 0, 0, .35);
  box-shadow: inset -1px 0 0 rgba(255, 255, 255, .05), 6px 0 24px -12px rgba(27, 40, 49, .45);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
  overflow: hidden;
  z-index: 30;
  transition: width var(--t), transform var(--t);
}

.sidebar-head {
  flex: none;
  padding: var(--s4) var(--s4) var(--s3);
  border-bottom: 1px solid var(--sb-line);
}
.wordmark {
  display: block;
  color: #fff; font-size: 19px; font-weight: 600; letter-spacing: -.02em;
  line-height: 1.1;
}
.wordmark:hover { text-decoration: none; color: #fff; }
/* "Duly" is the word a corporate lawyer reads a hundred times a week, so it
   carries the light and "Kept" carries the weight. */
.wordmark-duly { color: var(--brand-400); font-weight: 500; }
.wordmark-kept { color: #fff; font-weight: 700; }
.wordmark-sub {
  display: block; margin-top: 3px;
  font-size: 11px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: var(--sb-text-dim);
}

.sidebar-scroll { flex: 1; overflow-y: auto; padding: var(--s3) 0; }

.nav { display: flex; flex-direction: column; gap: 2px; padding: 0 var(--s2); }
.nav-link {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  min-height: 36px; padding: 7px 10px;
  border-radius: var(--radius-sm);
  color: var(--sb-text); font-size: 13.5px; font-weight: 500;
  border: 0; background: transparent; text-align: left; width: 100%;
  transition: background var(--t), color var(--t);
}
.nav-link:hover { background: var(--sb-hover); color: #fff; text-decoration: none; }
.nav-link.is-active { background: var(--sb-active); color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07); }
/* The orange rod: where you are. */
.nav-link.is-active::before {
  content: ""; position: absolute; left: -6px; top: 50%;
  width: 3px; height: 18px; margin-top: -9px;
  border-radius: 2px; background: var(--accent);
  box-shadow: 0 0 10px var(--accent-glow);
}
.nav-icon { flex: none; width: 18px; height: 18px; opacity: .72; }
.nav-link:hover .nav-icon, .nav-link.is-active .nav-icon { opacity: 1; }
.nav-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-count {
  font-size: 11px; font-weight: 600; color: var(--sb-text-dim);
  background: rgba(0, 0, 0, .22); border-radius: 999px; padding: 2px 7px;
  min-width: 22px; text-align: center;
}
.nav-link:hover .nav-count, .nav-link.is-active .nav-count { color: #fff; }
.nav-count.is-flagged { background: var(--accent-600); color: #fff; }

.nav-section-title {
  font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--sb-text-dim); padding: var(--s4) 18px 8px;
}
.nav-divider { height: 1px; background: var(--sb-line); margin: var(--s3) var(--s4) 0; }

.sidebar-foot {
  flex: none; padding: var(--s3) var(--s3);
  border-top: 1px solid var(--sb-line); background: rgba(0, 0, 0, .18);
  display: flex; align-items: center; gap: var(--s2);
}
.user-block { flex: 1; min-width: 0; }
.user-name {
  font-size: 13px; font-weight: 600; color: #fff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.user-role {
  font-size: 11px; color: var(--sb-text-dim); text-transform: capitalize; margin-top: 1px;
}
.user-logout {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: none;
  border-radius: var(--radius-sm); color: var(--sb-text-dim);
  transition: background var(--t), color var(--t);
}
.user-logout:hover { background: var(--sb-hover); color: #fff; text-decoration: none; }

.main {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  background-color: var(--canvas);
  background-image: var(--g-wash);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

/* Glass, and only here: the bar sits over scrolling content. Views that want
   a sticky title bar opt in; the default page supplies its own .page-head. */
.topbar {
  display: flex; align-items: center; gap: var(--s4);
  height: var(--topbar-h); flex: none;
  padding: 0 var(--s5);
  background: rgba(255, 255, 255, .80);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .6);
  position: sticky; top: 0; z-index: 20;
  contain: paint;
}
@supports (backdrop-filter: blur(1px)) {
  .topbar { backdrop-filter: saturate(150%) blur(12px); }
}
.topbar-title { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 10px; }
.topbar-title h1 { font-size: 21px; font-weight: 600; letter-spacing: -.018em; }

.page { padding: var(--s5); flex: 1; }
.page-wide { max-width: 1440px; width: 100%; margin: 0 auto; }
.page-narrow { max-width: 860px; width: 100%; margin: 0 auto; }

/* Page heading sits directly on the canvas, so its meta text uses the canvas
   ink step rather than the in-card one. */
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
  margin-bottom: var(--s4);
}
.page-head-main { min-width: 0; flex: 1; }
.page-title {
  font-size: 22px; font-weight: 600; letter-spacing: -.02em; color: var(--ink);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.page-sub { font-size: 13px; color: var(--ink-3-canvas); margin-top: 4px; line-height: 1.5; }
.page-actions { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

/* The one canvas-level heading that says "start here". Used by the agent
   queue and the current-cycle block; nowhere near a status chip. */
.page-head-locus {
  padding-left: 12px; border-left: 3px solid var(--accent); border-radius: 2px;
}

.breadcrumb {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12.5px; color: var(--ink-3-canvas); margin-bottom: var(--s3);
}
.breadcrumb a { color: var(--ink-2); }
.breadcrumb a:hover { color: var(--brand-700); }
.crumb-sep { color: var(--ink-4); }

.crumb-back {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 600; color: var(--brand-800);
  padding: 5px 12px 5px 9px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  box-shadow: var(--shadow-1), var(--seam);
  transition: background var(--t), border-color var(--t), transform var(--t);
}
.crumb-back:hover { background: var(--brand-050); border-color: var(--brand-300); text-decoration: none; }
.crumb-back:active { transform: translateX(-1px); }

/* Mobile control for the rail. Hidden above the breakpoint. */
.nav-toggle {
  display: none;
  align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2);
  box-shadow: var(--shadow-1), var(--seam);
}
.nav-scrim { display: none; }

/* ==========================================================================
   Feedback: flash, banners, inline notes
   ========================================================================== */

/* Block, not flex. A flex container turns every child and every run of text
   into a column, so "<strong>Nothing was saved.</strong>" followed by a list
   came out as two columns standing side by side. Prose, a heading line and a
   list now stack the way they read. The icon shape is restored below. */
.flash {
  display: block;
  border-radius: var(--radius); padding: var(--s3) var(--s4);
  margin-bottom: var(--s4);
  font-size: 13.5px; line-height: 1.55;
  border: 1px solid; border-left-width: 3px;
  box-shadow: var(--shadow-1), var(--seam);
  animation: flash-in 220ms cubic-bezier(.2, .7, .3, 1);
}
.flash-ok   { background: var(--ok-bg);   border-color: var(--ok-line);   border-left-color: var(--ok-mark);   color: #115A39; }
.flash-bad  { background: var(--bad-bg);  border-color: var(--bad-line);  border-left-color: var(--bad-mark);  color: #7E2323; }
.flash-warn { background: var(--warn-bg); border-color: var(--warn-line); border-left-color: var(--warn-mark); color: #6E4805; }
.flash-info { background: var(--surface); border-color: var(--line);      border-left-color: var(--brand);     color: var(--ink-2); }
.flash a { color: inherit; text-decoration: underline; }

@keyframes flash-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* Same recipe, no animation, meant to persist on the page. */
.banner {
  display: block;
  border-radius: var(--radius); padding: var(--s3) var(--s4); margin-bottom: var(--s4);
  font-size: 13.5px; line-height: 1.55;
  border: 1px solid; border-left-width: 3px;
  box-shadow: var(--shadow-1), var(--seam);
}
.banner strong, .flash strong { font-weight: 600; }
/* Links take the block's own ink: a brand-blue link on a red tint reads as a
   second message. */
.banner a { color: inherit; text-decoration: underline; }

/* The icon shape: a mark beside the text column. Opted into by adding a
   .flash-icon child, which is the only thing that needs a row. */
.banner:has(> .flash-icon), .flash:has(> .flash-icon) {
  display: flex; gap: var(--s3); align-items: flex-start;
}
.flash-icon { flex: none; margin-top: 1px; }
.banner:has(> .flash-icon) > :not(.flash-icon),
.flash:has(> .flash-icon) > :not(.flash-icon) { flex: 1; min-width: 0; }

/* Block children inside either one. The reset strips list markers and margins
   globally, so without these a list inside a banner loses its bullets and two
   paragraphs run together. */
.banner > p, .flash > p { margin: 0; }
.banner > p + p, .flash > p + p { margin-top: var(--s2); }
.banner > ul, .banner > ol, .flash > ul, .flash > ol {
  margin: var(--s2) 0 0; padding-left: 20px; list-style: disc;
}
.banner > ol, .flash > ol { list-style: decimal; }
.banner li, .flash li { margin-top: 3px; }
.banner > .btn, .banner > .inline-form, .flash > .btn, .flash > .inline-form { margin-top: var(--s2); }

.banner-ok   { background: var(--ok-bg);   border-color: var(--ok-line);   border-left-color: var(--ok-mark);   color: #115A39; }
.banner-bad  { background: var(--bad-bg);  border-color: var(--bad-line);  border-left-color: var(--bad-mark);  color: #7E2323; }
.banner-warn { background: var(--warn-bg); border-color: var(--warn-line); border-left-color: var(--warn-mark); color: #6E4805; }
.banner-info { background: var(--surface); border-color: var(--line);      border-left-color: var(--brand);     color: var(--ink-2); }
.banner-quiet {
  background: var(--brand-050); border-color: var(--brand-100);
  border-left-color: var(--brand-300); color: var(--ink-2);
}

.note { font-size: 12.5px; color: var(--ink-3); line-height: 1.55; }
.note-canvas { font-size: 12.5px; color: var(--ink-3-canvas); line-height: 1.55; }

/* ==========================================================================
   Cards and sections
   ========================================================================== */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1), var(--seam);
  padding: var(--s5);
  margin-bottom: var(--s4);
}
/* For cards whose whole body is a table or a row list: the child supplies
   its own padding and the card supplies only the frame. */
.card-flush { padding: 0; overflow: hidden; }
/* A card-flush supplies no inset, so anything dropped straight into it (an
   HTMX fragment that turned out to be a banner rather than a table) would run
   flush to the frame. These two carry their own. */
.card-flush > .banner { margin: var(--s4); }
.card-tight { padding: var(--s4); }
.card-danger { border-color: var(--bad-line); }
.card-danger .card-title, .card-danger .section-title { color: var(--bad); }
/* One step above the other cards on the page. Used for the current cycle
   panel on an entity and for the agent composer. */
.card-lift { box-shadow: var(--shadow-2), var(--seam); }

/* A 3px keel on the top edge, for the attention panels on the dashboard.
   The colour is the panel's subject, and the numbers inside stay ink. */
.card-keel { position: relative; overflow: hidden; }
.card-keel::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--brand-500), var(--brand-200));
}
.keel-warn::before { background: linear-gradient(90deg, var(--warn-mark), var(--warn-line)); }
.keel-bad::before  { background: linear-gradient(90deg, var(--bad-mark), var(--bad-line)); }
.keel-ok::before   { background: linear-gradient(90deg, var(--ok-mark), var(--ok-line)); }
.keel-sign::before { background: linear-gradient(90deg, var(--purple-mark), var(--purple-line)); }
/* The queue keel is the one place a panel edge wears orange, because the
   approval queue is the locus of the day and never a status. */
.keel-locus::before { background: linear-gradient(90deg, var(--accent), #F2B183); }

/* A left rail instead of a top keel, for a callout that runs inside a page. */
.card-rail { border-left: 3px solid var(--brand); }
.rail-warn { border-left-color: var(--warn-mark); }
.rail-bad { border-left-color: var(--bad-mark); }
.rail-ok { border-left-color: var(--ok-mark); }
.rail-locus { border-left-color: var(--accent); }

/* A numbered step heading in the import wizard. */
.step-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  background: var(--brand-100); color: var(--brand-800);
  font-size: 11.5px; font-weight: 700;
  box-shadow: var(--seam);
}
.step-badge.is-done { background: var(--ok-bg); color: var(--ok); }

.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
  background: var(--g-card-head);
}
.card-title { font-size: 14.5px; font-weight: 600; letter-spacing: -.005em; color: var(--ink); }
.card-sub { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.card-actions { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.card-body { padding: var(--s4); }
.card-body-lg { padding: var(--s5); }
.card-foot {
  padding: var(--s3) var(--s4);
  border-top: 1px solid var(--line-2);
  background: var(--surface-2);
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  flex-wrap: wrap;
}

.section-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); flex-wrap: wrap;
  margin-bottom: var(--s3);
}
/* A head whose text runs to several lines should not centre its action button
   against the middle of a paragraph. `.flex-start` is one class and loses the
   specificity tie to `.card-head` / `.section-head`, so opt in by name. */
.card-head.flex-start, .section-head.flex-start, .card-foot.flex-start { align-items: flex-start; }

.section-title { font-size: 14.5px; font-weight: 600; letter-spacing: -.005em; color: var(--ink); }
.section-note { font-size: 12px; color: var(--ink-3-canvas); }
.section-sub { font-size: 12.5px; color: var(--ink-3-canvas); margin-top: 3px; }
/* A small-caps brand label. What makes a dense panel read as an instrument
   rather than a paragraph. */
.eyebrow {
  display: block; font: 700 10.5px/1 var(--font); letter-spacing: .09em;
  text-transform: uppercase; color: var(--brand-500); margin-bottom: 7px;
}
.label-caps {
  font: 600 11px/1.3 var(--font); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
}

/* A collapsible block. Works on <details>. */
.disclose { margin-bottom: var(--s3); }
.disclose > summary {
  cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3-canvas); padding: 6px 0;
}
.disclose > summary::-webkit-details-marker { display: none; }
.disclose > summary::before {
  content: ""; width: 0; height: 0; flex: none;
  border-left: 5px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform var(--t);
}
.disclose[open] > summary::before { transform: rotate(90deg); }
.disclose > summary:hover { color: var(--brand-700); }

/* ==========================================================================
   Tables
   ========================================================================== */

.table-wrap {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-1), var(--seam);
  overflow: auto;
  margin-bottom: var(--s4);
}
/* Inside a .card-flush the frame is already drawn by the card. */
.card-flush > .table-wrap { border: 0; border-radius: 0; box-shadow: none; margin: 0; }
/* Inside a padded card the hairline stays (it is what separates the table from
   the prose above it) but the shadow goes: one object does not cast a shadow
   onto itself. */
.card > .table-wrap, .card > details > .table-wrap { box-shadow: none; }

.table { font-size: 13px; }
.table th, .table td {
  text-align: left; padding: 11px var(--s4); border-bottom: 1px solid var(--line-2);
  vertical-align: middle;
}
.table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--g-card-head);
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 0 var(--line);
  white-space: nowrap;
}
.table thead th[title] { cursor: help; }
.table tbody tr:last-child td { border-bottom: 0; }
.table td.right, .table th.right, .table td.num, .table th.num { text-align: right; }
/* A right-hand column holds an atomic value: a figure, a date, a button row.
   None of them should ever break over two lines, which is what happens when a
   name column two cells away takes the width. */
.table td.num, .table th.num, .table td.right { white-space: nowrap; }
.table td.center, .table th.center { text-align: center; }
.table td.top, .table th.top { vertical-align: top; }
/* An HTMX slot row: zero footprint while empty, normal cell once content
   swaps in (the loaded panel brings its own padding). */
.table td.cell-slot:empty { padding: 0; border-bottom: 0; }
.table-compact th, .table-compact td { padding: 7px var(--s3); }
.table-zebra tbody tr:nth-child(even) { background: var(--surface-2); }
.table-fixed { table-layout: fixed; }

/* Hover is a left keel plus the faintest wash, so a 150 row portfolio does
   not flash white on every pointer move. */
.table tbody tr.is-row,
.table-hover tbody tr {
  transition: background var(--t), box-shadow var(--t);
}
.table tbody tr.is-row:hover,
.table-hover tbody tr:hover {
  background: var(--brand-025);
  box-shadow: inset 3px 0 0 var(--brand-300);
}
.table tbody tr.is-selected { background: var(--brand-050); box-shadow: inset 3px 0 0 var(--brand); }
.table tbody tr.is-flagged td { background: var(--warn-bg); }
.table tbody tr.is-duplicate td { background: var(--warn-bg); }
.table tbody tr.is-errored td { background: var(--bad-bg); }
.table tbody tr.is-muted td { color: var(--ink-3); }
/* The current year's column or row in a multi-year grid. */
.table tbody tr.is-current td, .table td.is-current { background: var(--brand-050); }
/* A resigned director, a deactivated template, an archived import. */
.is-archived { opacity: .62; }
.is-archived:hover { opacity: 1; }

/* A second line under a cell's main value. Both break rather than widen: the
   values that land here are legal names, file names and email addresses, and a
   single unbroken 60-character token would otherwise take the width from every
   other column in the row. */
.cell-main { font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
.cell-sub {
  display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 3px;
  overflow-wrap: anywhere;
}
/* Two lines, then an ellipsis. A description written as a sentence would
   otherwise set the height of its row, and a table of those reads as a stack of
   paragraphs rather than a list. Carry the full text in a `title`. Works on its
   own or on a cell class, but it has to be declared *after* `.cell-sub` above:
   same specificity, and it needs to win the `display`. */
.clamp-2 {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
.cell-link { color: var(--ink); font-weight: 500; }
.cell-link:hover { color: var(--brand-700); text-decoration: none; }
.cell-actions { display: inline-flex; gap: var(--s1); white-space: nowrap; }

/* A group heading row inside a table body. */
.table .group-head th {
  position: static;
  background: linear-gradient(90deg, var(--brand-050), var(--surface) 60%);
  color: var(--brand-800); font-size: 12px; font-weight: 600;
  text-transform: none; letter-spacing: 0; box-shadow: none;
  border-top: 1px solid var(--line);
}

/* Cell inputs are invisible until you go near them. A table of forty date
   fields should read as dates, not as forty grey boxes. */
.cell-input {
  min-height: 30px; padding: 4px 8px; font-size: 12.5px; width: auto;
  border-color: transparent; background: transparent; box-shadow: none;
  color: var(--ink-2);
}
.cell-input:hover { border-color: var(--line-strong); background: var(--surface); color: var(--ink); }
.cell-input:focus { background: var(--surface); color: var(--ink); }

/* Days-in-status, set by App\Helpers::daysClass(). */
.days-ok { color: var(--ink-3); }
.days-warn { color: var(--warn); font-weight: 600; }
.days-bad { color: var(--bad); font-weight: 700; }

/* ==========================================================================
   Chips, tags, dots, pills
   ========================================================================== */

.dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  background: var(--dot, var(--brand)); flex: none;
}
.dot-sm { width: 7px; height: 7px; }
.dot-ok { background: var(--ok-mark); }
.dot-warn { background: var(--warn-mark); }
.dot-bad { background: var(--bad-mark); }
.dot-quiet { background: var(--ink-4); }

/* A chip is one unambiguous colour you identify peripherally. No gradient.
   The border is what lets it survive on white paper and on the tinted table. */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  /* min-height rather than height: the rendered height is identical, and a chip
     that is given a second line by a view can grow instead of clipping.
     A chip never wraps on its own: a status has to be read whole and at a
     glance, so a chip column widens rather than breaking the label. Long prose
     belongs in a tag or a cell-sub, not a chip. */
  min-height: 21px; padding: 0 9px 0 7px;
  border-radius: 999px;
  font: 600 10.5px/1 var(--font);
  letter-spacing: .05em; text-transform: uppercase;
  border: 1px solid transparent;
  background: var(--surface-2); color: var(--ink-3); border-color: var(--line);
  white-space: nowrap; vertical-align: middle;
  box-shadow: var(--seam);
}
.chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .85; flex: none; }
a.chip:hover { text-decoration: none; filter: brightness(.97); }
.chip-lg { height: 27px; padding: 0 13px 0 10px; font-size: 12px; letter-spacing: .04em; }
.chip-bare::before { display: none; }

/* --- semantic chips, for anything without a named state below ----------- */
.chip-ok      { background: var(--ok-bg);      color: var(--ok);         border-color: var(--ok-line); }
.chip-warn    { background: var(--warn-bg);    color: var(--warn);       border-color: var(--warn-line); }
.chip-bad     { background: var(--bad-bg);     color: var(--bad);        border-color: var(--bad-line); }
.chip-info    { background: var(--brand-050);  color: var(--brand-700);  border-color: var(--brand-100); }
.chip-brand   { background: var(--brand-100);  color: var(--brand-800);  border-color: var(--brand-200); }
.chip-purple  { background: var(--purple-bg);  color: var(--purple);     border-color: var(--purple-line); }
.chip-teal    { background: var(--teal-bg);    color: var(--teal);       border-color: var(--teal-line); }
.chip-neutral { background: var(--surface-2);  color: var(--ink-3);      border-color: var(--line); }
.chip-quiet   { background: transparent;       color: var(--ink-3);      border-color: var(--line); border-style: dashed; }
.chip-quiet::before { display: none; }

/* --- workflow states, twelve of them ------------------------------------
   Five families carry the whole cycle, which is what makes a 150 row table
   readable in one pass:
     quiet   nothing is owed yet          pending, skipped
     amber   the lawyer has to act        documents_generated
     brand   in motion inside the firm    review_approved
     purple  out with a signer            sent_for_signature, partially_signed
     teal    registry and billing half    invoice_sent, filing_ready
     green   the year is closed           fully_signed, filed, complete
     red     something is stopped         on_hold
   Orange stays out of this list on purpose. */
.chip-pending             { background: var(--surface-2); color: var(--ink-3);     border-color: var(--line); }
.chip-documents_generated { background: var(--warn-bg);   color: var(--warn);      border-color: var(--warn-line); }
.chip-review_approved     { background: var(--brand-050); color: var(--brand-700); border-color: var(--brand-100); }
.chip-sent_for_signature  { background: var(--purple-bg); color: var(--purple);    border-color: var(--purple-line); }
.chip-partially_signed    { background: var(--purple-bg); color: var(--purple);    border-color: var(--purple-line); }
.chip-partially_signed::before { opacity: .45; }
.chip-fully_signed        { background: var(--ok-bg);     color: var(--ok);        border-color: var(--ok-line); }
.chip-invoice_sent        { background: var(--teal-bg);   color: var(--teal);      border-color: var(--teal-line); }
.chip-filing_ready        { background: var(--teal-bg);   color: var(--teal);      border-color: var(--teal-mark); }
.chip-filed               { background: var(--ok-bg);     color: var(--ok);        border-color: var(--ok-line); }
/* Complete is the calmest green on the list. A closed year is a fact and
   should not out-shout a workflow that still needs a signature. */
.chip-complete            { background: var(--brand-050); color: var(--ok);        border-color: var(--ok-line); }
.chip-on_hold             { background: var(--bad-bg);    color: var(--bad);       border-color: var(--bad-line); }
.chip-skipped             { background: var(--surface-2); color: var(--ink-3);     border-color: var(--line); border-style: dashed; }
.chip-skipped::before { display: none; }

/* --- entity types -------------------------------------------------------
   Jurisdiction is taxonomy, not state. All five share one quiet brand shell
   and differ only in the dot, so a type chip never competes with the status
   chip two columns to its right. */
.chip-abca_corp, .chip-cbca_corp, .chip-extraprov, .chip-abca_ulc, .chip-cbca_nfp {
  background: var(--brand-050); color: var(--brand-800); border-color: var(--brand-100);
  letter-spacing: .04em;
}
.chip-abca_corp::before { background: var(--brand); opacity: 1; }
.chip-cbca_corp::before { background: var(--purple-mark); opacity: 1; }
.chip-extraprov::before { background: var(--warn-mark); opacity: 1; }
.chip-abca_ulc::before  { background: var(--teal-mark); opacity: 1; }
.chip-cbca_nfp::before  { background: var(--ink-4); opacity: 1; }

/* --- entity lifecycle (entities.status) ---------------------------------
   `incorporating` wears the same brand shell as review_approved: work in
   motion inside the firm, not yet a live file. No orange, since this is state. */
.chip-incorporating { background: var(--brand-050); color: var(--brand-700); border-color: var(--brand-100); }
.chip-active    { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.chip-dissolved { background: var(--surface-2); color: var(--ink-3);  border-color: var(--line); }
.chip-struck    { background: var(--bad-bg);    color: var(--bad);    border-color: var(--bad-line); }
.chip-inactive  { background: var(--surface-2); color: var(--ink-3);  border-color: var(--line); border-style: dashed; }
.chip-inactive::before { display: none; }

/* --- agent proposals (agent_proposals.status) ---------------------------
   A proposal waiting on the lawyer is the one thing in the app that is
   genuinely blocked on a human, so it wears amber rather than the quiet grey
   that a pending workflow wears. */
.chip-awaiting   { background: var(--warn-bg);   color: var(--warn);      border-color: var(--warn-line); }
.chip-approved   { background: var(--brand-050); color: var(--brand-700); border-color: var(--brand-100); }
.chip-rejected   { background: var(--surface-2); color: var(--ink-3);     border-color: var(--line); }
.chip-executed   { background: var(--ok-bg);     color: var(--ok);        border-color: var(--ok-line); }
.chip-failed     { background: var(--bad-bg);    color: var(--bad);       border-color: var(--bad-line); }
.chip-superseded { background: var(--surface-2); color: var(--ink-3);     border-color: var(--line); border-style: dashed; }
.chip-superseded::before { display: none; }
.chip-auto       { background: var(--teal-bg);   color: var(--teal);      border-color: var(--teal-line); }

/* --- playbook runs and import rows -------------------------------------- */
.chip-running   { background: var(--brand-050); color: var(--brand-700); border-color: var(--brand-100); }
.chip-running::before { animation: pulse 1.6s ease-in-out infinite; }
.chip-completed { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.chip-committed { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.chip-errored   { background: var(--bad-bg);    color: var(--bad);    border-color: var(--bad-line); }
.chip-new       { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.chip-duplicate { background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn-line); }

/* --- document and template kinds ----------------------------------------
   Kinds are taxonomy, like entity types, so they share the quiet brand shell
   and differ only in the dot. */
.chip-director_resolution, .chip-shareholder_resolution, .chip-annual_return_ab,
.chip-annual_return_federal, .chip-combined_package, .chip-combined {
  background: var(--brand-050); color: var(--brand-800); border-color: var(--brand-100);
  letter-spacing: .04em;
}
.chip-director_resolution::before    { background: var(--brand); opacity: 1; }
.chip-shareholder_resolution::before { background: var(--purple-mark); opacity: 1; }
.chip-annual_return_ab::before       { background: var(--warn-mark); opacity: 1; }
.chip-annual_return_federal::before  { background: var(--teal-mark); opacity: 1; }
.chip-combined_package::before, .chip-combined::before { background: var(--ink-4); opacity: 1; }

/* Signed and unsigned on a document row. */
.chip-signed   { background: var(--ok-bg);     color: var(--ok);    border-color: var(--ok-line); }
.chip-unsigned { background: var(--surface-2); color: var(--ink-3); border-color: var(--line); border-style: dashed; }
.chip-unsigned::before { display: none; }

@keyframes pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* A tag is a label with no state at all: template kind, file type, a count, a
   shareholding. Unlike a chip it carries free text ("1,000,000 Class A Voting
   Common Shares", an officer title someone typed), so it wraps inside its own
   pill rather than forcing its column wider than the name beside it. */
.tag {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 500; line-height: 1.4;
  background: var(--surface-2); color: var(--ink-3); border: 1px solid var(--line);
  max-width: 100%; overflow-wrap: anywhere;
}
.tag-brand { color: var(--brand-700); border-color: var(--brand-100); background: var(--brand-050); }
.tag-quiet { background: transparent; }
.tag-mono { font-family: var(--mono); font-size: 10.5px; letter-spacing: .02em; }

/* A pill is a clickable filter or a link that wants to look like an object. */
.pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 13px; border-radius: 999px;
  font-size: 12.5px; font-weight: 500;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
  box-shadow: var(--shadow-1), var(--seam);
  transition: border-color var(--t), background var(--t), color var(--t);
}
.pill:hover { border-color: var(--brand-300); color: var(--ink); text-decoration: none; }
.pill.is-active {
  background: var(--brand-100); border-color: var(--brand-300); color: var(--brand-800);
  font-weight: 600;
}
.pill-count { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.pill.is-active .pill-count { color: var(--brand-700); }
.pill-row { display: flex; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s4); }

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 34px;
  padding: 7px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--g-btn);
  color: var(--ink);
  font-size: 13px; font-weight: 500;
  white-space: nowrap; text-decoration: none;
  box-shadow: var(--shadow-1), var(--seam);
  transition: background var(--t), border-color var(--t), box-shadow var(--t), transform var(--t);
}
.btn:hover { background: var(--g-btn-h); border-color: var(--brand-200); color: var(--ink); text-decoration: none; }
.btn:active { transform: translateY(.5px); box-shadow: inset 0 1px 2px rgba(27, 40, 49, .12); }
.btn[disabled], .btn:disabled, .btn.is-disabled { opacity: .45; cursor: not-allowed; box-shadow: none; }

/* The 1px light inner edge is the whole trick: a gradient button with a lit
   top reads as an object rather than a coloured rectangle. */
.btn-primary {
  background: var(--g-primary);
  border-color: var(--brand-700);
  color: #fff;
  box-shadow: var(--shadow-brand), inset 0 1px 0 rgba(255, 255, 255, .18);
}
.btn-primary:hover { background: var(--g-primary-h); border-color: var(--brand-700); color: #fff; }
.btn-primary:active { background: var(--g-primary); }

.btn-quiet { border-color: transparent; color: var(--ink-2); background: none; box-shadow: none; }
.btn-quiet:hover { background: var(--brand-050); border-color: var(--line); color: var(--brand-800); }

.btn-danger { color: var(--bad); border-color: var(--bad-line); background: var(--surface); }
.btn-danger:hover { background: var(--bad-bg); border-color: var(--bad-mark); color: var(--bad); }
.btn-danger-solid {
  background: linear-gradient(180deg, #C43C3C 0%, #A82C2C 100%);
  border-color: #972929; color: #fff;
  box-shadow: 0 1px 2px rgba(27, 40, 49, .22), 0 4px 12px rgba(176, 48, 48, .28),
              inset 0 1px 0 rgba(255, 255, 255, .18);
}
.btn-danger-solid:hover { background: linear-gradient(180deg, #CE4646 0%, #B33333 100%); color: #fff; }

.btn-ok {
  background: linear-gradient(180deg, #239A62 0%, #17754A 100%);
  border-color: #136240; color: #fff;
  box-shadow: 0 1px 2px rgba(27, 40, 49, .22), 0 4px 12px rgba(23, 117, 74, .26),
              inset 0 1px 0 rgba(255, 255, 255, .18);
}
.btn-ok:hover { background: linear-gradient(180deg, #2AA86C 0%, #1B8153 100%); color: #fff; }

.btn-xs { min-height: 26px; padding: 3px 9px; font-size: 11.5px; gap: 5px; }
.btn-sm { min-height: 30px; padding: 5px 11px; font-size: 12.5px; }
.btn-lg { min-height: 40px; padding: 9px 20px; font-size: 14px; }
.btn-block { width: 100%; }
.btn-row { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0; flex: none;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-2);
  transition: background var(--t), color var(--t), border-color var(--t);
}
.icon-btn:hover { background: var(--brand-050); color: var(--brand-800); border-color: var(--line); text-decoration: none; }
.icon-btn-sm { width: 26px; height: 26px; }
.icon-btn-danger:hover { background: var(--bad-bg); color: var(--bad); border-color: var(--bad-line); }
.icon-btn-primary {
  background: var(--g-primary); color: #fff; border-color: var(--brand-700);
  box-shadow: var(--shadow-brand), inset 0 1px 0 rgba(255, 255, 255, .18);
}
.icon-btn-primary:hover { background: var(--g-primary-h); color: #fff; border-color: var(--brand-700); }

/* A segmented control: view switches, year switches. */
.switcher {
  display: inline-flex; padding: 3px; gap: 2px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--canvas-2); box-shadow: inset 0 1px 2px rgba(27, 40, 49, .05);
}
.switcher > a, .switcher > button {
  padding: 5px 12px; font-size: 12.5px; font-weight: 500; color: var(--ink-2);
  background: transparent; border: 0; border-radius: var(--radius-xs);
  transition: background var(--t), color var(--t), box-shadow var(--t);
}
.switcher > a:hover, .switcher > button:hover { color: var(--ink); text-decoration: none; }
.switcher > .is-active {
  background: var(--surface); color: var(--brand-800);
  box-shadow: var(--shadow-1), var(--seam);
}

/* Forms that exist only to wrap one button, so they do not break a flex row. */
.inline-form { display: inline-flex; align-items: center; gap: 6px; margin: 0; }

/* ==========================================================================
   Forms
   ========================================================================== */
/* Inputs are sunken at rest and turn to paper on focus. The field lighting up
   as you enter it is worth more than any amount of border styling.

   The base sits inside :where() so it carries no specificity at all. A single
   class, .cell-input or .code-area, then overrides it cleanly, which a bare
   input[type="text"] selector would not allow. Hover and focus stay at normal
   specificity so a field always lights up when you enter it. */

:where(input[type="text"], input[type="password"], input[type="url"], input[type="search"],
       input[type="number"], input[type="date"], input[type="time"], input[type="email"],
       input[type="tel"], input[type="file"], select, textarea) {
  width: 100%;
  min-height: 34px;
  padding: 6px 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  /* Barely sunken. A full-width field filled with --canvas-2 turns into a
     grey slab, which is the exact flatness this palette exists to avoid. The
     inset shadow does the recessing and the fill stays close to paper. */
  background: var(--surface-2);
  color: var(--ink);
  box-shadow: inset 0 1px 2px rgba(27, 40, 49, .06);
  transition: background var(--t), border-color var(--t), box-shadow var(--t);
}
input::placeholder, textarea::placeholder { color: var(--ink-4); }
input:hover, select:hover, textarea:hover { border-color: var(--brand-300); }
input:focus, select:focus, textarea:focus {
  background: var(--surface);
  border-color: var(--brand-500);
  box-shadow: var(--ring);
  outline: none;
}
input:disabled, select:disabled, textarea:disabled { background: var(--canvas); color: var(--ink-3); cursor: not-allowed; }
input[readonly], textarea[readonly] { background: var(--canvas-2); color: var(--ink-2); }
textarea { resize: vertical; min-height: 68px; line-height: 1.55; padding: 8px 10px; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--brand); cursor: pointer; width: 15px; height: 15px; }
input[type="file"] { padding: 6px; font-size: 12.5px; }
input[type="file"]::file-selector-button {
  margin-right: 10px; padding: 5px 11px;
  border: 1px solid var(--line-strong); border-radius: var(--radius-xs);
  background: var(--g-btn); color: var(--ink); font-size: 12.5px; font-weight: 500;
  cursor: pointer;
}

select {
  appearance: none;
  padding-right: 28px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5' fill='none' stroke='%23647784' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 12px;
}
select:focus { background-color: var(--surface); }
select[multiple] { background-image: none; padding-right: 10px; min-height: 90px; }

.field { margin-bottom: var(--s4); }
/* The last field in its container stops short of the container's own padding,
   so a card that ends in a field has the same 24px below the control as it has
   above the first label. */
.field:last-child { margin-bottom: 0; }
/* One label recipe for the whole app. 11px, 600, .06em matches .label-caps,
   .dl dt, .tile-label and .stat-label, so a form label and a data label are
   recognisably the same thing. --ink-2 is 6.9:1 on paper and 5.9:1 on the
   canvas; --ink-3 only clears 3.9:1 on the canvas, and filter forms do sit
   there. */
.field > label, .field-label {
  display: block; font: 600 11px/1.3 var(--font); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-2); margin-bottom: 6px;
}
/* Big enough to see beside an 11px uppercase label, and off the last letter. */
.req-mark { color: var(--bad); font-weight: 700; margin-left: 3px; }
.field .hint, .hint { font-size: 12px; color: var(--ink-3); margin-top: 6px; line-height: 1.45; }
.hint code {
  background: var(--brand-050); border: 1px solid var(--line);
  border-radius: 4px; padding: 1px 5px; font-size: 11.5px; color: var(--brand-800);
}
.field-error { font-size: 12px; color: var(--bad); margin-top: 6px; font-weight: 500; line-height: 1.45; }
.is-invalid, input.is-invalid, select.is-invalid, textarea.is-invalid {
  border-color: var(--bad-line); background: var(--bad-bg);
}
.is-invalid:focus { border-color: var(--bad-mark); box-shadow: 0 0 0 3px rgba(207, 64, 64, .18); }

/* A row of fields. The row owns the spacing below itself and the gap between
   wrapped lines; its children own none, so a row that wraps puts 16px between
   the two lines rather than 16px of gap plus 16px of field margin. */
.field-row { display: flex; gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s4); }
.field-row > .field { flex: 1 1 180px; }
.field-row:last-child { margin-bottom: 0; }
/* A row-level error (one message for three corp-number fields) belongs to the
   row above it, so the row keeps it close instead of handing it to the next. */
.field-row:has(+ .field-error) { margin-bottom: var(--s1); }

/* Width steps for row children. Without them every child shared one 180px
   basis, so a postal code was as wide as a street address, and a four-field
   row broke after the third and stretched the fourth across the full width.
   The two short steps do not grow, so they stay short whether they are on the
   first line or the last. Measured against the uppercase label each step has
   to hold: 96px fits MONTH or DAY, 132px fits POSTAL CODE, PROVINCE and a
   native date control, 180px fits CITY or APPOINTED DATE, 260px fits a legal
   name or a street line. */
:is(.field-row, .field-row-tight) > .field-xs { flex: 0 1 96px; }
:is(.field-row, .field-row-tight) > .field-sm { flex: 0 1 132px; }
:is(.field-row, .field-row-tight) > .field-md { flex: 1 1 180px; }
:is(.field-row, .field-row-tight) > .field-lg { flex: 2 1 260px; }

/* A field beside a button. align-items: flex-end lines the control up with the
   button, which only works if the field has stopped carrying 16px of margin
   below it. */
.field-row-tight { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: flex-end; }
.field-row-tight > .field { flex: 0 1 auto; }

/* A label and its control on one line. The control sizes to its content with a
   floor rather than taking width: 100% and squeezing the label into wrapping.
   Checkboxes and radios are excluded; they keep their 15px box. */
.field-inline { display: flex; align-items: center; gap: var(--s3); }
.field-inline > label { margin: 0; flex: none; }
.field-inline > input:not([type="checkbox"], [type="radio"]),
.field-inline > select { flex: 0 1 auto; width: auto; min-width: 140px; }

/* Like .field-row, the grid owns the spacing below itself and its children own
   none, so two wrapped lines are 16px apart rather than 32px. */
.field-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s4); margin-bottom: var(--s4);
}
.field-grid:last-child { margin-bottom: 0; }
.field-auto > input, .field-auto > select { width: auto; min-width: 120px; }

/* A field inside a flex strip takes its spacing from the strip's gap. Stated
   once, here, because a 16px bottom margin inside an align-items: center row
   lifts the control 8px above its neighbours, and inside an align-items:
   flex-end row drops it 16px below them. This is the single biggest cause of
   fields looking out of line. */
.field-row > .field,
.field-row-tight > .field,
.field-grid > .field,
.add-strip > .field,
.repeater-row > .field,
.share-row > .field,
.check-all > .field,
.sticky-bar > .field { margin-bottom: 0; }

/* A named group of fields inside a long form. */
.form-section { margin-bottom: var(--s5); }
.form-section > legend, .form-section-title {
  display: block; width: 100%;
  font: 600 11px/1 var(--font); letter-spacing: .07em; text-transform: uppercase;
  color: var(--brand-500);
  padding-bottom: var(--s2); margin-bottom: var(--s4);
  border-bottom: 1px solid var(--line);
}
.form-actions {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  margin-top: var(--s5);
}
/* The rule above the buttons belongs on paper. Five forms put their actions
   outside every card, where the hairline was a stray line drawn across the
   canvas with nothing on either side of it. */
.card .form-actions, .modal-body .form-actions, .inline-panel .form-actions {
  padding-top: var(--s4); border-top: 1px solid var(--line-2);
}
/* Inside a bar that already has its own padding and top edge, the bar is the
   separation. */
.sticky-bar > .form-actions, .card-foot > .form-actions, .add-strip > .form-actions {
  margin-top: 0; padding-top: 0; border-top: 0;
}
.form-actions-end { justify-content: flex-end; }

.check-row {
  display: flex; gap: var(--s2); align-items: flex-start;
  font-size: 13px; color: var(--ink-2); margin: var(--s1) 0 var(--s3);
}
.check-row input { margin-top: 2px; flex: none; }
.check-row label { cursor: pointer; }

/* Radio and checkbox options that read as objects: filters, autonomy levels,
   import column choices. */
.radio-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.radio {
  display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px;
  color: var(--ink-2); cursor: pointer; padding: 5px 11px 5px 9px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
  transition: background var(--t), border-color var(--t), color var(--t);
}
.radio:hover { color: var(--ink); background: var(--brand-050); border-color: var(--brand-200); }
.radio:has(input:checked) {
  background: var(--brand-100); border-color: var(--brand-300); color: var(--brand-800); font-weight: 500;
}

/* A toolbar of filters above a table. Sits on the canvas, so it gets paper. */
.toolbar {
  display: flex; align-items: flex-end; gap: var(--s3); flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-1), var(--seam);
  padding: var(--s3) var(--s4); margin-bottom: var(--s4);
}
.toolbar .field { margin-bottom: 0; }
.toolbar input, .toolbar select { width: auto; min-width: 140px; }
.toolbar-grow { flex: 1; min-width: 180px; }
.toolbar-grow input { width: 100%; }

/* An HTMX row that swaps between a display row and an edit form. */
.edit-row {
  background: var(--brand-025);
  border: 1px solid var(--brand-100);
  border-radius: var(--radius-sm);
  padding: var(--s3);
  margin: var(--s2) 0;
}
/* :where() keeps this at single-class weight so the flex-strip rule above (which
   zeroes a field's margin inside a row) still wins inside an edit panel. */
:where(.edit-row) .field { margin-bottom: var(--s3); }
.edit-row-actions { display: flex; gap: var(--s2); align-items: center; }

/* The inline "add a director" strip at the foot of a list. */
.add-strip {
  display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  border-top: 1px dashed var(--line-strong);
  background: var(--surface-2);
}

/* A panel that opens in place: the add form above a list, the quick-add
   officer strip inside a table row. */
.inline-panel {
  background: var(--brand-025); border: 1px solid var(--brand-100);
  border-radius: var(--radius-sm); padding: var(--s3) var(--s4);
  margin-bottom: var(--s3);
}
.inline-panel-title {
  font: 600 10.5px/1 var(--font); letter-spacing: .08em; text-transform: uppercase;
  color: var(--brand-500); margin-bottom: var(--s3);
}

/* A list of checkboxes that each select a record: bulk document generation,
   proposal multi-approve. The whole row is the target. */
.check-list {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); overflow: hidden;
}
.check-item {
  display: flex; align-items: center; gap: var(--s3);
  padding: 10px var(--s4); cursor: pointer;
  border-bottom: 1px solid var(--line-2);
  transition: background var(--t);
}
.check-item:last-child { border-bottom: 0; }
.check-item:hover { background: var(--brand-025); }
.check-item:has(input:checked) { background: var(--brand-050); box-shadow: inset 3px 0 0 var(--brand-300); }
/* A row another control has taken the decision for: the "all" box above a list
   of entity types disables the individual ones rather than clearing them, so
   unticking it gives the previous choice back. It has to come after the checked
   rule, which carries the same specificity. */
.check-item:has(input:disabled),
.check-item:has(input:disabled):hover {
  color: var(--ink-4); cursor: default; background: none; box-shadow: none;
}
.check-item input { flex: none; }
/* Holds a checkbox's place in a row that has none, so a list where some rows
   are selectable and some are not (a signed document cannot be deleted) does
   not step its names in and out by thirteen pixels. */
.check-gap { flex: none; width: 13px; }
.check-item-main { flex: 1; min-width: 0; font-size: 13.5px; }
.check-all {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: var(--s2) var(--s4); background: var(--surface-2);
  border-bottom: 1px solid var(--line); font-size: 12.5px; color: var(--ink-2);
}

/* .check-item is a row in a .check-list. Used on its own, as the label of a
   single field or of a repeater cell, it brought the list chrome with it: 16px
   of side padding inside a 64px column plus a border that had no list to
   separate. Stripped back to a checkbox and its words, read in sentence case,
   because that is what a control's own label is. */
.field > .check-item,
.check-row > .check-item,
.repeater-row .check-item,
.share-row .check-item {
  padding: 0; border-bottom: 0; background: none; box-shadow: none;
  gap: var(--s2); margin-bottom: 0;
  font: 500 13px/1.4 var(--font); letter-spacing: 0; text-transform: none;
  color: var(--ink-2);
}
.field > .check-item:hover,
.check-row > .check-item:hover,
.repeater-row .check-item:hover,
.share-row .check-item:hover { background: none; }
.field > .check-item:has(input:checked),
.check-row > .check-item:has(input:checked),
.repeater-row .check-item:has(input:checked),
.share-row .check-item:has(input:checked) {
  background: none; box-shadow: none; color: var(--brand-800);
}

/* Repeater rows built in JS: share classes, shareholder holdings, share
   presets. Same shell in all three so the pages match. */
.repeater { display: flex; flex-direction: column; gap: var(--s2); }
/* The head has no border and the rows have a 1px one, so matching the rows'
   12px padding left every head label sitting 1px to the left of its column.
   The head is inset by the row's border as well as its padding. */
.repeater-head {
  display: flex; align-items: flex-end; gap: var(--s3);
  padding: 0 calc(var(--s3) + 1px);
  font: 600 10.5px/1.25 var(--font); letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-2);
}
.repeater-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s3);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
/* A row whose cells carry their own labels aligns on its controls, not on the
   middle of the row: a label that wraps to two lines must not push its input
   out of line with the inputs beside it. Rows of bare controls stay centred. */
.repeater-row:has(> .field > label), .share-row:has(> .field > label) { align-items: flex-end; }
/* Dense rows, but not the checkboxes in them: min-height on a 15px checkbox
   stretched it into a 15 by 30 lozenge. */
.repeater-row input:not([type="checkbox"], [type="radio"]),
.repeater-row select,
.repeater-row textarea { min-height: 30px; font-size: 12.5px; }
.repeater-grow { flex: 1; min-width: 0; }
.repeater-w-md { width: 150px; flex: none; }
.repeater-w-sm { width: 96px; flex: none; }
.repeater-w-xs { width: 64px; flex: none; text-align: center; }
/* The cell holding a row's remove control, and the spacer that reserves the
   same gutter in the head. Without it the head's growing column is one button
   wider than the row's, and every fixed column after it sits to the right of
   the input it names. */
.repeater-act { width: 26px; flex: none; }
/* Share structure rows, built by app.js, are repeater rows by another name. */
.share-rows { display: flex; flex-direction: column; gap: var(--s2); }
.share-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s3);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.share-row .share-class { flex: 1; min-width: 0; }
.share-row .share-authorized { width: 150px; flex: none; }
/* The voting checkbox is a column like the others, and 96px is what the head
   above it reserves (.repeater-w-sm). */
.share-row > .field-inline { width: 96px; flex: none; }
.share-row input:not([type="checkbox"], [type="radio"]),
.share-row select { min-height: 30px; font-size: 12.5px; }

/* ==========================================================================
   Tiles and stats
   ========================================================================== */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--s3); margin-bottom: var(--s4); }
.tile {
  position: relative; overflow: hidden;
  background: var(--g-tile);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1), var(--seam);
  padding: var(--s4);
  display: block; color: inherit;
}
a.tile:hover { text-decoration: none; border-color: var(--brand-200); box-shadow: var(--shadow-2), var(--seam); }
/* A 2px keel along the top edge. The tile's semantic colour lives here and
   not in the number, so "overdue: 3" is not red until it should be. */
.tile::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: linear-gradient(90deg, var(--brand-500), var(--brand-200));
}
.tile-accent::before { background: linear-gradient(90deg, var(--accent), #F2B183); }
.tile-warn::before   { background: linear-gradient(90deg, var(--warn-mark), var(--warn-line)); }
.tile-bad::before    { background: linear-gradient(90deg, var(--bad-mark), var(--bad-line)); }
.tile-ok::before     { background: linear-gradient(90deg, var(--ok-mark), var(--ok-line)); }
.tile-sign::before   { background: linear-gradient(90deg, var(--purple-mark), var(--purple-line)); }
.tile-file::before   { background: linear-gradient(90deg, var(--teal-mark), var(--teal-line)); }
.tile-label {
  display: block; font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
}
.tile-value {
  display: block; font-size: 30px; font-weight: 600; line-height: 1.1;
  margin: 6px 0 4px; letter-spacing: -.025em; color: var(--ink);
}
.tile-unit { font-size: 15px; font-weight: 500; color: var(--ink-3); margin-left: 3px; letter-spacing: 0; }
.tile-sub { display: block; font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }
.tile-warn .tile-value { color: var(--warn); }
.tile-bad .tile-value { color: var(--bad); }
.tile-ok .tile-value { color: var(--ok); }

/* A denser strip of figures, hairline-separated rather than boxed. */
.stat-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 1px;
  background: var(--line); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; margin-bottom: var(--s4);
  box-shadow: var(--shadow-1), var(--seam);
}
.stat { background: var(--surface); padding: var(--s4); }
.stat-label {
  display: block; font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
}
.stat-value { display: block; font-size: 19px; font-weight: 600; margin-top: 5px; letter-spacing: -.015em; color: var(--ink); }
.stat-sub { display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 4px; }
.stat.is-warn .stat-value { color: var(--warn); }
.stat.is-bad .stat-value { color: var(--bad); }

/* Label and value pairs on a detail page. */
.dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--s4) var(--s5); }
.dl > div { min-width: 0; }
.dl dt {
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 4px;
}
.dl dd { font-size: 13.5px; color: var(--ink); margin: 0; line-height: 1.5; word-break: break-word; }
.dl dd.is-blank { color: var(--ink-4); }
/* minmax(0, 1fr), not 1fr: the value column has to be allowed to shrink below
   its own longest word, or an agreement id pushes the grid past the card. */
.dl-rows { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 9px var(--s4); align-items: baseline; }
.dl-rows dt { text-align: right; white-space: nowrap; }

/* Label left, value right, one per line, hairline between. The shape most of
   the entity and workflow detail panels want. */
.dl-split { display: flex; flex-direction: column; }
.dl-split > div {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4);
  padding: 8px 0; border-bottom: 1px solid var(--line-2);
}
.dl-split > div:last-child { border-bottom: 0; }
.dl-split dt {
  font-size: 12.5px; font-weight: 500; color: var(--ink-3);
  text-transform: none; letter-spacing: 0; margin: 0; white-space: nowrap;
}
.dl-split dd {
  font-size: 13px; font-weight: 500; color: var(--ink); text-align: right;
  min-width: 0; overflow-wrap: anywhere;
}

/* ==========================================================================
   Meters
   ========================================================================== */
/* Track is a lighter step of the same ramp so the whole bar reads at a
   glance. The fill is always brand: progress and status are different facts,
   and the chip already carries the status. */

.progress { display: flex; align-items: center; gap: 10px; }
.progress-num { font-size: 13px; font-weight: 600; min-width: 36px; letter-spacing: -.01em; }
.progress-bar {
  display: block; height: 5px; width: 100%; border-radius: 999px;
  background: var(--brand-150); box-shadow: inset 0 1px 1px rgba(27, 40, 49, .08);
  overflow: hidden; flex: 1;
}
.progress-bar-sm { height: 4px; width: 72px; flex: none; }
.progress-fill {
  display: block; height: 100%; border-radius: 999px;
  background: var(--g-progress);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25);
  transition: width 320ms cubic-bezier(.2, .7, .3, 1);
}
.progress-fill.is-full { background: linear-gradient(90deg, #17754A, #2FA06B); }
.progress-fill.is-warn { background: linear-gradient(90deg, #8A5B07, #D9880A); }
.progress-fill.is-bad  { background: linear-gradient(90deg, #B03030, #CF4040); }

/* ==========================================================================
   Workflow timeline
   ========================================================================== */
/* A vertical rail down the left with one node per state. Done nodes are
   filled brand, the current node is filled and ringed, future nodes are
   hollow. No orange: this is state. */

.timeline { position: relative; padding-left: 26px; }
.timeline::before {
  content: ""; position: absolute; left: 7px; top: 8px; bottom: 8px;
  width: 2px; background: var(--line); border-radius: 1px;
}
.tl-step { position: relative; padding: 0 0 var(--s4) 0; }
.tl-step:last-child { padding-bottom: 0; }
.tl-step::before {
  content: ""; position: absolute; left: -26px; top: 3px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--line-strong);
  box-shadow: var(--seam);
}
.tl-step.is-done::before { background: var(--brand); border-color: var(--brand); }
.tl-step.is-current::before {
  background: var(--brand); border-color: var(--brand);
  box-shadow: 0 0 0 4px rgba(80, 110, 125, .20);
}
.tl-step.is-bad::before { background: var(--bad-mark); border-color: var(--bad-mark); }
.tl-step.is-skipped::before { border-style: dashed; }
.tl-title { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.tl-step.is-future .tl-title { color: var(--ink-3); font-weight: 500; }
.tl-meta { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.tl-body { font-size: 12.5px; color: var(--ink-2); margin-top: 5px; line-height: 1.55; }
.tl-actions { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); }

/* The same states laid out horizontally, for the top of a workflow page. */
.steps { display: flex; align-items: flex-start; gap: 0; overflow-x: auto; padding-bottom: var(--s2); }
.step { flex: 1; min-width: 92px; position: relative; text-align: center; padding-top: 22px; }
.step::before {
  content: ""; position: absolute; top: 5px; left: 50%;
  width: 12px; height: 12px; margin-left: -6px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--line-strong); z-index: 1;
}
.step::after {
  content: ""; position: absolute; top: 10px; left: 50%; right: -50%;
  height: 2px; background: var(--line);
}
.step:last-child::after { display: none; }
.step.is-done::before, .step.is-current::before { background: var(--brand); border-color: var(--brand); }
.step.is-done::after { background: var(--brand-300); }
.step.is-current::before { box-shadow: 0 0 0 4px rgba(80, 110, 125, .20); }
.step-label { font-size: 11px; font-weight: 600; color: var(--ink-3); line-height: 1.3; padding: 0 4px; }
.step.is-current .step-label { color: var(--brand-800); }

/* ==========================================================================
   Anniversary calendar and the portfolio year grid
   ========================================================================== */

.heatmap {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--s1);
}
.heat-cell {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-height: 62px; padding: var(--s2) 6px; text-align: center;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2);
  box-shadow: var(--seam);
  transition: border-color var(--t), transform var(--t), box-shadow var(--t);
}
a.heat-cell:hover { text-decoration: none; border-color: var(--brand-300); transform: translateY(-1px); box-shadow: var(--shadow-1), var(--seam); }
.heat-month { font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
.heat-count { font-size: 19px; font-weight: 600; line-height: 1.1; letter-spacing: -.02em; }
.heat-sub { font-size: 10px; color: var(--ink-3); }

/* Five steps of the same ramp. Ink flips to white only at the top two, where
   the fill is dark enough to carry it. */
.heat-0 { background: var(--surface); color: var(--ink-4); }
.heat-1 { background: var(--brand-050); }
.heat-2 { background: var(--brand-100); }
.heat-3 { background: var(--brand-200); color: var(--brand-900); }
.heat-4 { background: var(--brand-600); color: #fff; border-color: var(--brand-700); }
.heat-4 .heat-month, .heat-4 .heat-sub { color: rgba(255, 255, 255, .82); }
.heat-3 .heat-month, .heat-3 .heat-sub { color: var(--brand-800); }

/* The current month. Orange because this is locus, not state. */
.heat-cell.is-now { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 0 12px var(--accent-glow); }
.heat-cell.is-now .heat-month { color: var(--accent-700); }
.heat-4.is-now .heat-month { color: #FBE7D6; }

.heat-legend { display: flex; align-items: center; gap: var(--s2); font-size: 11.5px; color: var(--ink-3); margin-top: var(--s3); }
.heat-key { display: inline-block; width: 16px; height: 10px; border-radius: 3px; border: 1px solid var(--line); }

/* One cell per entity per year in the portfolio grid. Set by
   App\Helpers::entityYearStatus(). */
.ycell {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  font-size: 12px; font-weight: 600; line-height: 1;
  border: 1px solid transparent; box-shadow: var(--seam);
}
.ycell-blank { background: transparent; color: var(--line-strong); box-shadow: none; }
.ycell-quiet { background: var(--surface-2); color: var(--ink-4);  border-color: var(--line); }
.ycell-ok    { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.ycell-warn  { background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn-line); }
.ycell-bad   { background: var(--bad-bg);    color: var(--bad);    border-color: var(--bad-line); }
.ycell-info  { background: var(--brand-050); color: var(--brand-700); border-color: var(--brand-100); }
.ycell-sign  { background: var(--purple-bg); color: var(--purple); border-color: var(--purple-line); }

/* ==========================================================================
   Lists of people and documents
   ========================================================================== */

.list { display: flex; flex-direction: column; }
.list-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: 10px var(--s4);
  border-bottom: 1px solid var(--line-2);
  transition: background var(--t), box-shadow var(--t);
}
.list-row:last-child { border-bottom: 0; }
.list-row:hover { background: var(--brand-025); box-shadow: inset 3px 0 0 var(--brand-300); }
.list-main { flex: 1; min-width: 0; }
/* Same rule as a table cell: a document file name breaks instead of pushing the
   row's actions off the end. */
.list-name { font-size: 13.5px; font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
.list-name:hover { color: var(--brand-700); text-decoration: none; }
.list-meta { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; overflow-wrap: anywhere; }
.list-side { display: flex; align-items: center; gap: var(--s2); flex: none; }
.list-row.is-resigned .list-name { color: var(--ink-3); text-decoration: line-through; text-decoration-color: var(--line-strong); }

/* Row actions appear on hover, so a dense list stays quiet until you go
   near it. Invisible is not the same as gone: without pointer-events none
   the box still takes clicks on a row you were only passing over. */
.hover-actions { display: inline-flex; gap: var(--s1); opacity: 0; pointer-events: none; transition: opacity var(--t); }
.list-row:hover .hover-actions, .list-row:focus-within .hover-actions,
tr:hover .hover-actions, tr:focus-within .hover-actions { opacity: 1; pointer-events: auto; }
.hover-actions:focus-within { opacity: 1; pointer-events: auto; }

.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none; border-radius: 50%;
  background: var(--brand-100); color: var(--brand-800);
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  box-shadow: var(--seam);
}
.avatar-agent { background: var(--brand-600); color: #fff; }

/* ==========================================================================
   Agent chat
   ========================================================================== */

.chat { display: flex; flex-direction: column; min-height: 0; }
/* The conversation fills the viewport under the page heading. Stated here so
   no view has to carry an inline calc(). */
.chat-shell { height: calc(100vh - 210px); min-height: 460px; }
.chat-log {
  display: flex; flex-direction: column; gap: var(--s4);
  padding: var(--s5);
  overflow-y: auto;
}
.msg { display: flex; gap: var(--s3); align-items: flex-start; max-width: 100%; }
.msg-body {
  min-width: 0; max-width: 72ch;
  font-size: 13.5px; line-height: 1.6; color: var(--ink);
  border-radius: var(--radius); padding: 11px var(--s4);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-1), var(--seam);
  word-break: break-word;
}
.msg-body p { margin: 0 0 var(--s2); }
.msg-body p:last-child { margin-bottom: 0; }
.msg-body ul, .msg-body ol { margin: 0 0 var(--s2) 20px; list-style: disc; }
.msg-body ol { list-style: decimal; }
.msg-body code {
  background: var(--brand-050); border: 1px solid var(--line);
  border-radius: 4px; padding: 1px 5px; font-size: 12px; color: var(--brand-800);
}
.msg-meta { font-size: 11px; color: var(--ink-3); margin-top: 5px; }

/* The lawyer's own turns sit right and wear the brand tint, so the column
   reads as a conversation without either side shouting. */
.msg-user { flex-direction: row-reverse; margin-left: auto; }
.msg-user .msg-body {
  background: var(--brand-050); border-color: var(--brand-100);
  border-bottom-right-radius: var(--radius-xs);
}
.msg-user .msg-meta { text-align: right; }
.msg-agent .msg-body { background: var(--surface); border-bottom-left-radius: var(--radius-xs); }
.msg-system .msg-body {
  background: var(--surface-2); border-style: dashed; color: var(--ink-2); font-size: 12.5px;
}

/* A tool call is machinery, so it is rendered as machinery: mono, sunken,
   collapsed by default. */
.tool-call {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--canvas-2); margin: var(--s2) 0;
  box-shadow: inset 0 1px 2px rgba(27, 40, 49, .05);
  overflow: hidden;
}
.tool-call > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 8px;
  padding: 7px 11px; font-size: 11.5px; color: var(--ink-2);
}
.tool-call > summary::-webkit-details-marker { display: none; }
.tool-call > summary:hover { background: var(--brand-050); }
.tool-name { font-family: var(--mono); font-size: 11.5px; font-weight: 600; color: var(--brand-800); }
.tool-status { margin-left: auto; font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); }
.tool-status.is-ok { color: var(--ok); }
.tool-status.is-bad { color: var(--bad); }
.tool-io {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.55; color: var(--ink-2);
  padding: var(--s3) 11px; border-top: 1px solid var(--line);
  background: var(--surface); white-space: pre-wrap; word-break: break-word;
  max-height: 280px; overflow: auto; margin: 0;
}

.typing { display: inline-flex; align-items: center; gap: 5px; padding: 4px 0; }
.typing span {
  width: 6px; height: 6px; border-radius: 50%; background: var(--brand-300);
  animation: typing 1.2s ease-in-out infinite;
}
.typing span:nth-child(2) { animation-delay: .15s; }
.typing span:nth-child(3) { animation-delay: .3s; }
@keyframes typing { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

.composer {
  display: flex; gap: var(--s2); align-items: flex-end;
  padding: var(--s3) var(--s4);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}
.composer textarea { min-height: 44px; max-height: 200px; font-size: 13.5px; background: var(--surface); }
.composer-actions { display: flex; flex-direction: column; gap: var(--s1); flex: none; }
.composer-hint { font-size: 11px; color: var(--ink-3); padding: 0 var(--s4) var(--s2); background: var(--surface-2); }

/* Opening prompts on an empty conversation. */
.suggestions { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s3); }
.suggestion {
  font-size: 12.5px; text-align: left; color: var(--ink-2);
  border: 1px dashed var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface); padding: 8px 12px;
  transition: border-color var(--t), background var(--t), color var(--t);
}
.suggestion:hover { border-style: solid; border-color: var(--brand-300); background: var(--brand-050); color: var(--brand-800); text-decoration: none; }

/* The conversation rail beside the chat. */
.convo-list { display: flex; flex-direction: column; gap: 2px; }
.convo {
  display: block; padding: 8px 10px; border-radius: var(--radius-sm);
  color: var(--ink-2); transition: background var(--t), color var(--t);
}
.convo:hover { background: var(--brand-050); color: var(--brand-800); text-decoration: none; }
.convo.is-active { background: var(--brand-100); color: var(--brand-800); font-weight: 600; }
.convo-title { font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.convo-when { font-size: 11px; color: var(--ink-3); margin-top: 2px; }

/* A proposal card in the approval queue. */
.proposal { margin-bottom: var(--s3); }
.proposal-head { display: flex; align-items: flex-start; gap: var(--s3); justify-content: space-between; flex-wrap: wrap; }
.proposal-kind {
  font-family: var(--mono); font-size: 11.5px; font-weight: 600;
  color: var(--brand-800); background: var(--brand-050);
  border: 1px solid var(--brand-100); border-radius: var(--radius-xs); padding: 2px 7px;
}
.proposal-reason { font-size: 13px; color: var(--ink-2); line-height: 1.55; margin-top: var(--s2); }
.proposal-payload {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.55; color: var(--ink-2);
  background: var(--canvas-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: var(--s3) var(--s4); margin: var(--s3) 0 0;
  white-space: pre-wrap; word-break: break-word; max-height: 240px; overflow: auto;
}
.proposal-actions { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s3); }

/* ==========================================================================
   Template editor
   ========================================================================== */

.editor { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); align-items: start; }
.editor-toolbar {
  display: flex; align-items: center; gap: var(--s1); flex-wrap: wrap;
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--line); background: var(--g-card-head);
}
/* Formatting buttons in the template toolbar. Quiet until hovered, because a
   row of twelve outlined buttons above the text would out-weigh the text. */
.tb-btn {
  padding: 4px 10px; font-size: 12.5px; line-height: 1.4;
  color: var(--ink-2); background: transparent;
  border: 1px solid transparent; border-radius: var(--radius-xs);
  transition: background var(--t), border-color var(--t), color var(--t);
}
.tb-btn:hover { background: var(--brand-050); border-color: var(--line); color: var(--brand-800); }
.tb-btn:active { background: var(--brand-100); }
.tb-btn[disabled] { opacity: .4; cursor: not-allowed; }
.tb-btn-underline { text-decoration: underline; }
.tb-btn-bold { font-weight: 700; }
.tb-btn-italic { font-style: italic; }
.tb-sep { width: 1px; height: 20px; background: var(--line-strong); margin: 0 var(--s1); flex: none; }

/* Two panes, source on the left and preview on the right, filling the
   viewport under the toolbar. The 100vh figure assumes the full-page editor,
   where the panes are the last thing on the screen. */
.editor-panes { display: flex; height: calc(100vh - 350px); min-height: 460px; }
/* An embedded preview: one pane inside a card on a page that is not the editor,
   sometimes in half of a two-column grid or inside a disclosure. A viewport
   height there pushes everything under it off the screen, so this one is a
   fixed, shorter block. */
.editor-panes-sm { height: 360px; min-height: 0; }
.editor-pane { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.editor-pane-source { border-right: 1px solid var(--line); }
.editor-pane-preview { background: var(--canvas-2); }
/* display: block kills the inline descender gap under the frame. The min-height
   is what keeps a frame used outside .editor-panes from falling back to the
   150px default iframe box. */
.preview-frame {
  display: block; flex: 1; width: 100%; min-height: 320px;
  border: 0; background: var(--surface);
}
.editor-panes-sm .preview-frame { min-height: 0; }
.preview-bar {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.preview-bar select, .preview-bar input { width: auto; min-height: 30px; font-size: 12.5px; }
.preview-status { font-size: 11.5px; color: var(--ink-3); margin-left: auto; }

/* Variable and snippet pickers inside the editor modals. */
.pick-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--s2); }
.pick-btn {
  display: block; width: 100%; text-align: left;
  padding: 7px 10px; border: 1px solid transparent; border-radius: var(--radius-xs);
  background: transparent; color: var(--ink-2); font-size: 12.5px;
  transition: background var(--t), border-color var(--t);
}
.pick-btn:hover { background: var(--brand-050); border-color: var(--brand-100); text-decoration: none; }
.pick-btn code { font-size: 11.5px; color: var(--brand-700); }
.pick-btn:hover code { color: var(--brand-800); }
.pick-card {
  display: block; width: 100%; text-align: left;
  padding: var(--s3); border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2); font-size: 12.5px; line-height: 1.5;
  box-shadow: var(--seam);
  transition: border-color var(--t), background var(--t);
}
.pick-card:hover { border-color: var(--brand-300); background: var(--brand-050); text-decoration: none; }
.pick-card b { display: block; color: var(--ink); font-size: 13px; margin-bottom: 3px; }
.pick-group-label {
  font: 700 10px/1 var(--font); letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-3); margin: var(--s4) 0 var(--s2);
}
/* The template body. Mono, generous leading, and the only textarea in the
   app that is allowed to be tall. */
.code-area {
  font-family: var(--mono); font-size: 12.5px; line-height: 1.7;
  min-height: 460px; border-radius: 0; border: 0;
  background: var(--surface); box-shadow: none; padding: var(--s4);
  color: var(--ink);
}
.code-area:focus { box-shadow: inset 0 0 0 2px var(--brand-500); }
.code {
  font-family: var(--mono); font-size: 12px; line-height: 1.6;
  background: var(--canvas-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: var(--s3) var(--s4); white-space: pre-wrap; word-break: break-word; color: var(--ink-2);
  overflow: auto; margin: 0;
}
/* Rendered preview of assembled legal text. Serif, because that is what the
   PDF is. */
.doc-preview {
  font-family: Georgia, "Times New Roman", serif; font-size: 13.5px; line-height: 1.7;
  color: var(--ink); background: var(--surface); padding: var(--s5);
  max-height: 620px; overflow: auto;
}
.doc-preview h1, .doc-preview h2, .doc-preview h3 { font-size: 15px; margin: var(--s4) 0 var(--s2); }
.doc-preview p { margin: 0 0 var(--s3); }

/* The variables reference. Tokens are click-to-copy. */
.var-table td:first-child { width: 42%; }
.token {
  display: inline-block; font-family: var(--mono); font-size: 11.5px;
  background: var(--brand-050); border: 1px solid var(--brand-100);
  border-radius: var(--radius-xs); padding: 2px 7px; color: var(--brand-800);
  cursor: pointer; word-break: break-all;
  transition: background var(--t), border-color var(--t);
}
.token:hover { background: var(--brand-100); border-color: var(--brand-300); }
.token.is-copied { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }

/* The DOCX conversion panel closes the editor as a sunken footer. */
.convert-panel {
  background: var(--canvas-2); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: inset 0 1px 3px rgba(27, 40, 49, .06);
  padding: var(--s4); margin-bottom: var(--s4);
}
:where(.convert-panel) .field { margin-bottom: var(--s3); }

/* ==========================================================================
   Import
   ========================================================================== */

/* A dropzone is a wrapper carrying data-dropzone around exactly one file
   input. The input keeps its browse button and stays focusable; app.js adds
   the drop half, the accept check and the readout. See docs/UI-SYSTEM.md. */
.dropzone {
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
  background: var(--canvas-2); box-shadow: inset 0 1px 3px rgba(27, 40, 49, .06);
  padding: var(--s6) var(--s5); text-align: center;
  transition: border-color var(--t), background var(--t), box-shadow var(--t), transform var(--t);
}
.dropzone:hover { border-color: var(--brand-300); background: var(--brand-050); }
/* A drag in flight has to be unmistakable: the only other feedback is the
   cursor. Solid brand edge, a 2px inner ring and 1px of lift. The fill stays
   on --brand-050, which is lighter than the canvas, so the sub text and the
   title keep their documented ratios while the state is on. */
.dropzone.is-over {
  border-style: solid; border-color: var(--brand-500); background: var(--brand-050);
  box-shadow: inset 0 0 0 2px var(--brand-300), 0 8px 18px -10px rgba(27, 40, 49, .35);
  transform: translateY(-1px);
}
.dropzone-title { font-size: 14px; font-weight: 600; color: var(--ink-2); }
.dropzone-sub { font-size: 12.5px; color: var(--ink-3-canvas); margin-top: 5px; }
.dropzone input[type="file"] { max-width: 340px; margin: var(--s3) auto 0; }
/* A hint written inside a zone sits on the sunken fill, where --ink-3 does not
   clear 4.5:1. The canvas ink does. */
.dropzone .hint { color: var(--ink-3-canvas); }

/* The same frame on a compact row: a file beside its action button, or one
   cell of a field row. No 32px well, and the control fills the cell. */
.dropzone-slim { padding: var(--s3); text-align: left; }
.dropzone-slim input[type="file"] { max-width: none; width: 100%; margin: 0; }
.dropzone-slim .dropzone-sub, .dropzone-slim .hint { margin-top: var(--s2); }

/* What is attached now: names, sizes, and anything the zone turned away.
   Written by app.js on a drop and on an ordinary pick, so a drop is never
   silent. Empty until something is chosen, and it takes no room while empty. */
.dropzone-files {
  font-size: 12px; line-height: 1.5; color: var(--ink-2);
  margin: var(--s2) 0 0; overflow-wrap: anywhere;
}
.dropzone-files:empty { display: none; }

.import-summary {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
  padding: var(--s3) var(--s4); margin-bottom: var(--s4);
  background: var(--canvas-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: inset 0 1px 2px rgba(27, 40, 49, .05);
  font-size: 12.5px; color: var(--ink-2);
}
.import-summary b { font-weight: 600; color: var(--ink); }
.cell-changed { background: var(--warn-bg); box-shadow: inset 0 0 0 1px var(--warn-line); }
.cell-added { background: var(--ok-bg); box-shadow: inset 0 0 0 1px var(--ok-line); }
.row-errors { font-size: 11.5px; color: var(--bad); line-height: 1.5; }
.row-errors li { list-style: disc; margin-left: 16px; }

/* ==========================================================================
   Settings
   ========================================================================== */

.settings { max-width: 900px; }
.setting-row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s5); flex-wrap: wrap;
  padding: var(--s4) 0; border-top: 1px solid var(--line-2);
}
.setting-row:first-of-type { border-top: 0; padding-top: 0; }
.setting-main { flex: 1; min-width: 240px; }
.setting-name { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.setting-desc { font-size: 12.5px; color: var(--ink-3); margin-top: 4px; line-height: 1.55; max-width: 62ch; }
.setting-control { flex: none; display: flex; align-items: center; gap: var(--s2); }
.setting-control input, .setting-control select { width: auto; min-width: 140px; }

/* A checkbox that reads as a switch. Uses the native input for state and
   keyboard behaviour, so it stays accessible. */
.switch { position: relative; display: inline-flex; align-items: center; flex: none; }
.switch input {
  position: absolute; opacity: 0; width: 44px; height: 24px; margin: 0; cursor: pointer;
}
.switch span {
  display: block; width: 44px; height: 24px; border-radius: 999px;
  background: var(--canvas-2); border: 1px solid var(--line-strong);
  box-shadow: inset 0 1px 2px rgba(27, 40, 49, .08);
  transition: background var(--t), border-color var(--t);
}
.switch span::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-1);
  transition: transform var(--t);
}
.switch input:checked + span { background: var(--brand); border-color: var(--brand-700); }
.switch input:checked + span::after { transform: translateX(20px); }
.switch input:focus-visible + span { outline: 2px solid var(--brand-500); outline-offset: 2px; }

/* ==========================================================================
   Overlays: modals, menus, tabs, pagination
   ========================================================================== */

.modal { position: fixed; inset: 0; z-index: 150; display: flex; align-items: center; justify-content: center; padding: var(--s4); }
.modal[hidden] { display: none; }
.modal-backdrop { position: absolute; inset: 0; background: rgba(19, 28, 33, .48); }
@supports (backdrop-filter: blur(1px)) { .modal-backdrop { backdrop-filter: blur(3px); } }
.modal-card {
  position: relative; background: var(--surface);
  border-radius: var(--radius); box-shadow: var(--shadow-3), var(--seam);
  width: min(680px, 100%); max-height: calc(100vh - 64px); overflow: auto;
  animation: modal-in 160ms cubic-bezier(.2, .7, .3, 1);
}
.modal-card-sm { width: min(460px, 100%); }
.modal-card-lg { width: min(940px, 100%); }
/* A picker modal scrolls its own body rather than the whole card, so the
   header and the close control stay put. */
.modal-card-scroll { display: flex; flex-direction: column; overflow: hidden; }
.modal-card-scroll .modal-body { overflow-y: auto; min-height: 0; }
@keyframes modal-in { from { opacity: 0; transform: translateY(10px) scale(.99); } to { opacity: 1; transform: none; } }
.modal-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line);
  background: var(--g-card-head);
}
.modal-head h2 { font-size: 15px; font-weight: 600; }
.modal-body { padding: var(--s5); }
/* A modal body that IS the document: no padding, because the page inside the
   frame carries its own margins, and a tall fixed box because a PDF viewer
   given `height: auto` collapses to the 150px default iframe height. The card
   is 940px wide and a Letter page is 816px at 96dpi, so the document renders
   at about full size without scrolling sideways. */
.modal-body-viewer { padding: 0; height: calc(100vh - 220px); min-height: 420px; }
.modal-body-viewer .preview-frame { height: 100%; min-height: 0; }
.modal-foot { display: flex; justify-content: flex-end; gap: var(--s2); padding: var(--s3) var(--s5) var(--s5); }

/* A menu on <details>. No JS, closes on outside click only in browsers that
   support it, so keep menus short and give every item a real link. */
.dropdown { position: relative; display: inline-block; }
.dropdown > summary { list-style: none; cursor: pointer; }
.dropdown > summary::-webkit-details-marker { display: none; }
.menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 60;
  min-width: 190px; padding: var(--s1);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-2), var(--seam);
}
.menu-item {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 7px 10px; border: 0; background: none; border-radius: var(--radius-xs);
  font-size: 13px; color: var(--ink-2); text-align: left;
}
.menu-item:hover { background: var(--brand-050); color: var(--brand-800); text-decoration: none; }
.menu-item-danger { color: var(--bad); }
.menu-item-danger:hover { background: var(--bad-bg); color: var(--bad); }
.menu-sep { height: 1px; background: var(--line-2); margin: var(--s1) 0; }
.menu-label { font: 600 10.5px/1 var(--font); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); padding: 8px 10px 5px; }

.tabs { display: flex; gap: var(--s1); border-bottom: 1px solid var(--line); margin-bottom: var(--s4); }
.tab {
  padding: 9px 14px; font-size: 13px; font-weight: 500; color: var(--ink-3);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color var(--t), border-color var(--t);
}
.tab:hover { color: var(--brand-700); text-decoration: none; }
.tab.is-active { color: var(--brand-800); border-bottom-color: var(--brand); font-weight: 600; }

.pager { display: flex; align-items: center; gap: var(--s1); flex-wrap: wrap; margin-top: var(--s4); }
.pager-link {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 32px; padding: 0 9px;
  border: 1px solid var(--line); border-radius: var(--radius-xs);
  background: var(--surface); color: var(--ink-2); font-size: 12.5px;
  box-shadow: var(--seam);
}
.pager-link:hover { border-color: var(--brand-300); color: var(--brand-800); text-decoration: none; }
.pager-link.is-active { background: var(--brand-100); border-color: var(--brand-300); color: var(--brand-800); font-weight: 600; }
.pager-link.is-disabled { opacity: .4; pointer-events: none; }
.pager-info { margin-left: auto; font-size: 12px; color: var(--ink-3-canvas); }

/* ==========================================================================
   Empty states
   ========================================================================== */
/* An empty state should look like a hole in the page, because that is what
   it is. Sunken, dashed, one sentence, one button. No illustration. */

.empty-state {
  background: var(--canvas-2);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  padding: var(--s6) var(--s5);
  text-align: center;
  /* An empty state on the canvas is a block like a card, and three views put a
     card straight after one. Without this they touched. */
  margin-bottom: var(--s4);
}
.empty-title { font-size: 14px; font-weight: 600; color: var(--ink-2); }
.empty-sub { font-size: 12.5px; color: var(--ink-3-canvas); margin-top: 5px; line-height: 1.55; }
.empty-state .btn { margin-top: var(--s3); }
/* Inside a card or a table cell, where the frame already exists. */
.empty-inline { padding: var(--s5) var(--s4); background: none; border: 0; margin-bottom: 0; }
.empty-line { font-size: 13px; color: var(--ink-3); padding: var(--s3) var(--s4); line-height: 1.55; }

/* ==========================================================================
   HTMX states
   ========================================================================== */

.htmx-swapping { opacity: .5; transition: opacity .2s ease-in; }
.htmx-indicator { opacity: 0; transition: opacity var(--t); }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }
.htmx-request .htmx-hide-on-request { visibility: hidden; }

.spinner {
  display: inline-block; width: 14px; height: 14px; flex: none;
  border: 2px solid var(--brand-150); border-top-color: var(--brand);
  border-radius: 50%; animation: spin .7s linear infinite;
}
.spinner-light { border-color: rgba(255, 255, 255, .35); border-top-color: #fff; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   Standalone pages: sign in, errors
   ========================================================================== */
/* The sign-in screen is the one place to be a little theatrical. */

.auth {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: var(--s5);
  background:
    radial-gradient(900px 500px at 50% -8%, rgba(224, 114, 43, .13), transparent 60%),
    var(--g-sidebar);
}
.auth-col { width: 100%; max-width: 380px; }
.auth-card {
  width: 100%; max-width: 380px;
  background: var(--surface); border: 1px solid rgba(255, 255, 255, .12);
  border-radius: var(--radius); box-shadow: var(--shadow-3), var(--seam);
  overflow: hidden;
}
.auth-head {
  padding: var(--s5) var(--s6) var(--s4);
  background: var(--g-card-head); border-bottom: 1px solid var(--line);
}
.auth-mark { font-size: 24px; font-weight: 600; letter-spacing: -.022em; line-height: 1.1; }
.auth-mark .wordmark-duly { color: var(--brand); }
.auth-mark .wordmark-kept { color: var(--ink); }
.auth-lede { color: var(--ink-3); font-size: 12.5px; margin-top: 6px; }
.auth-body { padding: var(--s5) var(--s6) var(--s6); }
.auth-title { font-size: 14px; font-weight: 600; color: var(--ink); margin-bottom: var(--s4); }
.auth-error {
  background: var(--bad-bg); border: 1px solid var(--bad-line); color: #7E2323;
  border-left: 3px solid var(--bad-mark);
  border-radius: var(--radius-sm); padding: 10px 13px; font-size: 13px; margin-bottom: var(--s4);
}
.auth-foot {
  text-align: center; margin-top: var(--s4);
  font-size: 11.5px; color: var(--sb-text-dim);
}

.errpage {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: var(--s5);
  background-color: var(--canvas); background-image: var(--g-wash);
  background-repeat: no-repeat;
  text-align: center;
}
.errpage-code {
  font-size: 72px; font-weight: 700; line-height: 1; letter-spacing: -.04em;
  color: var(--brand-200);
}
.errpage-title { font-size: 17px; font-weight: 600; color: var(--ink); margin-top: var(--s3); }
.errpage-sub { font-size: 13px; color: var(--ink-3-canvas); margin-top: 6px; line-height: 1.55; }
.errpage .btn { margin-top: var(--s4); }

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (min-width: 1100px) {
  .editor { grid-template-columns: minmax(0, 1.6fr) minmax(300px, 1fr); }
  .split { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--s4); align-items: start; }
  .split-side { position: sticky; top: var(--s5); }
}

@media (max-width: 1099px) {
  .sidebar { width: var(--rail-w); }
  .sidebar .wordmark-sub,
  .sidebar .nav-text,
  .sidebar .nav-count,
  .sidebar .nav-section-title,
  .sidebar .user-block { display: none; }
  .sidebar .wordmark { font-size: 15px; text-align: center; }
  .sidebar .wordmark-duly { display: none; }
  .sidebar-head { padding: var(--s4) var(--s2) var(--s3); }
  .sidebar .nav-link { justify-content: center; padding: 7px 0; }
  .sidebar .nav-link.is-active::before { left: 0; }
  .sidebar-foot { justify-content: center; }

  /* Tapping the rail expands it in flow. A fixed overlay would cover the
     control that opened it, with no way back out. */
  body.nav-open .sidebar { width: var(--sidebar-w); }
  body.nav-open .sidebar .wordmark-sub,
  body.nav-open .sidebar .nav-text,
  body.nav-open .sidebar .nav-count,
  body.nav-open .sidebar .nav-section-title,
  body.nav-open .sidebar .user-block { display: block; }
  body.nav-open .sidebar .wordmark { font-size: 19px; text-align: left; }
  body.nav-open .sidebar .wordmark-duly { display: inline; }
  body.nav-open .sidebar .nav-link { justify-content: flex-start; padding: 7px 10px; }
  body.nav-open .sidebar-head { padding: var(--s4) var(--s4) var(--s3); }
  body.nav-open .sidebar-foot { justify-content: flex-start; }

  /* A 26px chevron inside a 60px rail is a poor target, so from here down the
     hamburger above the page is the control that names the links. */
  .nav-toggle { display: inline-flex; }
}

@media (max-width: 767px) {
  .sidebar {
    position: fixed; left: 0; top: 0; bottom: 0; z-index: 90;
    width: var(--sidebar-w); transform: translateX(-100%);
    box-shadow: var(--shadow-3);
  }
  body.nav-open .sidebar { transform: none; }
  body.nav-open .nav-scrim {
    display: block; position: fixed; inset: 0; z-index: 80;
    background: rgba(19, 28, 33, .45);
  }
  .page { padding: var(--s4) var(--s3); }
  .card { padding: var(--s4); }
  .page-title { font-size: 19px; }
  .heatmap { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .table-wrap { overflow-x: auto; }
  .dl { grid-template-columns: minmax(0, 1fr); }
  .msg-body { max-width: none; }
  .chat-log { padding: var(--s3); }
  .chat-shell { height: calc(100vh - 170px); }
  .setting-row { gap: var(--s3); }
  /* Source over preview. Side by side at 375px wide is neither. */
  .editor-panes { flex-direction: column; height: auto; }
  .editor-pane-source { border-right: 0; border-bottom: 1px solid var(--line); }
  .code-area { min-height: 320px; }
  .editor-panes-sm { height: auto; }
  .editor-panes-sm .preview-frame { min-height: 320px; }
  /* Rows wrap at this width, so a single-line head can no longer sit over the
     columns it names. The cells keep their own labels. */
  .repeater-row, .share-row { flex-wrap: wrap; }
  .repeater-head { display: none; }
  .share-row > .field-inline { width: auto; }
}

/* ==========================================================================
   Motion and print
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .btn:active, a.tile:hover, a.heat-cell:hover { transform: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .topbar { backdrop-filter: none; background: var(--surface); }
  .modal-backdrop { backdrop-filter: none; }
}

@media print {
  .sidebar, .topbar, .nav-toggle, .nav-scrim, .modal, .composer,
  .page-actions, .card-actions, .toolbar, .pager, .hover-actions,
  .form-actions, .no-print { display: none !important; }
  body, .main { background: #fff; background-image: none; }
  .card, .table-wrap, .tile, .stat-strip, .auth-card { box-shadow: none; border-color: #ccc; }
  .page { padding: 0; }
  .chip { box-shadow: none; }
  a { color: #000; text-decoration: none; }
  .table thead th { background: #f2f2f2; color: #000; box-shadow: none; }
  tr, .card, .list-row { break-inside: avoid; }
  .doc-preview { max-height: none; overflow: visible; padding: 0; }
}
