/* ═══════════════════════════════════════════════════════════════════════════
   VALIDURE ERP — components                              owner: UI Designer
   Class vocabulary fixed in docs/design/PROTOTYPE.md §2.3. Every colour is a
   token; every state (hover, focus, selected, invalid, disabled) is designed.
   Live reference with copyable markup: /styleguide/components.html
   Extensions beyond §2.3 are marked "ext" and documented in 04-design-system.md.

   Contents
     1 icon · 2 button · 3 form controls · 4 choice controls · 5 card · 6 kpi
     7 table · 8 badge/chip/tag/code/money/kbd · 9 tabs · 10 dialog/drawer
     11 menu · 12 alert/toast · 13 empty · 14 toolbar/pagination · 15 stepper
     16 avatar/progress/timeline · 17 tree/dl/divider/skeleton · 18 brand
     19 golden-screen components: quick actions · column chart · line grid
        (table--entry) · item search (suggest) · priority columns
     20 batch 2–3 promotions: statement table · set-off · GSTIN switch ·
        Dr/Cr grid and sheet · allocation grid · header band variants ·
        file pick / dropzone · phone tier · card-foot links · print head
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · icon ───────────────────────────────────────────────────────────── */
.icon {
  display: inline-block;
  flex: none;
  width: 16px;
  height: 16px;
  vertical-align: -3px;
  stroke-width: 1.75;
}

.icon--sm {
  width: 14px;
  height: 14px;
  vertical-align: -2px;
}

.icon--lg {
  width: 20px;
  height: 20px;
  vertical-align: -4px;
  stroke-width: 1.6;
}

/* ── 2 · button ─────────────────────────────────────────────────────────── */
.btn {
  --btn-h: 36px;
  --btn-px: 14px;
  --btn-bg: var(--e-raised);
  --btn-fg: var(--e-fg);
  --btn-line: color-mix(in srgb, var(--e-fg) 22%, transparent);
  --btn-bg-hover: var(--e-subtle);
  --btn-line-hover: color-mix(in srgb, var(--e-fg) 38%, transparent);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--btn-h);
  padding: 0 var(--btn-px);
  border: 1px solid var(--btn-line);
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-ui);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}

.btn:hover {
  background: var(--btn-bg-hover);
  border-color: var(--btn-line-hover);
  color: var(--btn-fg);
}

.btn:active {
  box-shadow: inset 0 1px 2px color-mix(in srgb, var(--e-fg) 18%, transparent);
}

.btn .icon {
  margin-inline: -2px;
}

.btn--primary,
.btn-split__more {
  --btn-bg: var(--e-primary);
  --btn-fg: var(--e-on-primary);
  --btn-line: var(--e-primary);
  --btn-bg-hover: var(--e-primary-dark);
  --btn-line-hover: var(--e-primary-dark);
  box-shadow: var(--shadow-1);
}

/* ext: split button — ONE primary action with its variants attached
   (an editor's Save invoice + Save & new / Save & print / Save & share).
   <div class="btn-split">
     <a class="btn btn--primary" …>Save invoice</a>
     <details class="menu menu--up menu--end">
       <summary class="btn btn-split__more" aria-label="More ways to save">{{icon chevron-up}}</summary>
       <div class="menu__list">…</div></details></div>
   The toggle is part of the one primary action, so it is not a second
   .btn--primary: a view still has exactly one. */
.btn-split {
  display: inline-flex;
  align-items: stretch;
}

.btn-split > .btn {
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

.btn-split > .menu {
  display: flex;
}

.btn-split__more {
  width: var(--btn-h);
  padding: 0;
  border-inline-start-color: color-mix(in srgb, var(--e-on-primary) 40%, var(--e-primary));
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

.btn-split__more .icon {
  margin: 0;
  transition: rotate var(--dur-fast) var(--ease-out);
}

.menu[open] > .btn-split__more .icon {
  rotate: 180deg;
}

/* a menu row's trailing note: its shortcut ("Ctrl+Shift+S") */
.menu__item > small {
  margin-inline-start: auto;
  padding-inline-start: var(--sp-4);
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
}

.btn--secondary {
  /* the default look, spelled out for clarity in markup */
  --btn-bg: var(--e-raised);
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-line: transparent;
  --btn-bg-hover: var(--e-surface);
  --btn-line-hover: transparent;
  --btn-fg: var(--e-fg);
}

/* Danger: dark red with light text in light mode, light red with dark text in
   dark mode — the pair is err-fg ⇄ raised, verified ≥ 4.5:1 by contrast.mjs. */
.btn--danger {
  --btn-bg: var(--e-err-fg);
  --btn-fg: var(--e-raised);
  --btn-line: var(--e-err-fg);
  --btn-bg-hover: color-mix(in srgb, var(--e-err-fg) 86%, var(--e-fg));
  --btn-line-hover: var(--btn-bg-hover);
}

/* A quieter destructive action for toolbars and menus. */
.btn--ghost.btn--danger {
  --btn-bg: transparent;
  --btn-fg: var(--e-err-fg);
  --btn-line: transparent;
  --btn-bg-hover: var(--e-err-soft);
  --btn-line-hover: transparent;
}

.btn--sm {
  --btn-h: 30px;
  --btn-px: 10px;
  gap: 6px;
  font-size: var(--fs-sm);
}

.btn--lg {
  --btn-h: 44px;
  --btn-px: 20px;
  font-size: var(--fs-lg);
}

.btn--icon {
  width: var(--btn-h);
  padding: 0;
}

.btn--icon .icon {
  margin: 0;
}

.btn--block {
  display: flex;
  width: 100%;
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
  box-shadow: none;
}

/* Loading look: aria-busy="true" keeps the label and shows a spinner. */
.btn[aria-busy="true"] {
  cursor: progress;
  pointer-events: none;
}

.btn[aria-busy="true"]::before {
  content: "";
  width: 14px;
  height: 14px;
  flex: none;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: erp-spin 0.7s linear infinite;
}

.btn[aria-busy="true"] > .icon:first-child {
  display: none;
}

@keyframes erp-spin {
  to {
    rotate: 360deg;
  }
}

@media (pointer: coarse) {
  .btn {
    --btn-h: 40px;
  }
  .btn--sm {
    --btn-h: 34px;
  }
}

/* ── 3 · form controls ──────────────────────────────────────────────────── */
.field {
  display: grid;
  gap: 6px;
  align-content: start;
  min-width: 0;
}

.field__label {
  display: block;
  color: var(--e-fg);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
}

legend.field__label {
  margin-bottom: 6px;
}

.field__label small,
.field__label .field__optional {
  color: var(--e-muted);
  font-weight: var(--fw-regular);
}

.field.is-required .field__label::after {
  content: " *";
  content: " *" / "";
  color: var(--e-err-fg);
  font-weight: var(--fw-bold);
}

.field__hint {
  color: var(--e-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.field__error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  color: var(--e-err-fg);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
}

.field__error::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: 1px;
  background: currentColor;
  -webkit-mask: var(--_icon-alert) center / contain no-repeat;
  mask: var(--_icon-alert) center / contain no-repeat;
}

:root {
  --_icon-alert: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9.5'/%3E%3Cpath d='M12 7.5v5.5M12 16.4v.1'/%3E%3C/svg%3E");
}

.input,
.select,
.textarea {
  display: block;
  width: 100%;
  min-height: 36px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--e-border-strong);
  border-radius: var(--r-md);
  background: var(--e-raised);
  color: var(--e-fg);
  font-family: var(--font-ui);
  font-size: var(--fs-md);
  line-height: 1.4;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}

.input:hover,
.select:hover,
.textarea:hover {
  border-color: color-mix(in srgb, var(--e-border-strong) 55%, var(--e-fg));
}

.input:focus,
.select:focus,
.textarea:focus,
.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
  border-color: var(--e-focus);
  outline: 2px solid var(--e-focus);
  outline-offset: -1px;
}

.textarea {
  min-height: 88px;
  padding: var(--sp-2) var(--sp-3);
  line-height: var(--lh-ui);
}

.input.num {
  text-align: right;
}

/* ext: uppercase monospace entry for GSTIN, PAN, IFSC, HSN */
.input--code {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.input--code::placeholder {
  text-transform: none;
  letter-spacing: 0;
  font-family: var(--font-ui);
  font-size: var(--fs-md);
}

.input--sm,
.select--sm {
  min-height: 30px;
  font-size: var(--fs-sm);
}

.input[readonly],
.textarea[readonly] {
  background: var(--e-subtle);
  border-style: dashed;
}

.input:disabled,
.select:disabled,
.textarea:disabled {
  background: var(--e-surface);
  border-color: var(--e-border);
  color: var(--e-muted);
  cursor: not-allowed;
}

.select {
  appearance: none;
  padding-right: 2rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--e-muted) 50%),
    linear-gradient(135deg, var(--e-muted) 50%, transparent 50%);
  background-position:
    calc(100% - 16px) 52%,
    calc(100% - 11px) 52%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
  text-overflow: ellipsis;
  cursor: pointer;
}

.select[multiple],
.select[size] {
  padding: var(--sp-1);
  background-image: none;
}

/* Invalid: both the field modifier and aria-invalid on the control work. */
.field.is-invalid .input,
.field.is-invalid .select,
.field.is-invalid .textarea,
.field.is-invalid .input-group,
.input[aria-invalid="true"],
.select[aria-invalid="true"],
.textarea[aria-invalid="true"],
.input-group:has([aria-invalid="true"]) {
  border-color: var(--e-err);
  box-shadow: inset 0 0 0 1px var(--e-err);
}

.field.is-invalid .input-group > .input,
.input-group > .input[aria-invalid="true"] {
  box-shadow: none;
}

.input-group {
  display: flex;
  align-items: stretch;
  width: 100%;
  min-height: 36px;
  border: 1px solid var(--e-border-strong);
  border-radius: var(--r-md);
  background: var(--e-raised);
  transition: border-color var(--dur-fast) var(--ease-out);
}

.input-group:hover {
  border-color: color-mix(in srgb, var(--e-border-strong) 55%, var(--e-fg));
}

.input-group:focus-within {
  border-color: var(--e-focus);
  outline: 2px solid var(--e-focus);
  outline-offset: -1px;
}

.input-group > .input,
.input-group > .select {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 34px;
  border: 0;
  border-radius: 0;
  background-color: transparent;
}

.input-group > .input:focus,
.input-group > .input:focus-visible,
.input-group > .select:focus,
.input-group > .select:focus-visible {
  outline: none;
}

.input-group > :first-child {
  border-top-left-radius: calc(var(--r-md) - 1px);
  border-bottom-left-radius: calc(var(--r-md) - 1px);
}

.input-group > :last-child {
  border-top-right-radius: calc(var(--r-md) - 1px);
  border-bottom-right-radius: calc(var(--r-md) - 1px);
}

.input-group__addon {
  display: flex;
  flex: none;
  align-items: center;
  gap: 6px;
  padding: 0 var(--sp-3);
  background: var(--e-subtle);
  color: var(--e-muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

.input-group__addon:first-child {
  border-right: 1px solid var(--e-border);
}

.input-group__addon:last-child {
  border-left: 1px solid var(--e-border);
}

/* Show / hide password (app.js data-reveal): with both an eye and an eye-off
   icon in the button, the pressed state shows the crossed eye. */
[data-reveal] > .icon-eye-off,
[data-reveal][aria-pressed="true"] > .icon-eye {
  display: none;
}

[data-reveal][aria-pressed="true"] > .icon-eye-off {
  display: inline-block;
}

/* Icon-only addon (search): no well, sits inside the field. */
.input-group__addon:has(> .icon:only-child) {
  padding-right: 0;
  background: transparent;
  border: 0;
}

.input-group__addon:last-child:has(> .icon:only-child) {
  padding: 0 var(--sp-3) 0 0;
}

.input-group > .btn {
  min-height: 34px;
  border: 0;
  border-left: 1px solid var(--e-border);
  border-radius: 0;
  box-shadow: none;
}

.input-group > .btn:last-child {
  border-radius: 0 calc(var(--r-md) - 1px) calc(var(--r-md) - 1px) 0;
}

/* ── 4 · choice controls: checkbox · radio · switch · segmented ─────────── */
.checkbox,
.radio,
.switch {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--sp-2);
  color: var(--e-fg);
  font-size: var(--fs-md);
  line-height: 1.4;
  cursor: pointer;
}

.checkbox > input,
.radio > input,
input.checkbox,
input.radio {
  appearance: none;
  display: inline-grid;
  place-content: center;
  flex: none;
  width: 16px;
  height: 16px;
  margin: 2px 0 0;
  border: 1px solid var(--e-border-strong);
  border-radius: var(--r-sm);
  background: var(--e-raised);
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

input.checkbox,
input.radio {
  margin: 0;
  vertical-align: middle;
}

.radio > input,
input.radio {
  border-radius: 50%;
}

.checkbox > input::before,
input.checkbox::before {
  content: "";
  width: 10px;
  height: 10px;
  background: var(--e-on-primary);
  clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 22%, 84% 6%, 40% 66%);
  scale: 0;
  transition: scale var(--dur-fast) var(--ease-out);
}

.radio > input::before,
input.radio::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--e-on-primary);
  scale: 0;
  transition: scale var(--dur-fast) var(--ease-out);
}

.checkbox > input:hover,
.radio > input:hover,
input.checkbox:hover,
input.radio:hover {
  border-color: color-mix(in srgb, var(--e-border-strong) 50%, var(--e-fg));
}

.checkbox > input:checked,
.checkbox > input:indeterminate,
.radio > input:checked,
input.checkbox:checked,
input.checkbox:indeterminate,
input.radio:checked {
  border-color: var(--e-primary);
  background: var(--e-primary);
}

.checkbox > input:checked::before,
.radio > input:checked::before,
input.checkbox:checked::before,
input.radio:checked::before {
  scale: 1;
}

.checkbox > input:indeterminate::before,
input.checkbox:indeterminate::before {
  clip-path: inset(40% 8% 40% 8%);
  scale: 1;
}

.checkbox > input:focus-visible,
.radio > input:focus-visible,
.switch > input:focus-visible,
input.checkbox:focus-visible,
input.radio:focus-visible,
input.switch:focus-visible {
  outline: 2px solid var(--e-focus);
  outline-offset: 2px;
}

.checkbox > input:disabled,
.radio > input:disabled,
.switch > input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.checkbox:has(> input:disabled),
.radio:has(> input:disabled),
.switch:has(> input:disabled) {
  color: var(--e-muted);
  cursor: not-allowed;
}

.checkbox small,
.radio small,
.switch small {
  display: block;
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

.is-invalid .checkbox > input,
.checkbox > input[aria-invalid="true"] {
  border-color: var(--e-err);
  box-shadow: 0 0 0 1px var(--e-err);
}

/* Switch: <label class="switch"><input type="checkbox" role="switch"> Label</label> */
.switch {
  align-items: center;
}

.switch > input,
input.switch {
  appearance: none;
  position: relative;
  flex: none;
  width: 34px;
  height: 20px;
  margin: 0;
  border-radius: var(--r-full);
  background: var(--e-border-strong);
  cursor: pointer;
  transition: background-color var(--dur-med) var(--ease-out);
}

.switch > input::before,
input.switch::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--e-raised);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition:
    translate var(--dur-med) var(--ease-out),
    background-color var(--dur-med) var(--ease-out);
}

.switch > input:checked,
input.switch:checked {
  background: var(--e-primary);
}

.switch > input:checked::before,
input.switch:checked::before {
  translate: 14px 0;
  background: var(--e-on-primary);
}

/* ext: toggle scope — fields that follow a checkbox, switch or radio without
   script (:has). Inside .toggle-scope, .when-on shows only while the control
   marked .toggle-scope__on is checked, .when-off only while it is not.
     "Same as billing" hides the shipping address:
       <div class="card toggle-scope"> <input type="checkbox" class="toggle-scope__on" checked> …
         <fieldset class="when-off">Shipping address…</fieldset></div>
     Daily Bulk Billing switch shows the route fields:  .when-on on the fields
     Product / Service radios: .toggle-scope__on on the Service radio;
       .when-off on the stock card and goods hints, .when-on on service hints
   One control per scope: do not nest scopes (an inner control would also
   switch the outer scope's .when-* elements). */
.toggle-scope:has(.toggle-scope__on:checked) .when-off,
.toggle-scope:not(:has(.toggle-scope__on:checked)) .when-on {
  display: none;
}

/* Segmented: radios (view switch) or links with aria-current (page switch). */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--e-border);
  border-radius: var(--r-md);
  background: var(--e-surface);
}

.segmented > label,
.segmented > a,
.segmented > button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 30px;
  padding: 0 12px;
  border-radius: calc(var(--r-md) - 2px);
  color: var(--e-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

.segmented > label > input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.segmented > label:hover,
.segmented > a:hover,
.segmented > button:hover {
  color: var(--e-fg);
}

.segmented > label:has(> input:checked),
.segmented > a[aria-current],
.segmented > button[aria-pressed="true"] {
  background: var(--e-raised);
  color: var(--e-fg);
  box-shadow:
    inset 0 0 0 1px var(--e-border-strong),
    var(--shadow-1);
}

.segmented > label:has(> input:focus-visible) {
  outline: 2px solid var(--e-focus);
  outline-offset: 1px;
}

/* ext: stacked / inline groups of checkboxes and radios */
.choices {
  display: grid;
  gap: var(--sp-2);
}

.choices--inline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
}

/* ── 5 · card ───────────────────────────────────────────────────────────── */
.card {
  min-width: 0;
  border: 1px solid var(--e-border);
  border-radius: var(--r-lg);
  background: var(--e-raised);
  box-shadow: var(--shadow-1);
}

.card--flat {
  box-shadow: none;
}

.card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-3);
  min-height: 52px;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--e-border);
}

.card__title {
  color: var(--e-fg);
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  letter-spacing: -0.005em;
}

/* ext: one-line context under or beside a card title */
.card__meta {
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

.card__body {
  padding: var(--sp-4);
}

.card__body--flush {
  padding: 0;
}

.card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--e-border);
  border-radius: 0 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px);
  background: var(--e-subtle);
}

/* ext: a foot that starts at the left — a single leading action (Subscription)
   or a note. .card__foot--note is a sentence, optionally led by an icon:
   <p class="card__foot card__foot--note muted text-sm">{{icon lock}}<span>…</span></p>
   The foot is a flex row, so write a note with inline .money / .code spans
   inside ONE element (<span> or <p>): bare text + spans would become separate
   flex items and break the sentence apart. */
.card__foot--start,
.card__foot--note {
  justify-content: flex-start;
}

.card__foot--note {
  flex-wrap: nowrap;
  align-items: flex-start;
}

.card__foot--note > .icon {
  flex: none;
  margin-top: 2px;
}

/* Cards side by side in a grid row share its height: the foot sits at the
   bottom so the actions of neighbouring cards line up. */
:is(.grid-2, .grid-3, .grid-4, .grid-auto) > .card {
  display: flex;
  flex-direction: column;
}

:is(.grid-2, .grid-3, .grid-4, .grid-auto) > .card > .card__foot {
  margin-top: auto;
}

/* Tables and toolbars sit flush inside cards. */
.card > .table-wrap,
.card__body--flush > .table-wrap {
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.card > .table-wrap:last-child,
.card__body--flush:last-child > .table-wrap:last-child {
  border-radius: 0 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px);
}

/* a header band and the table under it in one card (an editor's lines) */
.card > .card__body + .table-wrap,
.card > .card__body + h2 + .table-wrap,
.card > .card__body + .visually-hidden + .table-wrap {
  border-top: 1px solid var(--e-border);
}

.card > .toolbar {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--e-border);
}

/* ── 6 · kpi ────────────────────────────────────────────────────────────── */
.kpi {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
  container: kpi / inline-size; /* the figure steps down with the tile, never ellipsized */
  padding: var(--sp-4);
  border: 1px solid var(--e-border);
  border-radius: var(--r-lg);
  background: var(--e-raised);
  box-shadow: var(--shadow-1);
  color: var(--e-fg);
  text-decoration: none;
}

a.kpi:hover {
  border-color: color-mix(in srgb, var(--e-fg) 24%, transparent);
  color: var(--e-fg);
}

.kpi__label {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--e-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.kpi__label .icon {
  color: var(--e-faint);
}

/* A KPI figure is never cut: exact ledger figures (₹1,56,903.50 Dr) sit in
   tiles too (05-page-patterns §3.4). The size steps down with the tile's own
   width (container "kpi"); only the Dr / Cr suffix may wrap to a second line. */
.kpi__value {
  color: var(--e-fg);
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-stretch: 106%;
  font-weight: var(--display-weight);
  line-height: 1.12;
  letter-spacing: -0.02em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.kpi__value:has(.money--dr, .money--cr),
.kpi__value:has(> small),
.kpi__value .money--dr,
.kpi__value .money--cr {
  white-space: normal; /* the amount has no spaces, so the only break is before the suffix (Dr, Cr, MTD) */
}

.kpi__value .money--dr::after,
.kpi__value .money--cr::after {
  display: inline-block;
}

@container kpi (max-width: 219.98px) {
  .kpi__value {
    font-size: var(--fs-xl);
  }
}

@container kpi (max-width: 159.98px) {
  .kpi__value {
    font-size: var(--fs-lg);
  }
}

/* a two-up grid of exact ledger figures on a phone (₹1,23,45,678.90 Dr) */
@container kpi (max-width: 139.98px) {
  .kpi__value,
  .kpi-band--compact .kpi__value {
    font-size: var(--fs-md);
    letter-spacing: -0.01em;
  }
}

@container main (max-width: 639.98px) {
  .kpi {
    padding: var(--sp-3);
  }
}

.kpi__value small {
  margin-left: 2px;
  color: var(--e-muted);
  font-family: var(--font-ui);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  letter-spacing: 0;
}

.kpi--lg .kpi__value {
  font-size: var(--fs-3xl);
}

@container kpi (max-width: 259.98px) {
  .kpi--lg .kpi__value {
    font-size: var(--fs-2xl);
  }
}

@container kpi (max-width: 179.98px) {
  .kpi--lg .kpi__value {
    font-size: var(--fs-xl);
  }
}

.kpi__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

.kpi__delta,
.kpi__delta--up,
.kpi__delta--down {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}

.kpi__delta--up,
.kpi--inverse .kpi__delta--down {
  color: var(--e-ok-fg);
}

.kpi__delta--down,
.kpi--inverse .kpi__delta--up {
  color: var(--e-err-fg);
}

.kpi__delta--up::before,
.kpi__delta--down::before {
  content: "";
  width: 8px;
  height: 7px;
  background: currentColor;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

.kpi__delta--down::before {
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

/* ext: KPI breakdown — the exact parts of the headline figure. The rows must
   add up to the tile's value (cash = HQ + JSR; receivables = not yet due +
   overdue), so the compact figure above is never the only number.
   <dl class="kpi__list"><dt>Kolkata HQ</dt><dd>₹40,739.20</dd>…</dl>        */
.kpi__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 3px var(--sp-3);
  margin: var(--sp-2) 0 0;
  padding-top: var(--sp-2);
  border-top: 1px solid var(--e-border);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

/* a label may wrap beside its figure; it is never cut to "Overdue …" */
.kpi__list dt {
  min-width: 0;
  color: var(--e-muted);
}

.kpi__list dd {
  margin: 0;
  color: var(--e-fg);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* a narrow tile (a phone's two-up band, a 1024 four-up band) stacks each part:
   label over figure, so a label is never cut to "Overdue …" */
@container kpi (max-width: 199.98px) {
  .kpi__list {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  .kpi__list dt {
    overflow: visible;
    white-space: normal;
  }

  .kpi__list dd {
    margin-bottom: 4px;
    text-align: left;
  }

  .kpi__list dd:last-child {
    margin-bottom: 0;
  }
}

.kpi > .progress {
  margin-top: var(--sp-2);
}

/* ext: KPI band — one hairline-divided strip of figures that belong together
   (the dashboard's liquidity & working-capital position; a list's totals).
   Not a row of cards: one surface, one border, hairlines between the figures.
   <section class="kpi-band" aria-labelledby="…"><h2 class="visually-hidden" …>…</h2>
     <a class="kpi" href="…">…</a> <div class="kpi">…</div> …</section>
   .kpi-band--compact  smaller figures and padding (a list's totals, a second,
                       quieter row under the main band)
   Columns: as many as fit at ≥ 13 rem (--compact 11 rem); 2 on phones.        */
.kpi-band {
  --_kb-min: 13rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--_kb-min)), 1fr));
  overflow: hidden;
  border: 1px solid var(--e-border);
  border-radius: var(--r-lg);
  background: var(--e-raised);
  box-shadow: var(--shadow-1);
}

.kpi-band > .kpi {
  border: 0;
  border-radius: 0;
  background: transparent;
  /* hairline on the left and top edges; the band's overflow hides the outer ones */
  box-shadow: -1px 0 0 var(--e-border), 0 -1px 0 var(--e-border);
  padding: var(--sp-4) var(--sp-5);
}

.kpi-band > a.kpi:hover {
  background: var(--e-subtle);
}

.kpi-band > a.kpi:focus-visible {
  outline-offset: -3px;
}

.kpi-band--compact {
  --_kb-min: 11rem;
}

.kpi-band--compact > .kpi {
  gap: 2px;
  padding: var(--sp-3) var(--sp-4);
}

/* the quieter row: exact figures one step down (a list's totals, the
   dashboard's P&L and statutory row) — the band above keeps the display size.
   20 px holds down to a ~190 px tile (160 px of content: the dashboard's five
   tiles at 1366); then 16, then 14 px. */
.kpi-band--compact .kpi__value {
  font-size: var(--fs-xl);
}

@container kpi (max-width: 159.98px) {
  .kpi-band--compact .kpi__value {
    font-size: var(--fs-lg);
  }
}

@container kpi (max-width: 139.98px) {
  .kpi-band--compact .kpi__value {
    font-size: var(--fs-md);
  }
}

.kpi-band--compact .kpi__list {
  margin-top: var(--sp-1);
  padding-top: var(--sp-1);
}

/* the compact tiles keep label | figure side by side down to a 150 px tile
   (the dashboard's five tiles at 1366 are ~176 px): a long label wraps
   beside its right-aligned figure, so parts line up like a ledger */
@container kpi (min-width: 150px) and (max-width: 199.98px) {
  .kpi-band--compact .kpi__list {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 2px var(--sp-2);
  }

  .kpi-band--compact .kpi__list dd {
    margin-bottom: 0;
    text-align: right;
  }
}

@container main (max-width: 639.98px) {
  .kpi-band {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .kpi-band > .kpi {
    padding: var(--sp-3);
  }

  /* an odd last tile takes the whole row instead of half of it */
  .kpi-band > .kpi:last-child:nth-child(odd of .kpi) {
    grid-column: 1 / -1;
  }

  /* a list's band folds to one summary line of its two key figures on phones:
     mark those two .kpi--key; the others hide (their figures are in the list) */
  .kpi-band:has(> .kpi--key) > .kpi:not(.kpi--key) {
    display: none;
  }

  .kpi-band:has(> .kpi--key) > .kpi--key .kpi__meta {
    display: none;
  }

  /* …and "odd" counts the key tiles only: hidden tiles before them must not
     push the last key tile onto a row of its own */
  .kpi-band:has(> .kpi--key) > .kpi:last-child:nth-child(odd of .kpi) {
    grid-column: auto;
  }

  .kpi-band:has(> .kpi--key) > .kpi--key:last-child:nth-child(odd of .kpi--key) {
    grid-column: 1 / -1;
  }
}

/* ── 7 · table ──────────────────────────────────────────────────────────── */
/* Wrap every data table. If it can scroll, give it tabindex="0", role="region"
   and an aria-label so keyboard users can scroll it. --sticky caps its height
   so the header row sticks while rows scroll. */
.table-wrap {
  position: relative;
  width: 100%;
  max-width: 100%;
  overflow: auto;
  border: 1px solid var(--e-border);
  border-radius: var(--r-lg);
  /* Scroll cue (functional, not decoration): an edge shadow appears only on
     the side that has more columns; covers slide over it with the content. */
  background:
    linear-gradient(to right, var(--e-raised) 40%, transparent) left / 32px 100% no-repeat local,
    linear-gradient(to left, var(--e-raised) 40%, transparent) right / 32px 100% no-repeat local,
    linear-gradient(to right, color-mix(in srgb, var(--e-fg) 14%, transparent), transparent) left / 12px 100% no-repeat scroll,
    linear-gradient(to left, color-mix(in srgb, var(--e-fg) 14%, transparent), transparent) right / 12px 100% no-repeat scroll,
    var(--e-raised);
  overscroll-behavior-x: contain;
}

.table-wrap--sticky {
  max-height: min(70vh, 720px);
}

.table-wrap:focus-visible {
  outline-offset: -2px;
}

.table {
  width: 100%;
  color: var(--e-fg);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
  font-variant-numeric: tabular-nums;
}

.table caption {
  padding: var(--sp-3) var(--sp-4);
  color: var(--e-muted);
  font-size: var(--fs-sm);
  text-align: left;
  caption-side: top;
}

/* Cells do not wrap: dates, amounts and codes stay on one line and the
   .table-wrap scrolls instead. Opt a free-text column in with .wrap. */
.table th,
.table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--e-border);
  text-align: left;
  vertical-align: middle;
  white-space: nowrap;
}

.table .wrap {
  min-width: 14rem;
  white-space: normal;
}

/* phones: a free-text column may not claim 62% of the screen */
@container main (max-width: 639.98px) {
  .table .wrap {
    min-width: 8.5rem;
  }
}

.table th:first-child,
.table td:first-child {
  padding-left: var(--sp-4);
}

.table th:last-child,
.table td:last-child {
  padding-right: var(--sp-4);
}

.table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--e-surface);
  border-bottom-color: color-mix(in srgb, var(--e-fg) 16%, transparent);
  color: var(--e-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: 1.2;
  white-space: nowrap;
}

.table thead th a {
  color: inherit;
  text-decoration: none;
}

.table thead th a:hover {
  color: var(--e-fg);
}

.table thead th[aria-sort] {
  color: var(--e-fg);
}

.table tbody th {
  font-weight: var(--fw-semibold);
}

.table tbody tr {
  transition: background-color var(--dur-fast) var(--ease-out);
}

.table tbody tr:last-child > * {
  border-bottom: 0;
}

.table--zebra tbody tr:nth-child(even) {
  background: var(--e-subtle);
}

.table tbody tr:hover {
  background: color-mix(in srgb, var(--e-primary-soft) 60%, var(--e-raised));
}

.table tbody tr[aria-selected="true"],
.table tbody tr.is-selected {
  background: var(--e-primary-soft);
}

.table tbody tr[aria-selected="true"] > :first-child,
.table tbody tr.is-selected > :first-child {
  box-shadow: inset 3px 0 0 var(--e-primary);
}

.table tbody tr[data-href] {
  cursor: pointer;
}

.table a {
  color: var(--e-primary-dark);
  font-weight: var(--fw-semibold);
  text-decoration: none;
}

.table a:hover {
  text-decoration: underline;
}

.table .num {
  text-align: right;
  white-space: nowrap;
}

/* A code COLUMN is plain mono text, not a chip per cell. :where() keeps this
   at (0,1,1), under the priority-column (.table .col-mid / .col-wide) and the
   phone-record (.table--records tr > *) hiding, so a code column folds away
   like any other column (G02 bug report). It still beats the .code chip. */
:where(.table) td.code,
:where(.table) th.code {
  display: table-cell;
  border: 0;
  border-bottom: 1px solid var(--e-border);
  border-radius: 0;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.01em;
  line-height: inherit;
  white-space: nowrap;
}

/* a sub-line under a code (the date under an invoice no.) reads in the UI face,
   and so does a plain word in a code column ("Unregistered", "No SKU"):
   <td class="code"><span class="text-ui">Unregistered</span></td> */
.table td.code small,
.table td.code .cell-sub,
.table td.code .text-ui {
  font-family: var(--font-ui);
  letter-spacing: normal;
}

/* Green-primary themes (Dark Professional #10B981, Emerald Business): the theme
   colour is the hue family of Paid (--e-ok). In a table, a document link reads
   in ink with a theme-colour underline, so in a status list green means Paid
   and nothing else (03-brand §6.3, the recorded exception). The same holds for
   a master list's record link in its name column (td[data-col="party"] > a:
   customer, vendor, item), next to Active / Inactive badges. */
:is([data-theme="dark-pro"], [data-theme="emerald"]) .table td.code a,
:is([data-theme="dark-pro"], [data-theme="emerald"]) .table td a.code,
:is([data-theme="dark-pro"], [data-theme="emerald"]) .table td[data-col="party"] > a {
  color: var(--e-fg);
  text-decoration: underline;
  text-decoration-color: var(--e-primary);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 0.2em;
}

.table tbody tr:last-child > .code {
  border-bottom: 0;
}

.table tfoot .code {
  border-top: 1px solid color-mix(in srgb, var(--e-fg) 30%, transparent);
}

.table .actions {
  width: 1%;
  text-align: right;
  white-space: nowrap;
}

.table .actions > * + * {
  margin-left: var(--sp-1);
}

.table td.actions .btn {
  vertical-align: middle;
}

/* a bar inside a cell (share of total, utilisation) keeps a readable length */
.table td > .progress {
  min-width: 4.5rem;
}

/* a bar under its own figure (a share column: "88.3%" then the bar) */
.table td > .progress:not(:first-child) {
  margin-top: 3px;
}

.table .check {
  width: 1%;
  padding-right: 0;
}

/* Secondary line inside a cell: party GSTIN, item SKU, due date… */
.table td small,
.table td .cell-sub,
.table tbody th small,
.table tfoot th small {
  display: block;
  margin-top: 2px;
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
}

.table tfoot th,
.table tfoot td {
  border-top: 1px solid color-mix(in srgb, var(--e-fg) 30%, transparent);
  border-bottom: 0;
  background: var(--e-subtle);
  font-weight: var(--fw-semibold);
}

.table tfoot tr:last-child > * {
  border-bottom: 3px double color-mix(in srgb, var(--e-fg) 30%, transparent);
}

.table--dense {
  font-size: var(--fs-sm);
}

/* 32 px rows for single-line cells (5 + 20 badge / 17 text + 5 + hairline);
   a row with a sub-line grows to about 46 px, so dense lists keep sub-lines for
   the narrower tiers (.sub-narrow / .sub-mid) and single lines at 1366. */
.table--dense th,
.table--dense td {
  padding: 5px 10px;
}

.table--dense .badge {
  height: 20px;
}

.table--dense td.code,
.table--dense th.code {
  font-size: 0.75rem;
}

/* a record list's party name stays on one line with its exception tag (Bill
   of Supply, IGST) so dense rows stay 32 px; it may take a second line only
   when the page is narrower than the table's columns (tablets) */
.table--records td[data-col="party"] {
  white-space: nowrap;
}

@container main (max-width: 1099.98px) {
  .table--records td[data-col="party"] {
    white-space: normal;
  }
}

/* …unless it is a free-text particulars column (a stock movement's "Purchase
   bill … from …"): td.wrap[data-col="party"] wraps at every width */
.table--records td.wrap[data-col="party"] {
  white-space: normal;
}

/* ext: a two-word head over a short figure ("Kolkata HQ", "Purchase (₹)")
   takes two lines so the column stays as narrow as its figures. Unlike .wrap
   it sets no min-width. <th scope="col" class="num th-wrap">Kolkata HQ</th> */
.table thead th.th-wrap {
  white-space: normal;
}

/* an exception tag inside a dense cell: 18 px, so it never sets the row height */
.table--dense td .tag {
  height: 18px;
  padding-inline: 5px;
  vertical-align: 1px;
}

/* the row-actions button of a dense list: 24 px, so it never sets the row height */
.table--dense td.actions {
  padding-block: 4px;
}

.table--dense td.actions .btn--icon.btn--sm {
  --btn-h: 24px;
  width: 24px;
  min-width: 24px;
}



.table--dense th:first-child,
.table--dense td:first-child {
  padding-left: var(--sp-3);
}

.table--dense th:last-child,
.table--dense td:last-child {
  padding-right: var(--sp-3);
}

/* ext: stack rows into label/value cards on phones (<640px).
   Give each <td> a data-label="Column name". */
@media (max-width: 639.98px) {
  .table--stack thead {
    display: block; /* a row group ignores overflow: block, so the 1 px box clips its cells */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }
  .table--stack,
  .table--stack tbody,
  .table--stack tr,
  .table--stack td,
  .table--stack tfoot {
    display: block;
    width: 100%;
  }
  .table--stack tr {
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--e-border);
  }
  .table--stack tbody tr:last-child {
    border-bottom: 0;
  }
  .table--stack td,
  .table--stack td.code,
  .table--stack td:first-child,
  .table--stack td:last-child {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: 4px var(--sp-4);
    border: 0;
    text-align: right;
  }
  /* a cell's sub-lines (GSTIN, stock after sale, CGST/SGST) sit on their own
     right-aligned lines under the value, not beside it */
  .table--stack td {
    flex-wrap: wrap;
    align-items: baseline;
  }
  .table--stack td > small,
  .table--stack td > .cell-sub {
    flex-basis: 100%;
    text-align: right;
  }
  .table--stack td[data-label]::before {
    content: attr(data-label);
    color: var(--e-muted);
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    letter-spacing: normal;
    text-align: left;
  }
  .table--stack td.actions {
    width: auto;
    justify-content: flex-end;
  }
  .table--stack tbody tr[aria-selected="true"] > :first-child {
    box-shadow: none;
  }
  .table--stack tfoot tr > *,
  .table--stack tfoot tr:last-child > * {
    display: flex;
    justify-content: space-between;
    border: 0;
  }
  /* the footer's priority cells fold like the body's (a phone never shows
     the CGST / SGST columns or their label cell) */
  .table--stack tfoot tr > :is(.col-wide, .col-mid) {
    display: none;
  }
}

/* ext: read-only document lines (a saved invoice, quotation, note, purchase):
   <table class="table table--dense table--stack table--lines"> with cells
   td[data-col=line|item|qty|rate|disc|taxable|tax|amount]. On phones a line
   reads as the item name (title) · "60 Boxes × ₹185.00" with the amount on the
   right · GST as a sub-line — not a six-row label/value card. The foot is one
   label + total row. Editors use .table--entry instead. */
@media (max-width: 639.98px) {
  .table--stack.table--lines tbody tr { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--sp-2); row-gap: 2px; padding: var(--sp-3) var(--sp-4); }
  .table--stack.table--lines tbody td { display: block; width: auto; padding: 0; text-align: left; }
  .table--stack.table--lines tbody td::before { content: none; }
  .table--stack.table--lines .col-wide,
  .table--stack.table--lines [data-col="taxable"],
  .table--stack.table--lines [data-col="disc"] { display: none; }
  .table--stack.table--lines tfoot tr { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); background: var(--e-subtle); border-top: 1px solid color-mix(in srgb, var(--e-fg) 30%, transparent); }
  .table--stack.table--lines tfoot tr > :not(.col-wide):not([data-col="taxable"]) { display: block; width: auto; padding: 0; border: 0; background: none; }
  .table--stack.table--lines tbody [data-col="line"] { flex: 0 0 1.25rem; color: var(--e-muted); }
  .table--stack.table--lines tbody [data-col="item"] { flex: 1 0 calc(100% - 1.25rem - var(--sp-2)); min-width: 0; font-weight: var(--fw-semibold); }
  .table--stack.table--lines tbody [data-col="item"] > small { display: block; text-align: left; font-weight: var(--fw-regular); }
  .table--stack.table--lines tbody [data-col="qty"] { margin-inline-start: calc(1.25rem + var(--sp-2)); }
  .table--stack.table--lines tbody [data-col="rate"]::before { content: "× ₹"; color: var(--e-muted); }
  .table--stack.table--lines tbody [data-col="rate"] > small { display: inline; margin-inline-start: var(--sp-1); }
  .table--stack.table--lines [data-col="amount"] { margin-inline-start: auto; text-align: right; }
  .table--stack.table--lines [data-col="amount"]::before { content: "₹"; }
  .table--stack.table--lines [data-col="amount"] > small { display: block; text-align: right; font-weight: var(--fw-regular); }
  .table--stack.table--lines [data-col="tax"]:not(.col-wide) { order: 9; flex: 1 0 100%; text-align: right; color: var(--e-muted); font-size: var(--fs-xs); font-weight: var(--fw-regular); }
  .table--stack.table--lines [data-col="tax"]:not(.col-wide)::before { content: attr(data-label) " "; }
}

/* ext: record rows on phones (< 640 px of page width) — the list pattern.
   A desktop list table becomes one three-line record per row, no sideways
   scroll. Mark the cells a phone shows with data-col; every other cell hides:
     line 1   data-col="no" (document number)  ·  data-col="status"  ·  data-col="actions" (the ⋯ menu)
     line 2   data-col="party" (customer / vendor) + its exception tag
     line 3   data-col="date" · data-col="due"  ·  data-col="amount" (bold, right)
   The amount reads "₹2,192.00 due": ₹ is added, data-note="due" names it. A
   paid document puts data-col="amount" data-note="paid" on its TOTAL cell
   ("₹1,035.00 paid", ok-fg), never a bare 0.00.
   tfoot: a phone-only label cell th.sub-phone[data-col="label"] that names
   the figure ("Outstanding · 240 invoices") and data-col="amount". Sub-lines
   (.sub-narrow / .sub-mid, party GSTIN) hide: the record shows their facts. */
.table .sub-phone {
  display: none;
}

@container main (max-width: 639.98px) {
  .table--records thead {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .table--records,
  .table--records tbody,
  .table--records tfoot {
    display: block;
    width: 100%;
  }

  .table--records tr {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    grid-template-areas:
      "no no status act"
      "party party party act"
      "date due amount act";
    align-items: center;
    gap: 2px var(--sp-2);
    padding: var(--sp-2) var(--sp-4);
    border-bottom: 1px solid var(--e-border);
  }

  .table--records tbody tr:last-child {
    border-bottom: 0;
  }

  .table--records tr > * {
    display: none;
    padding: 0 !important;
    border: 0 !important;
  }

  .table--records tr > [data-col] {
    display: block;
    min-width: 0;
  }

  .table--records [data-col="no"] { grid-area: no; }
  .table--records [data-col="status"] { grid-area: status; justify-self: end; }
  .table--records [data-col="actions"] { grid-area: act; align-self: start; margin: -4px -8px 0 0; }
  .table--records [data-col="actions"] .btn--icon.btn--sm.btn--sm { --btn-h: 36px; width: 36px; min-width: 36px; } /* touch */
  .table--records [data-col="party"] { grid-area: party; white-space: normal; font-weight: var(--fw-medium); }
  .table--records [data-col="date"] { grid-area: date; color: var(--e-muted); font-size: var(--fs-xs); }
  .table--records [data-col="due"] { grid-area: due; font-size: var(--fs-xs); }
  .table--records [data-col="date"] + [data-col="due"]::before,
  .table--records [data-col="date"] ~ [data-col="due"]::before { content: "· "; color: var(--e-muted); }
  .table--records [data-col="amount"] { grid-area: amount; justify-self: end; font-weight: var(--fw-bold); }

  .table--records :is(.sub-narrow, .sub-mid),
  .table--records [data-col="party"] small,
  .table--records [data-col="amount"] small {
    display: none !important;
  }

  /* an exception tag (IGST, Bill of Supply) stays beside the party name */
  .table--records td[data-col="party"] .sub-narrow:has(> .tag) {
    display: inline !important;
  }

  /* with no column heads, every figure carries its unit and its meaning:
     ₹ in front, and data-note after it ("due" on an outstanding balance;
     a paid document shows its total with data-note="paid", in ok-fg) */
  .table--records tbody [data-col="amount"]::before {
    content: "₹";
  }

  .table--records tbody [data-col="amount"][data-note]::after {
    content: " " attr(data-note);
    font-weight: var(--fw-regular);
    color: var(--e-muted);
    font-size: var(--fs-xs);
  }

  .table--records tbody [data-col="amount"][data-note="paid"],
  .table--records tbody [data-col="amount"][data-note="paid"]::after {
    color: var(--e-ok-fg);
  }

  .table--records [data-col="due"] small {
    display: inline;
    margin: 0;
    font-size: inherit;
  }

  .table--records tfoot tr {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "label amount";
    background: var(--e-subtle);
  }

  .table--records tfoot [data-col="label"] { grid-area: label; white-space: normal; }
  .table--records tfoot [data-col="label"] small { display: block !important; }

  /* a second figure on the record (the overdue part of a balance) names
     itself too: data-note on its date / due cell, and ₹ before its .money
     <td class="num" data-col="date" data-note="overdue"><span class="money text-err">37,406.00</span></td> */
  .table--records tbody td[data-col]:not([data-col="amount"])[data-note] .money::before {
    content: "₹";
  }

  .table--records tbody td[data-col]:not([data-col="amount"])[data-note]::after {
    content: " " attr(data-note);
    color: var(--e-muted);
  }

  /* .table--qty: the record's figure is a quantity (stock, movements), not ₹ */
  .table--records.table--qty tbody [data-col="amount"]::before {
    content: none;
  }

  /* .table--by-name: a master list (customers, vendors, items, users) is
     scanned by name and balance — line 1 the name with its exception tags,
     line 2 the GSTIN / HSN as a muted code beside the bold figure, line 3 the
     overdue figure or the price */
  .table--records.table--by-name tbody tr {
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas: "party party act" "no amount act" "date date act";
  }

  .table--records.table--by-name tbody td[data-col="no"] { color: var(--e-muted); font-size: var(--fs-xs); align-self: baseline; }
  .table--records.table--by-name tbody td[data-col="amount"] { align-self: baseline; }
  .table--records.table--by-name tbody td[data-col="date"] { font-size: var(--fs-xs); }

  /* .table--two-line: a ledger-like record (a stock card, purchase history,
     a passbook) — two lines, the label and its date, beside ONE bold figure */
  .table--records.table--two-line tbody tr {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "party amount" "date amount";
    padding-block: 6px;
    line-height: 1.25;
  }

  .table--records.table--two-line tbody [data-col="amount"] { align-self: center; }
  .table--records.table--two-line tbody td.code[data-col="party"] { font-weight: var(--fw-regular); }
  .table--records.table--two-line tbody [data-col="date"] { color: var(--e-muted); }
}

/* ── 8 · badge · chip · tag · code · money · kbd ────────────────────────── */
.badge {
  --_b-bg: var(--e-surface);
  --_b-fg: var(--e-muted);
  --_b-dot: var(--e-faint);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: var(--_b-bg);
  color: var(--_b-fg);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  line-height: 1;
  white-space: nowrap;
  vertical-align: middle;
}

.badge::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--_b-dot);
}

/* Colour meaning follows docs/design/03-brand.md §6.3:
   green Paid/Approved/Active/Converted · amber Partially Paid/Trial/Follow-up ·
   red Unpaid/Overdue/Suspended/Rejected · blue Sent/info ·
   neutral Draft/Cancelled/Expired/Inactive. The text always names the status. */

/* settled / positive */
.badge--paid,
.badge--approved,
.badge--active,
.badge--converted {
  --_b-bg: var(--e-ok-soft);
  --_b-fg: var(--e-ok-fg);
  --_b-dot: var(--e-ok);
}

/* converted has moved on to the next document: arrow instead of dot */
.badge--converted::before {
  width: 7px;
  height: 8px;
  border-radius: 0;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

/* in between / on the clock */
.badge--partial,
.badge--trial {
  --_b-bg: var(--e-warn-soft);
  --_b-fg: var(--e-warn-fg);
  --_b-dot: var(--e-warn);
}

/* owed / refused / blocked */
.badge--unpaid,
.badge--rejected,
.badge--suspended {
  --_b-bg: var(--e-err-soft);
  --_b-fg: var(--e-err-fg);
  --_b-dot: var(--e-err);
}

/* Past due is louder than owed: inverse (solid), so it is distinct without
   colour. It is the ONE past-due cue (03-brand §6.3): every invoice or bill
   past its due date with a balance keeps its SRS status word on the inverse
   red — .badge--unpaid.is-overdue / .badge--partial.is-overdue — with a
   visually-hidden "Overdue, " first, and the due cell (or sub-line) reads
   "Overdue by n days" in .overdue-note. Past-due rows then differ only by the
   Unpaid / Partially Paid word, and never by how late or how big they are.
   <span class="badge badge--partial is-overdue"><span class="visually-hidden">Overdue, </span>Partially Paid</span>
   .badge--overdue ("Overdue" alone) is for filters, counts and legends. */
.badge--overdue,
.badge.is-overdue {
  --_b-bg: var(--e-err-fg);
  --_b-fg: var(--e-raised);
  --_b-dot: var(--e-raised);
}

.badge + .badge {
  margin-left: 4px;
}

/* ext: the past-due sub-line / due cell text */
.overdue-note,
.table td .overdue-note,
.table td small.overdue-note {
  color: var(--e-err-fg);
  font-weight: var(--fw-semibold);
}

/* informational / in flight */
.badge--sent,
.badge--info {
  --_b-bg: var(--e-info-soft);
  --_b-fg: var(--e-info-fg);
  --_b-dot: var(--e-info);
}

/* unfinished: dashed outline, no fill */
.badge--draft {
  --_b-bg: transparent;
  --_b-fg: var(--e-muted);
  --_b-dot: transparent;
  border: 1px dashed var(--e-border-strong);
}

.badge--draft::before {
  box-shadow: inset 0 0 0 1.5px var(--e-faint);
}

/* void / lapsed / dormant: hollow dot */
.badge--cancelled,
.badge--expired,
.badge--inactive {
  --_b-dot: transparent;
}

.badge--cancelled::before,
.badge--expired::before,
.badge--inactive::before {
  box-shadow: inset 0 0 0 1.5px var(--e-faint);
}

.badge--neutral {
  --_b-dot: var(--e-faint);
}

/* ext: count badge without a dot (tab counts, nav counters) */
.badge--count::before {
  display: none;
}

.badge--count {
  min-width: 20px;
  height: 18px;
  padding: 0 6px;
  justify-content: center;
  border-radius: var(--r-full);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 10px;
  border: 1px solid color-mix(in srgb, var(--e-fg) 18%, transparent);
  border-radius: var(--r-full);
  background: var(--e-raised);
  color: var(--e-fg);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

a.chip:hover,
button.chip:hover,
label.chip:hover {
  border-color: color-mix(in srgb, var(--e-fg) 36%, transparent);
  background: var(--e-subtle);
  color: var(--e-fg);
}

.chip[aria-pressed="true"],
.chip[aria-current],
.chip.is-active,
label.chip:has(> input:checked) {
  border-color: var(--e-primary);
  background: var(--e-primary-soft);
  color: var(--e-primary-dark);
}

label.chip {
  position: relative;
  cursor: pointer;
}

label.chip > input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

label.chip:has(> input:focus-visible) {
  outline: 2px solid var(--e-focus);
  outline-offset: 2px;
}

.chip__count {
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}

.chip__remove {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  margin-right: -4px;
  border-radius: 50%;
  color: var(--e-muted);
  text-decoration: none;
}

.chip__remove:hover {
  background: var(--e-surface);
  color: var(--e-fg);
}

.chip__remove .icon {
  width: 12px;
  height: 12px;
  vertical-align: 0;
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 20px;
  padding: 0 6px;
  border: 1px solid var(--e-border);
  border-radius: var(--r-sm);
  background: var(--e-surface);
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: nowrap;
  vertical-align: middle;
}

/* an exception fact on a record (Over limit, Low stock): the tag stays
   neutral and only its leading icon takes the warning colour
   <span class="tag tag--warn" title="…">{{icon triangle-alert icon--sm}}Over limit</span> */
.tag--warn > .icon {
  color: var(--e-warn-fg);
}

.tag--primary {
  border-color: color-mix(in srgb, var(--e-primary) 30%, transparent);
  background: var(--e-primary-soft);
  color: var(--e-primary-dark);
}

.code {
  display: inline-block;
  padding: 1px 6px;
  border: 1px solid var(--e-border);
  border-radius: var(--r-sm);
  background: var(--e-subtle);
  color: var(--e-fg);
  font-family: var(--font-mono);
  font-size: max(12px, 0.8125em); /* never below the 12 px screen floor (04 §3.1) */
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.45;
  white-space: nowrap;
  font-variant-ligatures: none;
  vertical-align: baseline;
}

/* in a feed of facts (activity, today so far) a code is read, not copied:
   tint only, no box — the bordered chip stays for copy targets */
.timeline .code,
.dl--stack .code {
  padding-inline: 4px;
  border-color: transparent;
}

a.code {
  color: var(--e-primary-dark);
  text-decoration: none;
}

a.code:hover {
  border-color: var(--e-primary);
}

.money {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  white-space: nowrap;
}

.money--neg {
  color: var(--e-err-fg);
}

/* Dr / Cr suffix is generated — write only the amount. */
.money--dr::after,
.money--cr::after {
  margin-left: 0.3em;
  color: var(--e-muted);
  font-size: 0.86em;
  font-weight: var(--fw-semibold);
}

.money--dr::after {
  content: "Dr";
}

.money--cr::after {
  content: "Cr";
}

.kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border: 1px solid color-mix(in srgb, var(--e-fg) 22%, transparent);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  background: var(--e-raised);
  color: var(--e-muted);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  vertical-align: 1px;
}

/* ── 9 · tabs ───────────────────────────────────────────────────────────── */
.tabs {
  display: flex;
  gap: var(--sp-5);
  overflow-x: auto;
  border-bottom: 1px solid var(--e-border);
  scrollbar-width: none;
}

.tabs::-webkit-scrollbar {
  display: none;
}

.tab {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  margin-bottom: -1px;
  padding: 10px 2px 9px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--e-muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.tab:hover {
  border-bottom-color: color-mix(in srgb, var(--e-fg) 24%, transparent);
  color: var(--e-fg);
}

.tab[aria-selected="true"],
.tab[aria-current="page"],
.tab[aria-current="true"] {
  border-bottom-color: var(--e-primary);
  color: var(--e-fg);
}

.tab:focus-visible {
  outline-offset: -2px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}

/* ── 10 · dialog · drawer ───────────────────────────────────────────────── */
/* <dialog class="dialog"> opened by <button commandfor="id" command="show-modal">
   (native invoker commands — no script) or [popover] + popovertarget.
   Close with <form method="dialog"> or command="close". */
.dialog,
.drawer {
  padding: 0;
  border: 1px solid var(--e-border);
  background: var(--e-raised);
  color: var(--e-fg);
  box-shadow: var(--shadow-pop);
}

.dialog {
  width: min(560px, calc(100vw - 2 * var(--sp-4)));
  max-height: calc(100dvh - 2 * var(--sp-6));
  margin: auto;
  overflow: auto;
  border-radius: var(--r-lg);
}

.dialog--sm {
  width: min(420px, calc(100vw - 2 * var(--sp-4)));
}

.dialog--lg {
  width: min(820px, calc(100vw - 2 * var(--sp-4)));
}

.dialog::backdrop,
.drawer::backdrop {
  background: rgba(11, 21, 38, 0.48);
  background: var(--e-overlay);
}

.dialog__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) var(--sp-3);
}

.dialog__title {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-stretch: 106%;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: var(--lh-snug);
}

.dialog__head .btn--icon {
  margin: -6px -8px 0 0;
}

.dialog__body {
  padding: 0 var(--sp-5) var(--sp-5);
  color: var(--e-fg);
}

.dialog__body > * + * {
  margin-top: var(--sp-3);
}

.dialog__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--e-border);
  background: var(--e-subtle);
}

.dialog__foot .dialog__note {
  margin-right: auto;
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

/* ext: render a dialog in the page flow (stacked-state documentation) */
.dialog--static {
  position: static;
  display: block;
  width: 100%;
  max-width: 560px;
  margin: 0;
  box-shadow: var(--shadow-2);
}

.dialog--static.dialog--sm {
  max-width: 420px;
}

.dialog--static.dialog--lg {
  max-width: 820px;
}

.drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(480px, 100vw);
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  border-width: 0 0 0 1px;
  border-radius: 0;
}

.drawer--left {
  inset: 0 auto 0 0;
  border-width: 0 1px 0 0;
}

.drawer[open],
.drawer:popover-open {
  display: flex;
  flex-direction: column;
}

.drawer > .dialog__body {
  flex: 1 1 auto;
  overflow: auto;
}

.drawer > .dialog__head {
  border-bottom: 1px solid var(--e-border);
  padding-bottom: var(--sp-4);
}

.drawer > .dialog__head + .dialog__body {
  padding-top: var(--sp-4);
}

@keyframes erp-dialog-in {
  from {
    opacity: 0;
    translate: 0 8px;
  }
}

@keyframes erp-drawer-in {
  from {
    translate: 100% 0;
  }
}

@keyframes erp-drawer-in-left {
  from {
    translate: -100% 0;
  }
}

@keyframes erp-fade-in {
  from {
    opacity: 0;
  }
}

.dialog[open]:not(.dialog--static),
.dialog:popover-open {
  animation: erp-dialog-in var(--dur-med) var(--ease-out);
}

.drawer[open],
.drawer:popover-open {
  animation: erp-drawer-in var(--dur-med) var(--ease-out);
}

.drawer--left[open],
.drawer--left:popover-open {
  animation-name: erp-drawer-in-left;
}

.dialog[open]::backdrop,
.drawer[open]::backdrop {
  animation: erp-fade-in var(--dur-med) var(--ease-out);
}

/* ── 11 · menu (dropdown on <details>) ──────────────────────────────────── */
/* Plain links/buttons inside — no role="menu" (it would promise arrow-key
   handling we do not provide). app.js closes an open menu on outside click
   and Escape. */
.menu {
  position: relative;
  display: inline-block;
}

.menu > summary {
  list-style: none;
}

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

.menu > summary .icon-chevron-down {
  transition: rotate var(--dur-fast) var(--ease-out);
}

.menu[open] > summary .icon-chevron-down {
  rotate: 180deg;
}

.menu__list {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: var(--z-drawer);
  display: grid;
  gap: 1px;
  min-width: 210px;
  max-width: min(320px, calc(100vw - 32px));
  padding: var(--sp-1);
  border: 1px solid var(--e-border);
  border-radius: var(--r-md);
  background: var(--e-raised);
  box-shadow: var(--shadow-2);
}

.menu[open] > .menu__list {
  animation: erp-dialog-in var(--dur-fast) var(--ease-out);
}

.menu--end > .menu__list {
  right: 0;
  left: auto;
}

.menu--up > .menu__list {
  top: auto;
  bottom: calc(100% + 4px);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: 32px;
  padding: 6px 10px;
  border-radius: var(--r-sm);
  color: var(--e-fg);
  font-size: var(--fs-md);
  line-height: 1.3;
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
}

.menu__item:hover,
.menu__item:focus-visible {
  background: var(--e-surface);
  color: var(--e-fg);
}

.menu__item:focus-visible {
  outline-offset: -2px;
}

.menu__item .icon {
  color: var(--e-muted);
}

.menu__item .kbd {
  margin-left: auto;
}

.menu__item[aria-current],
.menu__item[aria-checked="true"] {
  color: var(--e-primary-dark);
  font-weight: var(--fw-semibold);
}

.menu__item--danger,
.menu__item--danger .icon {
  color: var(--e-err-fg);
}

.menu__item--danger:hover {
  background: var(--e-err-soft);
  color: var(--e-err-fg);
}

.menu__label {
  padding: 8px 10px 4px;
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}

.menu__sep {
  margin: var(--sp-1) calc(-1 * var(--sp-1));
  border: 0;
  border-top: 1px solid var(--e-border);
}

/* ── 12 · alert · toast ─────────────────────────────────────────────────── */
.alert {
  --_a: var(--e-info);
  --_a-soft: var(--e-info-soft);
  --_a-fg: var(--e-info-fg);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 2px var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  /* one even hairline in the status hue: the icon carries the accent (no side stripe) */
  border: 1px solid color-mix(in srgb, var(--_a) 36%, transparent);
  border-radius: var(--r-md);
  background: var(--_a-soft);
  color: var(--e-fg);
  font-size: var(--fs-md);
}

/* ext: one-line inline warning next to its cause (an editor's credit limit,
   a stock warning in a form): title and body run on as one sentence. */
.alert--compact {
  align-items: center;
  padding: 6px var(--sp-3);
  font-size: var(--fs-sm);
}

.alert--compact > .icon {
  grid-row: auto;
  margin-top: 0;
}

.alert--compact > .alert__body a {
  white-space: nowrap;
}

.alert--info {
  --_a: var(--e-info);
  --_a-soft: var(--e-info-soft);
  --_a-fg: var(--e-info-fg);
}

.alert--ok {
  --_a: var(--e-ok);
  --_a-soft: var(--e-ok-soft);
  --_a-fg: var(--e-ok-fg);
}

.alert--warn {
  --_a: var(--e-warn);
  --_a-soft: var(--e-warn-soft);
  --_a-fg: var(--e-warn-fg);
}

.alert--err {
  --_a: var(--e-err);
  --_a-soft: var(--e-err-soft);
  --_a-fg: var(--e-err-fg);
}

/* ext: "not real" notice (demo sign-in, sandbox) — the same visual language
   as the demo chip and the Draft badge: a dashed outline and the flask icon,
   no status colour (03-brand §5.1). <div class="alert alert--demo" role="note">
   {{icon flask-conical}}<p class="alert__title">Demo sign-in</p>… */
.alert--demo {
  --_a: var(--e-muted);
  --_a-soft: var(--e-raised);
  --_a-fg: var(--e-fg);
  border: 1px dashed var(--e-border-strong);
}

.alert > .icon {
  grid-row: 1 / span 3;
  margin-top: 2px;
  color: var(--_a-fg);
}

.alert__title {
  grid-column: 2;
  color: var(--_a-fg);
  font-weight: var(--fw-semibold);
}

.alert__body {
  grid-column: 2;
  color: var(--e-fg);
}

.alert__body a {
  color: var(--_a-fg);
  font-weight: var(--fw-semibold);
}

.alert__actions {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

.alert > [data-dismiss],
.toast > [data-dismiss] {
  grid-column: 3;
  grid-row: 1;
  margin: -6px -8px -6px 0;
}

.alert:not(:has(> .icon)) {
  grid-template-columns: 1fr auto;
}

.alert:not(:has(> .icon)) > .alert__title,
.alert:not(:has(> .icon)) > .alert__body,
.alert:not(:has(> .icon)) > .alert__actions {
  grid-column: 1;
}

.alert:not(:has(> .icon)) > [data-dismiss] {
  grid-column: 2;
}

/* ext: fixed stack in the corner of the viewport. On a page with a sticky
   .page-bar (editors, settings, wizards) the stack moves to the top right,
   under the topbar, so a message never covers the next action (Save …). */
.toast-stack {
  position: fixed;
  right: var(--sp-4);
  bottom: var(--sp-4);
  z-index: var(--z-toast);
  display: grid;
  gap: var(--sp-2);
  width: min(380px, calc(100vw - 2 * var(--sp-4)));
}

/* a detail page's hidden edit bar (.page-bar--edit) does not count until its
   #edit panel is open (G05 fix: toasts no longer cover the page head's actions) */
body:has(.page > .page-bar:not(.page-bar--edit)) .toast-stack,
body:has(#edit:target):has(.page > .page-bar--edit) .toast-stack {
  top: calc(var(--topbar-h) + var(--sp-3));
  bottom: auto;
}

.toast {
  --_t: var(--e-primary);
  --_t-fg: var(--e-primary-dark);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 2px var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--e-border);
  border-radius: var(--r-md);
  background: var(--e-raised);
  box-shadow: var(--shadow-pop);
  color: var(--e-fg);
  animation: erp-dialog-in var(--dur-med) var(--ease-out);
}

.toast--ok {
  --_t: var(--e-ok);
  --_t-fg: var(--e-ok-fg);
}

.toast--warn {
  --_t: var(--e-warn);
  --_t-fg: var(--e-warn-fg);
}

.toast--err {
  --_t: var(--e-err);
  --_t-fg: var(--e-err-fg);
}

.toast--info {
  --_t: var(--e-info);
  --_t-fg: var(--e-info-fg);
}

.toast > .icon {
  grid-row: 1 / span 3;
  margin-top: 2px;
  color: var(--_t-fg);
}

.toast__title {
  grid-column: 2;
  font-weight: var(--fw-semibold);
}

.toast__body {
  grid-column: 2;
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

.toast__actions {
  grid-column: 2;
  display: flex;
  gap: var(--sp-3);
  margin-top: var(--sp-1);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

/* ── 13 · empty state ───────────────────────────────────────────────────── */
.empty {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
  padding: var(--sp-12) var(--sp-6);
  text-align: center;
}

.empty--compact {
  padding: var(--sp-8) var(--sp-4);
}

.empty__icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  margin-bottom: var(--sp-2);
  border: 1px solid var(--e-border);
  border-radius: var(--r-lg);
  background: var(--e-surface);
  color: var(--e-muted);
}

.empty__icon .icon {
  width: 22px;
  height: 22px;
  stroke-width: 1.6;
}

.empty__title {
  color: var(--e-fg);
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
}

.empty__body {
  max-width: 48ch;
  color: var(--e-muted);
}

/* ext */
.empty__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
}

/* ── 14 · toolbar · pagination ──────────────────────────────────────────── */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

.toolbar > .input,
.toolbar > .select {
  width: auto;
}

/* a select with long options (all customers) never pushes the row apart */
.toolbar > .select {
  max-width: 16rem;
}

/* ext: growing search slot and a spacer pushing the rest right */
.toolbar__search {
  flex: 1 1 240px;
  max-width: 380px;
}

.toolbar__spacer {
  flex: 1 1 auto;
}

/* ext: the filters of a list toolbar. On desktop the disclosure is invisible
   (its controls sit in the toolbar row, one line at 1366); on phones (< 640 px
   of page width) it folds to one "Filters (n)" button next to the search.
   <details class="toolbar__filters"><summary class="btn btn--secondary">…Filters (1)</summary>
     select… select… <button>Apply</button></details>
   Needs ::details-content (Chrome 131+); elsewhere it stays a disclosure.   */
.toolbar__filters {
  display: contents;
}

.toolbar__filters::details-content {
  display: contents;
  content-visibility: visible;
}

.toolbar__filters > summary {
  display: none;
}

.toolbar__filters > .select {
  width: auto;
  max-width: 16rem;
}

@supports not selector(::details-content) {
  .toolbar__filters {
    display: block;
  }

  .toolbar__filters > summary {
    display: inline-flex;
  }
}

@container main (max-width: 639.98px) {
  .toolbar > .toolbar__search {
    flex: 1 1 0;
    max-width: none;
  }

  .toolbar__filters {
    display: block;
  }

  .toolbar__filters > summary {
    display: inline-flex;
    list-style: none;
  }

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

  .toolbar__filters:not([open])::details-content {
    display: block;
    content-visibility: hidden;
  }

  .toolbar__filters[open] {
    flex-basis: 100%;
  }

  .toolbar__filters[open]::details-content {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
  }

  .toolbar__filters > .select {
    flex: 1 1 100%;
    max-width: none;
  }
}

.toolbar__label {
  color: var(--e-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.toolbar__sep {
  align-self: stretch;
  width: 1px;
  margin: 2px var(--sp-1);
  background: var(--e-border);
}

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

.pagination ul,
.pagination ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pagination li > a,
.pagination li > span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-width: 32px;
  height: 32px;
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  color: var(--e-fg);
  font-weight: var(--fw-semibold);
  text-decoration: none;
}

.pagination li > a:hover {
  border-color: color-mix(in srgb, var(--e-fg) 22%, transparent);
  background: var(--e-raised);
  color: var(--e-fg);
}

.pagination [aria-current="page"] {
  border-color: var(--e-primary);
  background: var(--e-primary-soft);
  color: var(--e-primary-dark);
}

.pagination [aria-disabled="true"] {
  color: var(--e-muted);
  opacity: 0.6;
  pointer-events: none;
}

.pagination li > span:not([aria-current]):not([aria-disabled]) {
  min-width: 20px;
  padding: 0;
  color: var(--e-muted);
}

.pagination__summary {
  margin: 0;
}

/* ── 15 · stepper ───────────────────────────────────────────────────────── */
.stepper {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: erp-step;
}

.step {
  position: relative;
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
  padding-right: var(--sp-3);
  color: var(--e-muted);
  font-size: var(--fs-sm);
  counter-increment: erp-step;
}

.step:last-child {
  flex: 0 0 auto;
  padding-right: 0;
}

/* connector */
.step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 12px;
  left: 32px;
  right: 8px;
  height: 2px;
  border-radius: 1px;
  background: var(--e-border);
}

.step.is-done:not(:last-child)::after {
  background: var(--e-primary);
}

.step__marker {
  position: relative;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: 1.5px solid var(--e-border-strong);
  border-radius: 50%;
  background: var(--e-raised);
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
}

.step__marker:empty::before {
  content: counter(erp-step);
}

.step__label {
  color: inherit;
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
}

.step__meta {
  color: var(--e-muted);
  font-size: var(--fs-xs);
}

.step.is-current {
  color: var(--e-fg);
}

.step.is-current .step__marker {
  border: 2px solid var(--e-primary);
  color: var(--e-primary-dark);
  box-shadow: 0 0 0 3px var(--e-primary-soft);
}

.step.is-done {
  color: var(--e-fg);
}

.step.is-done .step__marker {
  border-color: var(--e-primary);
  background: var(--e-primary);
  color: transparent;
}

.step.is-done .step__marker::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 11px;
  height: 11px;
  background: var(--e-on-primary);
  clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 22%, 84% 6%, 40% 66%);
}

a.step {
  text-decoration: none;
}

a.step:hover .step__label {
  text-decoration: underline;
}

@media (max-width: 639.98px) {
  .step__label {
    font-size: var(--fs-xs);
  }
  .step:not(.is-current) .step__meta {
    display: none;
  }
}

/* ── 16 · avatar · progress · timeline ──────────────────────────────────── */
.avatar {
  --size: 32px;
  display: inline-grid;
  flex: none;
  place-items: center;
  width: var(--size);
  height: var(--size);
  overflow: hidden;
  border-radius: 50%;
  background: var(--e-primary-soft);
  color: var(--e-primary-dark);
  font-size: calc(var(--size) * 0.38);
  font-weight: var(--fw-bold);
  letter-spacing: 0.02em;
  line-height: 1;
  text-transform: uppercase;
  user-select: none;
  vertical-align: middle;
}

.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar--sm {
  --size: 24px;
}

.avatar--lg {
  --size: 40px;
}

.avatar--xl {
  --size: 56px;
}

/* <progress class="progress" value="62" max="100">62%</progress>, or
   <div class="progress" role="progressbar" aria-valuenow="62" … style="--value:62%"> */
/* default fill is the data ink (--e-data), not the theme colour: a green
   theme primary would read as "paid / ok". Status fills only when the value is
   a warning (--warn / --err) or a completed good state (--ok). */
.progress {
  --_p: var(--e-data);
  appearance: none;
  position: relative;
  display: block;
  width: 100%;
  height: 6px;
  overflow: hidden;
  border: 0;
  border-radius: var(--r-full);
  background: var(--e-surface);
  color: var(--_p);
}

.progress::-webkit-progress-bar {
  border-radius: var(--r-full);
  background: var(--e-surface);
}

.progress::-webkit-progress-value {
  border-radius: var(--r-full);
  background: var(--_p);
}

.progress::-moz-progress-bar {
  border-radius: var(--r-full);
  background: var(--_p);
}

div.progress::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--value, 0%);
  border-radius: inherit;
  background: var(--_p);
}

.progress--lg {
  height: 10px;
}

.progress--ok {
  --_p: var(--e-ok);
}

.progress--warn {
  --_p: var(--e-warn);
}

.progress--err {
  --_p: var(--e-err);
}

.timeline {
  position: relative;
  display: grid;
  gap: var(--sp-4);
  margin: 0;
  padding: 0 0 0 var(--sp-6);
  list-style: none;
}

.timeline::before {
  content: "";
  position: absolute;
  top: 8px;
  bottom: 8px;
  left: 6px;
  width: 2px;
  border-radius: 1px;
  background: var(--e-border);
}

.timeline > li {
  --_tl: var(--e-border-strong);
  position: relative;
  display: grid;
  gap: 2px;
}

.timeline > li::before {
  content: "";
  position: absolute;
  top: 3px;
  left: calc(-1 * var(--sp-6));
  width: 14px;
  height: 14px;
  border: 2px solid var(--_tl);
  border-radius: 50%;
  background: var(--e-raised);
}

.timeline > li.is-ok {
  --_tl: var(--e-ok);
}

.timeline > li.is-warn {
  --_tl: var(--e-warn);
}

.timeline > li.is-err {
  --_tl: var(--e-err);
}

.timeline > li.is-info {
  --_tl: var(--e-info);
}

.timeline > li.is-current {
  --_tl: var(--e-primary);
}

.timeline > li.is-current::before {
  background: var(--e-primary);
  box-shadow: 0 0 0 3px var(--e-primary-soft);
}

.timeline__time {
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}

.timeline__title {
  color: var(--e-fg);
  font-weight: var(--fw-semibold);
}

.timeline__body {
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

/* ── 17 · tree · definition list · divider · skeleton ───────────────────── */
/* Chart of Accounts. Groups are <details><summary class="tree__row">, leaves
   are <div class="tree__row">. No role="tree" (no arrow-key script). */
.tree,
.tree ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tree ul {
  margin-left: 15px;
  padding-left: 10px;
  border-left: 1px solid var(--e-border);
}

.tree__row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 36px;
  padding: 4px var(--sp-2);
  border-radius: var(--r-sm);
  color: var(--e-fg);
}

.tree__row:hover {
  background: var(--e-subtle);
}

.tree summary.tree__row {
  list-style: none;
  cursor: pointer;
  font-weight: var(--fw-semibold);
}

.tree summary.tree__row::-webkit-details-marker {
  display: none;
}

/* chevron for groups, matching spacer for leaves */
.tree__row::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
}

.tree summary.tree__row::before {
  background: var(--e-muted);
  -webkit-mask: var(--_icon-chevron) center / 12px no-repeat;
  mask: var(--_icon-chevron) center / 12px no-repeat;
  transition: rotate var(--dur-fast) var(--ease-out);
}

.tree details[open] > summary.tree__row::before {
  rotate: 90deg;
}

:root {
  --_icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 5 7 7-7 7'/%3E%3C/svg%3E");
}

.tree__label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tree__meta {
  color: var(--e-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-regular);
  white-space: nowrap;
}

.tree__amount {
  flex: none;
  min-width: 9rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.tree > li > details > summary.tree__row {
  font-size: var(--fs-md);
  text-transform: none;
}

.tree__row .code {
  flex: none;
}

a.tree__row {
  text-decoration: none;
}

.tree__row[aria-current] {
  background: var(--e-primary-soft);
}

.dl {
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  gap: var(--sp-2) var(--sp-6);
  margin: 0;
}

.dl dt {
  color: var(--e-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1.55;
}

.dl dd {
  min-width: 0;
  margin: 0;
  color: var(--e-fg);
  overflow-wrap: anywhere;
}

.dl--stack {
  grid-template-columns: 1fr;
  gap: 0;
}

.dl--stack dd + dt {
  margin-top: var(--sp-3);
}

/* ext: pairs wrapped in <div> flow into responsive columns */
.dl--grid {
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--sp-4) var(--sp-6);
}

.dl--grid > div {
  display: grid;
  align-content: start;
  gap: 2px;
}

/* a long value (an address with its contact) takes a row of its own, so it
   does not make every pair beside it as tall as itself:
   <div class="dl__wide"><dt>Billing address</dt><dd>…</dd></div> */
.dl--grid > .dl__wide {
  grid-column: 1 / -1;
}

/* ext: one summary line of facts (the party line under an editor's Customer):
   <dl class="dl dl--inline"><div><dt>GSTIN</dt><dd>…</dd></div> …</dl>
   pairs run on one line, separated by a middot; they wrap as whole pairs.
   A wrapped line never STARTS with the middot: every pair carries its
   separator in a fixed slot and the list is pulled one slot left under a
   clip, so the slot of whichever pair starts a line falls outside
   (batch-2/3 fix, G04 · G06 · G08 · G09). Below 640 px of main the facts
   stack one per line, without separators, and long values wrap. */
.dl--inline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 0;
  font-size: var(--fs-sm);
}

.dl--inline > div {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  white-space: nowrap;
}

.dl--inline > div + div::before {
  content: "·";
  content: "·" / "";
  margin: 0 var(--sp-2);
  color: var(--e-faint);
}

/* the slot is exactly the old separator (8 px + the 13 px middot + 8 px), so
   an unwrapped line sets its facts where it always did */
@container main (min-width: 640px) {
  .dl--inline {
    --_sep: calc(2 * var(--sp-2) + 0.334em);
    margin-left: calc(-1 * (var(--_sep) + 5px));
    clip-path: inset(-4px -4px -4px calc(var(--_sep) + 5px));
  }

  .dl--inline > div::before,
  .dl--inline > div + div::before {
    content: "·";
    content: "·" / "";
    flex: none;
    width: var(--_sep);
    margin: 0;
    color: var(--e-faint);
    text-align: center;
  }
}

@container main (max-width: 639.98px) {
  .dl--inline {
    flex-direction: column;
    align-items: flex-start;
  }

  .dl--inline > div {
    flex-wrap: wrap;
    max-width: 100%;
    white-space: normal;
  }

  .dl--inline > div + div::before {
    content: none;
  }

  .dl--inline dd {
    overflow-wrap: anywhere;
  }

  /* a fact led by an icon (the chosen file) keeps its value beside the icon */
  .dl--inline > div > dt:has(> .icon) + dd {
    flex: 1 1 0;
    min-width: 0;
  }
}

.dl--inline dt {
  font-size: inherit;
  line-height: var(--lh-ui);
}

.dl--inline dd {
  overflow-wrap: normal;
}

/* ext: disclosure — a quiet "show more" for secondary details (addresses,
   tax details of a line). <details class="disclosure"><summary>Billing &amp;
   shipping address</summary>…</details>                                     */
.disclosure > summary {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  width: fit-content;
  color: var(--e-primary-dark);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  list-style: none;
  cursor: pointer;
}

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

.disclosure > summary::before {
  content: "";
  flex: none;
  width: 12px;
  height: 12px;
  background: currentColor;
  -webkit-mask: var(--_icon-chevron) center / 11px no-repeat;
  mask: var(--_icon-chevron) center / 11px no-repeat;
  transition: rotate var(--dur-fast) var(--ease-out);
}

.disclosure[open] > summary::before {
  rotate: 90deg;
}

.disclosure > summary:hover {
  text-decoration: underline;
}

.disclosure[open] > :not(summary) {
  margin-top: var(--sp-2);
}

/* ext: document totals — Taxable, CGST, SGST, Round off, Grand total */
.dl--totals {
  grid-template-columns: 1fr auto;
  gap: var(--sp-2) 0; /* no column gap: the total's rule runs unbroken */
  font-variant-numeric: tabular-nums;
}

.dl--totals dt {
  padding-right: var(--sp-6);
}

.dl--totals dt {
  color: var(--e-muted);
  font-size: var(--fs-md);
  line-height: var(--lh-ui);
}

.dl--totals dd {
  text-align: right;
  white-space: nowrap;
}

/* the grand total sits between a rule and a double rule, like a ledger's
   total line; label and figure share one unbroken rule above */
.dl--totals .dl__total {
  padding-top: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-top: 1px solid color-mix(in srgb, var(--e-fg) 30%, transparent);
  border-bottom: 3px double color-mix(in srgb, var(--e-fg) 30%, transparent);
  color: var(--e-fg);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
}

/* the one number the document exists to produce: an Archivo display figure;
   its label stays in the UI face, on the figure's baseline side */
.dl--totals dt.dl__total {
  display: flex;
  align-items: flex-end;
  align-self: stretch;
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
}

.dl--totals dd.dl__total {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-stretch: 106%;
  font-weight: var(--display-weight);
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 479.98px) {
  .dl:not(.dl--totals):not(.dl--grid):not(.dl--inline) {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .dl:not(.dl--totals):not(.dl--grid):not(.dl--inline) dd + dt {
    margin-top: var(--sp-3);
  }
}

.divider {
  height: 0;
  margin: var(--sp-4) 0;
  border: 0;
  border-top: 1px solid var(--e-border);
}

/* labelled divider: <div class="divider" role="separator">or</div> */
.divider:not(hr) {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  height: auto;
  border: 0;
  color: var(--e-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.divider:not(hr)::before,
.divider:not(hr)::after {
  content: "";
  flex: 1 1 auto;
  border-top: 1px solid var(--e-border);
}

.skeleton {
  position: relative;
  display: block;
  width: 100%;
  height: 0.9em;
  overflow: hidden;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--e-fg) 9%, var(--e-raised));
}

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  translate: -100% 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--e-raised) 70%, transparent), transparent);
  animation: erp-shimmer 1.4s ease-in-out infinite;
}

.skeleton--title {
  width: 40%;
  height: 1.4em;
}

.skeleton--block {
  height: 120px;
  border-radius: var(--r-lg);
}

.skeleton--circle {
  width: 32px;
  height: 32px;
  border-radius: 50%;
}

@keyframes erp-shimmer {
  to {
    translate: 100% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton::after {
    display: none;
  }
  .btn[aria-busy="true"]::before {
    animation: none;
    border-right-color: currentColor;
    border-style: dotted;
  }
}

/* ── 18 · brand (promoted from partials/brand/*.html — Brand Guardian spec,
        docs/design/03-brand.md §3–§5; geometry is fixed logo geometry) ──── */
.brand-lockup {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  margin: -4px;
  padding: 4px;
  border-radius: var(--r-md);
  color: var(--e-sidebar-fg);
  text-decoration: none;
  white-space: nowrap;
}

.brand-lockup:hover {
  color: var(--e-sidebar-fg);
}

.brand-lockup:focus-visible {
  outline: 2px solid var(--e-focus);
  outline-offset: 2px;
}

.brand-lockup__tile {
  display: grid;
  flex: none;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 7px; /* 22% of the tile */
  background: var(--e-brand-tile);
  box-shadow: inset 0 0 0 1px var(--e-brand-tile-ring);
}

.brand-lockup__emblem {
  display: block;
  width: 24px;
  height: 24px;
}

.brand-lockup__word {
  font-family: var(--font-display);
  font-size: 15px;
  line-height: 1;
}

.brand-lockup__name {
  font-stretch: 118%;
  font-weight: var(--fw-bold);
  letter-spacing: 0.045em;
  text-transform: uppercase; /* the logotype: the one sanctioned all-caps setting */
}

.brand-lockup__product {
  margin-left: 0.12em;
  color: var(--e-sidebar-muted);
  font-weight: var(--fw-medium);
  letter-spacing: 0.06em;
}

.brand-lockup__stack {
  display: grid;
  gap: 5px;
}

.brand-lockup__role {
  color: var(--e-sidebar-muted);
  font-family: var(--font-ui);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.02em;
  line-height: 1;
}

:where([data-sidebar="collapsed"]) :where(.brand-lockup__word, .brand-lockup__stack) {
  display: none; /* zero specificity: layout.css container queries decide in the drawer/rail */
}

:where([data-sidebar="collapsed"]) :where(.brand-lockup) {
  gap: 0;
}

.brand-auth {
  display: flex;
  align-items: center;
  gap: 16px;
  color: var(--e-fg);
}

.brand-auth__tile {
  display: grid;
  flex: none;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 12px;
  background: var(--e-brand-tile);
  box-shadow:
    inset 0 0 0 1px var(--e-brand-tile-ring),
    var(--shadow-1);
}

.brand-auth__emblem {
  display: block;
  width: 42px;
  height: 42px;
}

.brand-auth__text {
  display: grid;
  gap: 6px;
  min-width: 0;
}

.brand-auth__word {
  margin: 0;
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1.05;
  white-space: nowrap;
}

.brand-auth__name {
  font-stretch: 118%;
  font-weight: var(--fw-bold);
  letter-spacing: 0.045em;
  text-transform: uppercase;
}

.brand-auth__product {
  margin-left: 0.12em;
  color: var(--e-muted);
  font-weight: var(--fw-medium);
  letter-spacing: 0.06em;
}

.brand-auth__line {
  max-width: 34ch;
  margin: 0;
  color: var(--e-muted);
  font-family: var(--font-ui);
  font-size: var(--fs-md);
  line-height: var(--lh-ui);
  text-wrap: pretty;
}

/* "Demo tenant" chip: dashed outline + flask = "sample, not real" (§5.1) */
.chip--demo {
  min-height: 26px;
  border: 1px dashed var(--e-border-strong);
  background: transparent;
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.01em;
  cursor: pointer;
}

.chip.chip--demo:hover {
  border-color: var(--e-fg);
  background: transparent;
  color: var(--e-fg);
}

.chip--demo .icon {
  width: 14px;
  height: 14px;
}

@media (max-width: 480px) {
  .chip--demo .brand-demo__more {
    display: none;
  }
}

.brand-demo-note {
  width: min(26rem, calc(100vw - 32px));
  margin: auto;
  padding: 16px 18px;
  border: 1px solid var(--e-border);
  border-radius: var(--r-lg);
  background: var(--e-raised);
  box-shadow: var(--shadow-pop);
  color: var(--e-fg);
  font-family: var(--font-ui);
  font-size: var(--fs-md);
  line-height: 1.5;
}

.brand-demo-note h2 {
  margin: 0 0 8px;
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  line-height: 1.3;
}

.brand-demo-note ul {
  margin: 0 0 14px;
  padding-left: 18px;
  color: var(--e-muted);
}

.brand-demo-note li + li {
  margin-top: 4px;
}

.brand-demo-note::backdrop {
  background: transparent;
}

.powered-by {
  margin: 12px 0 0;
  color: var(--e-faint);
  font-family: var(--font-ui);
  font-size: 9px;
  letter-spacing: 0.01em;
  line-height: 1.4;
  text-align: center;
}

/* the demo notice is the only demo marker on a shareable TAX INVOICE: larger
   and heavier than the credit (03-brand §4.3) — 11 px on screen, 8 pt in print */
.powered-by__demo {
  display: block;
  margin-top: 2px;
  color: var(--e-muted); /* #44566B on the paper palette of a .sheet */
  font-size: 11px;
  font-weight: var(--fw-medium);
}

@media print {
  .powered-by {
    color: #5b6e84;
    font-size: 7pt;
  }
  .powered-by__demo {
    color: #44566b;
    font-size: 8pt;
    font-weight: 520;
  }
}

.brand-owner {
  margin: 0;
  color: var(--e-faint);
  font-size: var(--fs-xs);
  line-height: 1.5;
}

/* Tenant monogram for documents without a logo (03-brand.md §4.2):
   <div class="tenant-mark"><span class="tenant-mark__monogram" aria-hidden="true">MF</span>
     <span class="tenant-mark__name">Meghmala Fresh</span></div>
   Ink only — prints in B/W; never a grey "LOGO" box. */
.tenant-mark {
  display: inline-flex;
  align-items: center;
  gap: 3mm;
  color: #0b1526;
}

.tenant-mark__monogram {
  display: grid;
  flex: none;
  place-items: center;
  width: 14mm;
  height: 14mm;
  border: 0.75pt solid currentColor;
  border-radius: 1.5mm;
  font-family: var(--font-display);
  font-size: 6mm;
  font-stretch: 110%;
  font-weight: 640;
  letter-spacing: 0.02em;
  line-height: 1;
}

.tenant-mark__name {
  font-family: var(--font-display);
  font-size: 16pt;
  font-weight: 620;
  letter-spacing: -0.01em;
  line-height: 1.1;
}

/* ── 19 · ext: golden-screen components (dashboard · lists · editors) ─────
   Promoted with the three golden screens (erp/index.html,
   erp/docs/invoices/index.html, erp/docs/invoices/new.html) so every page is
   composed from shared parts only (NFR-MNT-001). Style guide: Components →
   Quick actions, Chart; Tables → Line grid, Item search.
   ──────────────────────────────────────────────────────────────────────── */

/* Quick actions — the dashboard launcher bar (REQ-DSH-002). One compact
   36 px row of labelled buttons under the dashboard's figures, never tiles:
   the owner's position leads the page, the launchers follow. The meta (the
   next document number) is kept for screen readers and as the hover title.
   Below 768 px the bar hides: the topbar New menu carries the same actions.
   <nav class="quick-actions" aria-label="Quick actions"><ul>
     <li><a class="quick-actions__item" href="…" title="Next INV/2026-27/0212">{{icon file-text}}
       <span class="quick-actions__label">New invoice</span>
       <span class="quick-actions__meta">Next INV/2026-27/0212</span></a></li> … */
.quick-actions > ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.quick-actions__item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 36px;
  padding: 0 var(--sp-3) 0 10px;
  border: 1px solid color-mix(in srgb, var(--e-fg) 18%, transparent);
  border-radius: var(--r-md);
  background: var(--e-raised);
  color: var(--e-fg);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  white-space: nowrap;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}

.quick-actions__item > .icon {
  color: var(--e-primary-dark);
}

.quick-actions__item:hover {
  border-color: var(--e-primary);
  background: var(--e-subtle);
  color: var(--e-fg);
}

.quick-actions__meta {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 767.98px) {
  .quick-actions {
    display: none;
  }
}

/* Column chart — honest, labelled, CSS only (dashboard trends, report visuals).
   Bars start at zero; every bar carries its value; the exact figures sit in
   .chart__data (a real table in a disclosure) because the plot is aria-hidden.
   <figure class="chart">
     <figcaption class="chart__caption">…one-sentence reading…</figcaption>
     <ul class="chart__legend"><li class="chart__key">Series A</li>
       <li class="chart__key chart__key--alt">Series B</li></ul>
     <div class="chart__plot" aria-hidden="true">
       <ul class="chart__grid"><li style="--at:0%"><span>₹0</span></li> …</ul>
       <ol class="chart__cols">
         <li class="chart__col"><span class="chart__value">₹10.2 L</span>
           <span class="chart__seg chart__seg--alt" style="--v:17.6%"></span>
           <span class="chart__seg" style="--v:50.6%"></span>
           <span class="chart__label">Apr</span></li> …</ol></div>
     <details class="chart__data"><summary>Exact figures</summary>
       <div class="table-wrap">…table…</div></details>
   </figure>
   --v and --at are percentages of the axis maximum (the top grid line);
   --chart-h (default 200px) sets the plot height.
   Stack order: the first .chart__seg in the markup is the top of the bar.    */
.chart {
  display: grid;
  gap: var(--sp-3);
  min-width: 0;
  margin: 0;
}

.chart__caption {
  color: var(--e-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-4);
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--e-muted);
  font-size: var(--fs-sm);
}

.chart__key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.chart__key::before {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--e-data);
}

.chart__key--alt::before {
  background: var(--e-border-strong);
}

.chart__key--partial::before {
  background: color-mix(in srgb, var(--e-data) 22%, var(--e-raised));
  box-shadow: inset 0 0 0 1.5px var(--e-data);
}

.chart__plot {
  position: relative;
  height: var(--chart-h, 200px); /* a page may set --chart-h on the figure */
  margin: var(--sp-5) 0 var(--sp-8) 3.25rem; /* room for value labels, x labels, y labels */
  border-bottom: 1px solid var(--e-border-strong);
}

.chart__grid,
.chart__cols {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.chart__grid > li {
  position: absolute;
  right: 0;
  bottom: var(--at, 0%);
  left: 0;
  border-top: 1px solid var(--e-border);
}

.chart__grid > li > span {
  position: absolute;
  right: 100%;
  bottom: 0;
  padding-right: var(--sp-2);
  translate: 0 50%;
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.chart__cols {
  display: grid;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  gap: var(--sp-2);
  padding-inline: var(--sp-2);
}

.chart__col {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
  min-width: 0;
}

.chart__seg {
  flex: none;
  width: min(44px, 72%);
  height: var(--v, 0%);
  background: var(--e-data); /* data ink, never the theme colour (04 §6.4) */
}

.chart__seg:first-of-type {
  border-radius: 2px 2px 0 0;
}

.chart__seg + .chart__seg {
  border-top: 1px solid var(--e-raised); /* hairline between stacked series */
}

.chart__seg--alt {
  background: var(--e-border-strong);
}

.chart__value {
  margin-bottom: 4px;
  color: var(--e-fg);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.chart__label {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  translate: -50% 0;
  display: grid;
  justify-items: center;
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  line-height: 1.25;
  white-space: nowrap;
}

.chart__label small {
  color: var(--e-faint);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
}

/* a period still in progress (month to date): outlined, not solid */
.chart__col--partial .chart__seg {
  background: color-mix(in srgb, var(--e-data) 22%, var(--e-raised));
  box-shadow: inset 0 0 0 1.5px var(--e-data);
}

.chart__col--partial .chart__seg--alt {
  background: color-mix(in srgb, var(--e-border-strong) 22%, var(--e-raised));
  box-shadow: inset 0 0 0 1.5px var(--e-border-strong);
}

.chart__data > summary {
  width: fit-content;
  color: var(--e-primary-dark);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
}

.chart__data > .table-wrap {
  margin-top: var(--sp-2);
}

/* Needs attention — one hairline list of the things that want an action now
   (dashboard alerts, REQ-DSH-003; overdue money, credit limits, approvals).
   Not a quilt of tinted callouts: one card, one row per fact. Status colour
   sits ONLY on the icon and the amount — no tinted fills, no side stripes.
   <ul class="attention">
     <li class="attention__item is-err">{{icon hourglass}}
       <p class="attention__fact"><a href="…">13 invoices overdue</a></p>
       <p class="attention__amount money">₹2,85,678.00</p>
       <p class="attention__detail">Oldest <span class="code">INV/2026-27/0036</span> · 75 days late</p>
       <p class="attention__action"><a href="…">View overdue invoices</a></p></li> … </ul>
   Tones: .is-err · .is-warn · .is-info (icon only; the amount stays ink).
   .attention__amount--plain: a price or a count in a warn row stays ink.
   Order rows by consequence (money at risk first). Place the card straight
   after the figures it explains, or beside the chart. */
.attention {
  margin: 0;
  padding: 0;
  list-style: none;
}

.attention__item {
  --_at: var(--e-faint);
  --_at-fg: var(--e-fg);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 2px var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--e-border);
}

.attention__item:first-child {
  border-top: 0;
}

.attention__item.is-err {
  --_at: var(--e-err-fg);
  --_at-fg: var(--e-err-fg);
}

.attention__item.is-warn {
  --_at: var(--e-warn-fg);
  --_at-fg: var(--e-warn-fg);
}

.attention__item.is-info {
  --_at: var(--e-info-fg);
}

.attention__item > .icon {
  grid-row: 1 / span 2;
  align-self: start;
  margin-top: 2px;
  color: var(--_at);
}

.attention__fact,
.attention__detail {
  grid-column: 2;
  min-width: 0;
  margin: 0;
}

.attention__fact {
  color: var(--e-fg);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
}

.attention__fact a {
  color: inherit;
  text-decoration-color: color-mix(in srgb, currentColor 36%, transparent);
}

.attention__fact a:hover {
  color: var(--e-primary-dark);
  text-decoration-color: currentColor;
}

.attention__detail {
  color: var(--e-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.attention__amount,
.attention__action {
  grid-column: 3;
  margin: 0;
  text-align: right;
  white-space: nowrap;
}

.attention__amount {
  grid-row: 1;
  color: var(--_at-fg);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}

/* the amount is coloured only when the figure itself is at risk (overdue,
   over the limit, payable on hold). A price, a total to approve or a count
   is a plain fact: .attention__amount--plain keeps it in ink. */
.attention__amount--plain {
  color: var(--e-fg);
}

.attention__action {
  grid-row: 2;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

/* narrow cards: the action drops under the detail instead of squeezing it */
@container main (max-width: 479.98px) {
  .attention__item {
    padding-inline: var(--sp-3);
  }

  .attention__action {
    grid-column: 2;
    grid-row: auto;
    text-align: left;
  }
}


/* Row tones for feeds and checklists (notifications, the first-run setup list):
   .is-ok       done / money in — ok-fg icon and amount (like .is-err / .is-warn)
   .is-todo     a step not reached yet — faint icon, muted figure
   .is-current  the step to do now — primary-dark icon and figure; its
                .attention__action holds a .btn--sm, centred on the row
   .is-read     a notification already opened — regular weight, faint icon,
                muted time
   .notify-dot  the unread marker at the leading edge of .attention__fact,
                always with visually hidden text:
     <p class="attention__fact"><span class="notify-dot" aria-hidden="true"></span><span class="visually-hidden">Unread: </span><a href="…">…</a></p> */
.attention__item.is-ok {
  --_at: var(--e-ok-fg);
  --_at-fg: var(--e-ok-fg);
}

.attention__item.is-todo > .icon {
  color: var(--e-faint);
}

.attention__item.is-todo .attention__amount {
  color: var(--e-muted);
  font-weight: var(--fw-medium);
}

.attention__item.is-current {
  --_at: var(--e-primary-dark);
  --_at-fg: var(--e-primary-dark);
}

.attention__item.is-current .attention__action {
  align-self: center;
}

.attention__item.is-read .attention__fact {
  font-weight: var(--fw-regular);
}

.attention__item.is-read > .icon {
  color: var(--e-faint);
}

.attention__item.is-read .attention__amount {
  color: var(--e-muted);
  font-weight: var(--fw-medium);
}

.notify-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: var(--sp-2);
  border-radius: var(--r-full);
  background: var(--e-primary);
  vertical-align: 1px;
}

/* .attention--feed: a feed of events (notifications) — the amount column is
   a time (<time>, with .attention__amount--plain) and the action column a
   quiet type label, not a link. On phones both drop under the detail. */
.attention--feed .attention__action {
  color: var(--e-muted);
}

@container main (max-width: 479.98px) {
  .attention--feed .attention__item {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .attention--feed .attention__amount {
    grid-column: 2;
    grid-row: auto;
    order: 3;
    text-align: left;
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
  }

  .attention--feed .attention__action {
    order: 4;
    font-size: var(--fs-xs);
  }
}

/* ext: no-script list filter — a .segmented radiogroup of data-filter keys
   above a list whose rows carry data-type (or .is-unread). :has() hides the
   rows that do not match; when nothing matches, the list goes and the
   .filterable__none block for that key shows instead.
   <section class="card filterable">
     <div class="filter-bar"><div class="segmented" role="radiogroup" aria-label="Show notifications">
       <label><input type="radio" name="nf" data-filter="all" checked> All <span class="chip__count">8</span></label>
       <label><input type="radio" name="nf" data-filter="low_stock"> Low stock <span class="chip__count">3</span></label> …</div></div>
     <ul class="attention attention--feed filterable__list"><li class="attention__item is-warn is-unread" data-type="low_stock">…</li></ul>
     <div class="empty filterable__none" data-for="followup_due">…</div>
   </section>
   The keys are FIXED (CSS cannot compare two attributes): "all", "unread"
   (rows with .is-unread), the notification types of demo.json
   (payment_received, low_stock, subscription_expiring, gst_due, gst_filed, invoice_overdue, invoice_cancelled, credit_limit_exceeded, followup_due, bulk_batch_done) and the row
   tones ok · warn · err · info (an import's valid / warning / error rows).
   A new key is added HERE (UI Designer), never page-locally. */
.filter-bar {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--e-border);
}

.filterable__none {
  display: none;
}

.filterable:has([data-filter="unread"]:checked) .filterable__list > :not(.is-unread),
.filterable:has([data-filter="payment_received"]:checked) .filterable__list > :not([data-type="payment_received"]),
.filterable:has([data-filter="low_stock"]:checked) .filterable__list > :not([data-type="low_stock"]),
.filterable:has([data-filter="subscription_expiring"]:checked) .filterable__list > :not([data-type="subscription_expiring"]),
.filterable:has([data-filter="gst_due"]:checked) .filterable__list > :not([data-type="gst_due"]),
.filterable:has([data-filter="gst_filed"]:checked) .filterable__list > :not([data-type="gst_filed"]),
.filterable:has([data-filter="invoice_overdue"]:checked) .filterable__list > :not([data-type="invoice_overdue"]),
.filterable:has([data-filter="invoice_cancelled"]:checked) .filterable__list > :not([data-type="invoice_cancelled"]),
.filterable:has([data-filter="credit_limit_exceeded"]:checked) .filterable__list > :not([data-type="credit_limit_exceeded"]),
.filterable:has([data-filter="followup_due"]:checked) .filterable__list > :not([data-type="followup_due"]),
.filterable:has([data-filter="bulk_batch_done"]:checked) .filterable__list > :not([data-type="bulk_batch_done"]),
.filterable:has([data-filter="ok"]:checked) .filterable__list > :not([data-type="ok"]),
.filterable:has([data-filter="warn"]:checked) .filterable__list > :not([data-type="warn"]),
.filterable:has([data-filter="err"]:checked) .filterable__list > :not([data-type="err"]),
.filterable:has([data-filter="info"]:checked) .filterable__list > :not([data-type="info"]) {
  display: none;
}

.filterable:has([data-filter="unread"]:checked) .filterable__list:not(:has(> .is-unread)),
.filterable:has([data-filter="payment_received"]:checked) .filterable__list:not(:has(> [data-type="payment_received"])),
.filterable:has([data-filter="low_stock"]:checked) .filterable__list:not(:has(> [data-type="low_stock"])),
.filterable:has([data-filter="subscription_expiring"]:checked) .filterable__list:not(:has(> [data-type="subscription_expiring"])),
.filterable:has([data-filter="gst_due"]:checked) .filterable__list:not(:has(> [data-type="gst_due"])),
.filterable:has([data-filter="gst_filed"]:checked) .filterable__list:not(:has(> [data-type="gst_filed"])),
.filterable:has([data-filter="invoice_overdue"]:checked) .filterable__list:not(:has(> [data-type="invoice_overdue"])),
.filterable:has([data-filter="invoice_cancelled"]:checked) .filterable__list:not(:has(> [data-type="invoice_cancelled"])),
.filterable:has([data-filter="credit_limit_exceeded"]:checked) .filterable__list:not(:has(> [data-type="credit_limit_exceeded"])),
.filterable:has([data-filter="followup_due"]:checked) .filterable__list:not(:has(> [data-type="followup_due"])),
.filterable:has([data-filter="bulk_batch_done"]:checked) .filterable__list:not(:has(> [data-type="bulk_batch_done"])),
.filterable:has([data-filter="ok"]:checked) .filterable__list:not(:has(> [data-type="ok"])),
.filterable:has([data-filter="warn"]:checked) .filterable__list:not(:has(> [data-type="warn"])),
.filterable:has([data-filter="err"]:checked) .filterable__list:not(:has(> [data-type="err"])),
.filterable:has([data-filter="info"]:checked) .filterable__list:not(:has(> [data-type="info"])) {
  display: none;
}

.filterable:has([data-filter="unread"]:checked):not(:has(.filterable__list > .is-unread)) .filterable__none[data-for="unread"],
.filterable:has([data-filter="payment_received"]:checked):not(:has(.filterable__list > [data-type="payment_received"])) .filterable__none[data-for="payment_received"],
.filterable:has([data-filter="low_stock"]:checked):not(:has(.filterable__list > [data-type="low_stock"])) .filterable__none[data-for="low_stock"],
.filterable:has([data-filter="subscription_expiring"]:checked):not(:has(.filterable__list > [data-type="subscription_expiring"])) .filterable__none[data-for="subscription_expiring"],
.filterable:has([data-filter="gst_due"]:checked):not(:has(.filterable__list > [data-type="gst_due"])) .filterable__none[data-for="gst_due"],
.filterable:has([data-filter="gst_filed"]:checked):not(:has(.filterable__list > [data-type="gst_filed"])) .filterable__none[data-for="gst_filed"],
.filterable:has([data-filter="invoice_overdue"]:checked):not(:has(.filterable__list > [data-type="invoice_overdue"])) .filterable__none[data-for="invoice_overdue"],
.filterable:has([data-filter="invoice_cancelled"]:checked):not(:has(.filterable__list > [data-type="invoice_cancelled"])) .filterable__none[data-for="invoice_cancelled"],
.filterable:has([data-filter="credit_limit_exceeded"]:checked):not(:has(.filterable__list > [data-type="credit_limit_exceeded"])) .filterable__none[data-for="credit_limit_exceeded"],
.filterable:has([data-filter="followup_due"]:checked):not(:has(.filterable__list > [data-type="followup_due"])) .filterable__none[data-for="followup_due"],
.filterable:has([data-filter="bulk_batch_done"]:checked):not(:has(.filterable__list > [data-type="bulk_batch_done"])) .filterable__none[data-for="bulk_batch_done"],
.filterable:has([data-filter="ok"]:checked):not(:has(.filterable__list > [data-type="ok"])) .filterable__none[data-for="ok"],
.filterable:has([data-filter="warn"]:checked):not(:has(.filterable__list > [data-type="warn"])) .filterable__none[data-for="warn"],
.filterable:has([data-filter="err"]:checked):not(:has(.filterable__list > [data-type="err"])) .filterable__none[data-for="err"],
.filterable:has([data-filter="info"]:checked):not(:has(.filterable__list > [data-type="info"])) .filterable__none[data-for="info"] {
  display: grid;
}

/* phones: the filters stay one row and scroll sideways */
@container main (max-width: 479.98px) {
  .filter-bar {
    padding-inline: var(--sp-3);
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }

  .filter-bar .segmented {
    flex-wrap: nowrap;
  }
}

/* ext: pick card — a choice card whose name link is stretched over the whole
   card (company picker; plan, branch and FY pickers). One link per card, so
   the card is one tab stop; focus shows on the card.
   <ul class="pick-list">
     <li class="pick-card">
       <h2 class="pick-card__name"><a href="…">Meghmala Fresh</a> <span class="tag">Demo</span></h2>
       <span class="badge badge--active">Active</span>
       <p class="pick-card__line"><span>GSTIN …</span><span>2 branches</span></p>
       <p class="pick-card__go" aria-hidden="true">Open Meghmala Fresh {{icon arrow-right}}</p>
     </li> … </ul>
   Only the name link is interactive; a second control inside would sit under
   the stretched link. */
.pick-list {
  display: grid;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pick-card {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-1) var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--e-border-strong);
  border-radius: var(--r-lg);
  background: var(--e-raised);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}

.pick-card:hover {
  border-color: var(--e-primary);
  box-shadow: var(--shadow-1);
}

.pick-card:focus-within {
  outline: 2px solid var(--e-focus);
  outline-offset: 2px;
}

.pick-card__name {
  margin: 0;
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--fs-lg);
  line-height: var(--lh-snug);
}

.pick-card__name a {
  color: var(--e-fg);
  text-decoration: none;
}

.pick-card__name .tag {
  margin-left: var(--sp-1);
  vertical-align: 3px;
}

.pick-card__name a:focus-visible {
  outline: none;
}

.pick-card__name a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-lg);
}

.pick-card > .badge {
  align-self: start;
}

.pick-card__line {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 2px 0;
  margin: 0;
  color: var(--e-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.pick-card__line > * + *::before {
  content: "·" / "";
  margin: 0 var(--sp-2);
  color: var(--e-faint);
}

.pick-card__go {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin: var(--sp-2) 0 0;
  color: var(--e-primary-dark);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.pick-card__go .icon {
  width: 16px;
  height: 16px;
}

/* ext: stock-shortfall sub-line — told apart from the plain .text-warn
   "below minimum" note by shape, not colour alone: a triangle and bold words.
   <small class="text-warn stock-short">{{icon triangle-alert icon--sm}}HQ stock 40 · <strong>short by 20</strong></small> */
.stock-short .icon {
  width: 13px;
  height: 13px;
  margin-inline-end: 2px;
  vertical-align: -2px;
}

/* in an editor's line sub-line the words sit a hair further off the icon */
.table--entry td > small.stock-short .icon {
  margin-inline-end: 3px;
}

/* Line grid — editable document lines (invoice, quotation, note, purchase,
   journal, bulk billing). Keyboard model: partials/grid-keys (02-ia §8),
   run by app.js on <table … data-grid-keys>.
   <table class="table table--entry" data-grid-keys> with inputs in cells;
   size="…" on each input sets its column width. Every cell carries
   data-col (line · item · hsn · qty · unit · rate · disc · taxable · gst ·
   cgst · sgst · igst · amount · remove, and the phone-only tax cell).
   Tab path per line: Item → Qty → Rate → Disc % (4 stops). HSN/SAC, Unit and
   GST are prefilled from the item master: their inputs carry tabindex="-1"
   and data-grid-skip, sit on the quieter --e-subtle well, and are reached with
   Alt+T (tax details). Unit and GST are text comboboxes (input + datalist):
   ↑ / ↓ never change a value, they move rows.
   .calc = computed cell (not a tab stop); .line-no = the row number;
   tr.is-editing / .input.is-focused depict focus in a static snapshot and
   switch off as soon as the grid holds real focus. tfoot holds "Add line".
   The grid reads as a ledger, not a form: a hairline between columns, every
   value 10 px from its column rule (head, figures and typed values alike, so
   a figure and the text column after it are ≥ 20 px apart),
   inputs fill their cells and show their edge only on hover, focus and on
   the line being typed. Put data-col on the <th> too, so heads align. */
.table--entry {
  font-size: var(--fs-sm);
}

.table--entry th,
.table--entry td {
  padding: 6px 10px;
  vertical-align: top;
}

/* column rules: one hairline between columns (not before the remove button,
   not inside a sub-row or the "Add line" row) */
.table--entry :is(thead, tbody, tfoot) tr:not(.entry-subrow) > :is(th, td) + :is(th, td):not(.actions, [data-col="remove"], [data-col="add"]) {
  border-inline-start: 1px solid var(--e-border);
}

.table--entry th:first-child,
.table--entry td:first-child {
  padding-left: var(--sp-3);
}

.table--entry th:last-child,
.table--entry td:last-child {
  padding-right: var(--sp-2);
}

/* the remove button column is only as wide as its button */
.table--entry [data-col="remove"] {
  width: 1%;
  padding-inline: 2px var(--sp-2);
}

.table--entry thead th {
  vertical-align: bottom;
}

/* a cell holding an input gives it the room: the input's own 1 px edge and
   6 px inset put the value 10 px from the rule, like every other value */
.table--entry td:has(> .input, > .select) {
  padding-inline: 3px;
}

.table--entry td > .input,
.table--entry td > .select {
  width: 100%;
  min-width: var(--_in-min, 3rem);
  min-height: 32px;
  padding-inline: 6px;
  border-color: transparent;
  background: transparent;
  font-size: var(--fs-sm);
}

/* the input's size sets its column's natural width; these floors keep a
   column from collapsing when the grid is short of room */
.table--entry [data-col="item"] { --_in-min: 12rem; }
.table--entry [data-col="hsn"] { --_in-min: 5rem; }
.table--entry [data-col="qty"] { --_in-min: 3.25rem; }
.table--entry [data-col="unit"] { --_in-min: 4rem; }
.table--entry [data-col="rate"] { --_in-min: 5rem; }
.table--entry [data-col="disc"] { --_in-min: 3rem; }
.table--entry [data-col="gst"] { --_in-min: 3.5rem; }

.table--entry td > :is(.input, .select):hover {
  border-color: var(--e-border-strong);
  background: var(--e-raised);
}

/* the line being typed shows its fields; so does any line holding focus */
.table--entry tbody tr:is(.is-editing, :focus-within) > td > :is(.input, .select) {
  border-color: var(--e-border-strong);
  background: var(--e-raised);
}

.table--entry td > :is(.input, .select):focus,
.table--entry td > :is(.input, .select):focus-visible {
  border-color: var(--e-focus);
  background: var(--e-raised);
  outline: 2px solid var(--e-focus);
  outline-offset: -1px;
}

.table--entry td > .input[aria-invalid="true"] {
  border-color: var(--e-err);
  background: var(--e-raised);
}

.table--entry td > .select {
  padding-right: 1.75rem;
  background-position:
    calc(100% - 14px) 52%,
    calc(100% - 9px) 52%;
}

.table--entry td > .input--code {
  font-size: 0.75rem;
}

/* prefilled tax details (HSN/SAC, Unit, GST): outside the Tab path — they sit
   in the quieter well at rest, and in any state that is not focus */
.table--entry td > .input[data-grid-skip],
.table--entry tbody tr:is(.is-editing, :focus-within) > td > .input[data-grid-skip]:not(:focus) {
  background: var(--e-subtle);
}

.table--entry td > .input[list]::-webkit-calendar-picker-indicator {
  display: none !important; /* the combobox arrow is Alt+↓, not a mouse target */
}

.table--entry .calc,
.table--entry .line-no {
  padding-top: 13px;
  font-variant-numeric: tabular-nums;
}

.table--entry .line-no {
  width: 1%;
  color: var(--e-muted);
  text-align: right;
}

.table--entry tbody tr:hover {
  background: transparent;
}

/* the line being typed: a neutral ground (a theme wash would read "Paid" in
   the green themes, 03-brand §6.3) and the 3 px theme bar as its only cue */
.table--entry tbody tr:focus-within,
.table--entry tbody tr.is-editing {
  background: var(--e-subtle);
}

.table--entry tbody tr:focus-within > :first-child,
.table--entry tbody tr.is-editing > :first-child {
  box-shadow: inset 3px 0 0 var(--e-primary);
}

/* a line with an error stays marked as an error, whatever holds focus */
.table--entry tbody tr:has(.is-invalid, [aria-invalid="true"]),
.table--entry tbody tr:has(.is-invalid, [aria-invalid="true"]) + tr.entry-subrow > td {
  background: color-mix(in srgb, var(--e-err-soft) 60%, var(--e-raised));
}

.table--entry tbody tr:has(.is-invalid, [aria-invalid="true"]) > :first-child,
.table--entry tbody tr:has(.is-invalid, [aria-invalid="true"]) + tr.entry-subrow > td:first-child {
  box-shadow: inset 3px 0 0 var(--e-err);
}

.table--entry .is-editing .input.is-focused {
  border-color: var(--e-focus);
  outline: 2px solid var(--e-focus);
  outline-offset: -1px;
}

/* once the grid holds real focus, only live focus shows */
.table--entry:focus-within .is-editing .input.is-focused:not(:focus) {
  border-color: var(--e-border-strong);
  outline: none;
}

.table--entry:focus-within tbody tr.is-editing:not(:focus-within, :has([aria-invalid="true"])),
.table--entry:focus-within tbody tr.is-editing:not(:focus-within, :has([aria-invalid="true"])) + tr.entry-subrow > td {
  background: none;
}

.table--entry:focus-within tbody tr.is-editing:not(:focus-within, :has([aria-invalid="true"])) > :first-child,
.table--entry:focus-within tbody tr.is-editing:not(:focus-within, :has([aria-invalid="true"])) + tr.entry-subrow > td:first-child {
  box-shadow: none;
}

.table--entry td small,
.table--entry td .cell-sub {
  margin-top: 4px;
  white-space: nowrap;
}

/* a sub-line under an input starts where the typed value starts, and wraps
   rather than widen the grid */
.table--entry td:has(> .input) > small {
  padding-inline-start: 7px;
  white-space: normal;
}

/* Item names wrap at rest. An input holds one line and scrolls, so a long
   name on a line nobody is typing in read "…250 ml Cu". Each Item input is
   followed by span.cell-full, an aria-hidden copy of its value that wraps:
   it lies exactly over the input's line and lets the row grow downward.
   At rest the copy shows the name and the input sits invisible under it,
   still the Tab stop, still what a screen reader reads, and the click target
   of the first line (app.js sends a click on the rest of the cell there).
   While the Item holds focus (or is the depicted .is-focused), the input
   shows, scrolling as any field does, and the copy keeps its height hidden,
   so no line below moves. The copy has no width of its own (contain:
   inline-size): the column stays as wide as the input's size makes it and
   a longer name takes a second line.
     <td data-col="item"><input class="input" … value="Name"><span class="cell-full" aria-hidden="true">Name</span><small>…</small></td> */
.table--entry td > .input + .cell-full {
  display: block;
  position: relative; /* over the input */
  contain: inline-size;
  min-height: max(32px, 1.4em + 2px); /* the input's height, also with larger text */
  margin-top: calc(-1 * max(32px, 1.4em + 2px));
  padding: max(0px, (30px - 1.4em) / 2) 6px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  color: var(--e-fg);
  font-size: var(--fs-sm);
  line-height: 1.4;
  white-space: normal;
  overflow-wrap: anywhere;
  pointer-events: none;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}

.table--entry td > .input:has(+ .cell-full) {
  opacity: 0;
}

/* the copy is the field's face at rest: it shows the edge the input would */
.table--entry td:hover > .input + .cell-full,
.table--entry tbody tr:is(.is-editing, :focus-within) > td > .input + .cell-full {
  border-color: var(--e-border-strong);
  background: var(--e-raised);
}

.table--entry td > .input[aria-invalid="true"] + .cell-full {
  border-color: var(--e-err);
  background: var(--e-raised);
}

/* the name being typed: the input itself, one line */
.table--entry td > .input:is(:focus, .is-focused):has(+ .cell-full) {
  opacity: 1;
}

.table--entry td > .input:is(:focus, .is-focused) + .cell-full {
  visibility: hidden;
}

/* once the grid holds real focus, a depicted .is-focused Item is at rest */
.table--entry:focus-within td > .input.is-focused:not(:focus):has(+ .cell-full) {
  opacity: 0;
}

.table--entry:focus-within td > .input.is-focused:not(:focus) + .cell-full {
  visibility: visible;
}

/* the line totals: one quiet band across the whole row, figures regular */
.table--entry tfoot tr:first-child > * {
  background: var(--e-subtle);
  font-weight: var(--fw-regular);
}

.table--entry tfoot tr:first-child > th {
  font-weight: var(--fw-semibold);
}

.table--entry tfoot tr + tr > * {
  background: var(--e-raised);
}

.table--entry tfoot tr:last-child > * {
  border-bottom: 0;
}

/* the phone-only tax details cell never takes a column on wider screens */
.table--entry [data-col="tax"] {
  display: none;
}

/* spare width goes to the item column first (1920 desktops), not spread
   evenly over the figures */
.table--entry th[data-col="item"] {
  width: 30%;
}

/* tablets (main < 1100px): sub-lines may wrap */
@container main (max-width: 1099.98px) {
  /* lower floors, so an inter-state grid (one IGST column) fits 1024 × 768 */
  .table--entry [data-col="item"] { --_in-min: 10rem; }
  .table--entry [data-col="unit"] { --_in-min: 3.75rem; }
  .table--entry [data-col="rate"] { --_in-min: 4.75rem; }

  .table--entry td:not(.num) small {
    white-space: normal;
  }
}

/* a sub-row under the editing row (item search, a line-level note) */
.table--entry tr.is-editing > td {
  border-bottom-color: transparent;
}

.table--entry tbody tr.entry-subrow > td {
  padding-top: 0;
  background: var(--e-subtle);
}

.table--entry tbody tr.entry-subrow > td:first-child {
  box-shadow: inset 3px 0 0 var(--e-primary);
}

/* phones: each line is a compact card (≤ 180 px at 390):
     Line 1 ······························· ×      header + remove
     [ Item ······························ ]      stock sub-line
     Qty  ×  Rate  −  Disc %  =  Amount           one row
     ▸ Tax details · HSN … · Pcs · GST 5%         HSN / Unit / GST / taxes open here
   The markup is the desktop table: .table--entry.table--stack + data-col.   */
@media (max-width: 639.98px) {
  .table--entry.table--stack tbody tr:not(.entry-subrow) {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-areas:
      "line line line remove"
      "item item item item"
      "qty rate disc amount"
      "tax tax tax tax"
      "hsn unit gst gst"
      "taxable cgst sgst igst";
    column-gap: var(--sp-2);
    row-gap: 3px;
    padding: 6px var(--sp-4) var(--sp-2);
  }

  /* the line being typed is marked on the whole card, not on its first cell */
  .table--entry.table--stack tbody tr.is-editing,
  .table--entry.table--stack tbody tr:focus-within {
    box-shadow: inset 3px 0 0 var(--e-primary);
  }

  .table--entry.table--stack tbody tr > :first-child {
    box-shadow: none !important;
  }

  .table--entry.table--stack td > small {
    margin-top: 1px;
  }

  .table--entry.table--stack tbody tr > td,
  .table--entry.table--stack tbody tr > td:first-child,
  .table--entry.table--stack tbody tr > td:last-child {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    gap: 2px;
    width: auto;
    padding: 0;
    text-align: left;
  }

  .table--entry.table--stack td > small {
    text-align: left;
  }

  .table--entry.table--stack td[data-label]::before {
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
  }

  .table--entry.table--stack [data-col="line"] { grid-area: line; display: flex !important; justify-content: flex-start; align-items: center; gap: var(--sp-1); color: var(--e-fg); font-weight: var(--fw-semibold); }
  .table--entry.table--stack [data-col="line"]::before { color: inherit; font-size: inherit !important; font-weight: inherit !important; }
  .table--entry.table--stack [data-col="remove"] { grid-area: remove; justify-self: end; margin: -6px -8px -6px 0; }
  .table--entry.table--stack [data-col="item"] { grid-area: item; }
  .table--entry.table--stack [data-col="item"]::before { display: none; }
  .table--entry.table--stack [data-col="qty"] { grid-area: qty; }
  .table--entry.table--stack [data-col="rate"] { grid-area: rate; }
  .table--entry.table--stack [data-col="disc"] { grid-area: disc; }
  .table--entry.table--stack [data-col="amount"] { grid-area: amount; text-align: right; }
  .table--entry.table--stack [data-col="tax"] { grid-area: tax; display: block !important; }
  .table--entry.table--stack [data-col="hsn"] { grid-area: hsn; }
  .table--entry.table--stack [data-col="unit"] { grid-area: unit; }
  .table--entry.table--stack [data-col="gst"] { grid-area: gst; }
  .table--entry.table--stack [data-col="taxable"] { grid-area: taxable; }
  .table--entry.table--stack [data-col="cgst"] { grid-area: cgst; }
  .table--entry.table--stack [data-col="sgst"] { grid-area: sgst; }
  .table--entry.table--stack [data-col="igst"] { grid-area: igst; }

  .table--entry.table--stack [data-col="amount"]::before { text-align: right; }
  .table--entry.table--stack [data-col="amount"].calc { padding-top: 0; }
  .table--entry.table--stack [data-col="amount"] { align-self: start; }
  .table--entry.table--stack [data-col="amount"] { padding-top: 0; font-weight: var(--fw-bold); }
  .table--entry.table--stack [data-col="amount"] small { display: none; }

  .table--entry.table--stack td > .input {
    width: 100%;
    min-width: 0;
    max-width: none;
    min-height: 34px;
    margin: 0;
    border-color: var(--e-border-strong);
    background: var(--e-raised);
  }

  .table--entry.table--stack td > .input[data-grid-skip]:not(:focus) {
    background: var(--e-subtle);
  }

  /* the Item's wrapped copy shares the input's cell of the card's grid, so
     the input (the tap target) always spans the whole name */
  .table--entry.table--stack td > .input:has(+ .cell-full),
  .table--entry.table--stack td > .input + .cell-full {
    grid-area: 1 / 1;
  }

  .table--entry.table--stack td > .input + .cell-full {
    min-height: 34px;
    margin: 0;
    padding-block: calc((32px - 1.4em) / 2);
    border-color: var(--e-border-strong);
    background: var(--e-raised);
  }

  .table--entry.table--stack td > .input[aria-invalid="true"] + .cell-full {
    border-color: var(--e-err);
  }

  /* a card has no column rules */
  .table--entry.table--stack :is(thead, tbody, tfoot) tr > :is(th, td) {
    border-inline-start: 0 !important;
  }

  /* HSN / Unit / GST and the line's taxes stay folded until "Tax details" opens */
  .table--entry.table--stack tr:not(:has([data-col="tax"] > details[open])) > :is([data-col="hsn"], [data-col="unit"], [data-col="gst"], [data-col="taxable"], [data-col="cgst"], [data-col="sgst"], [data-col="igst"]) {
    display: none;
  }

  .table--entry.table--stack [data-col="tax"] summary {
    color: var(--e-muted);
    font-weight: var(--fw-medium);
  }

  .table--entry.table--stack .calc {
    padding-top: 0;
  }

  /* the editing row's sub-row (item search / line error) spans the card */
  .table--entry.table--stack tbody tr.entry-subrow {
    display: block;
    padding: 0 var(--sp-4) var(--sp-3);
  }

  .table--entry.table--stack tbody tr.entry-subrow > td:first-child {
    display: none;
  }

  .table--entry.table--stack tbody tr.entry-subrow > td {
    display: block;
    padding: 0;
    text-align: left;
  }

  /* tfoot: the line count, then the amount before round off */
  .table--entry.table--stack tfoot tr > :not([data-col]) {
    display: none;
  }

  .table--entry.table--stack tfoot tr {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-4);
  }

  .table--entry.table--stack tfoot tr > [data-col] {
    display: block;
    padding: 0;
    background: transparent;
    white-space: normal;
  }

  .table--entry.table--stack tfoot [data-col="add"] .muted {
    display: none; /* the keyboard hint is for keyboards; the legend card stays */
  }
}

/* Document header band — an editor's header fields as ONE compact band so the
   line grid starts in the first viewport (thead ≤ 420 px at 1366 × 768).
   Row 1: party (wide) + five fields, hints one short line. Row 2
   (.doc-head__row): the party summary line (.dl--inline) with a disclosure
   for addresses. Row 3 (.doc-head__row): at most one .alert--compact.
   <div class="card__body doc-head"> .field … <div class="doc-head__row doc-head__party">
     <dl class="dl dl--inline">…</dl><details class="disclosure">…</details></div>
     <div class="alert alert--warn alert--compact doc-head__row">…</div></div>
   Columns: 6 when the page area is ≥ 1100 px; 3 on tablets (party full
   width); 2 on phones.                                                    */
.doc-head {
  display: grid;
  grid-template-columns:
    minmax(12rem, 2fr) minmax(0, 1.55fr) minmax(0, 1.1fr) minmax(0, 1.1fr)
    minmax(0, 1.5fr) minmax(6.5rem, 0.95fr);
  gap: var(--sp-3);
  align-items: start;
}

.doc-head > .doc-head__row {
  grid-column: 1 / -1;
}

/* a native date control never pushes past its column on a narrow phone */
.doc-head .input {
  min-width: 0;
}

/* hints are one short line by authoring (long explanations live in a title or
   the help); they may wrap, but nothing is ever cut */
.doc-head .field__hint {
  overflow-wrap: anywhere;
}

.doc-head__party {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-1) var(--sp-5);
  margin-top: calc(-1 * var(--sp-1));
}

.doc-head__party > .dl--inline {
  flex: 0 1 auto;
  min-width: 0;
}

.doc-head__party > details[open] {
  flex-basis: 100%;
}

.doc-head__party > details > .dl {
  padding: var(--sp-3);
  border: 1px solid var(--e-border);
  border-radius: var(--r-md);
  background: var(--e-subtle);
}

/* a warning about the chosen party is one warn-coloured fact in its summary
   line, not a separate box (credit limit, overdue): <div class="is-warn">
   <dt>{{icon triangle-alert}}Credit</dt><dd>over limit by … · you can still save</dd></div> */
.dl--inline > .is-warn dt,
.dl--inline > .is-warn dd {
  color: var(--e-warn-fg);
}

/* the tax mode changed with the party (Place of Supply ≠ branch state): an
   info-coloured fact, same shape as the warning */
.dl--inline > .is-info dt,
.dl--inline > .is-info dd {
  color: var(--e-info-fg);
}

.dl--inline > :is(.is-warn, .is-info) dt {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: var(--fw-semibold);
}

.dl--inline > :is(.is-warn, .is-info) dt .icon {
  width: 14px;
  height: 14px;
  color: var(--e-warn);
  align-self: center;
}

.dl--inline > .is-info dt .icon {
  color: var(--e-info);
}

.dl--inline > :is(.is-warn, .is-info) dd strong {
  font-weight: var(--fw-semibold);
}

/* tablets (1024 × 768 is an SRS viewport): 4 columns — party (2) · branch ·
   date, then due · Place of Supply · Reverse Charge. Hints fold into the
   controls' descriptions (still read by aria-describedby, and the title);
   only the tax-mode tag stays visible. */
@container main (max-width: 1099.98px) {
  .doc-head {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    row-gap: var(--sp-2);
  }

  .doc-head > .field:first-child {
    grid-column: span 2;
  }

  /* Place of Supply takes two columns, its tax-mode tag beside the select,
     so the band stays two rows: due · Place of Supply + tag · Reverse Charge */
  .doc-head > .field:has(> .field__hint .tag) {
    grid-column: span 2;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--sp-3);
    align-items: center;
  }

  .doc-head > .field:has(> .field__hint .tag) > .field__label {
    grid-column: 1 / -1;
  }

  .doc-head .field__hint:not(:has(.tag)) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

@container main (max-width: 639.98px) {
  .doc-head {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-3);
  }

  .doc-head > .field:nth-child(2),
  .doc-head > .field:nth-child(5) {
    grid-column: 1 / -1;
  }

  .doc-head > .field:has(> .field__hint .tag) {
    grid-template-columns: minmax(0, 1fr);
  }

  .doc-head .dl--inline {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 2px var(--sp-3);
  }

  .doc-head .dl--inline > div {
    display: contents;
  }

  .doc-head .dl--inline > div + div::before {
    display: none;
  }

  .doc-head .dl--inline dd {
    white-space: normal;
  }

}

/* Item search — static depiction of an open type-ahead (item, party and
   account pickers). The React build renders an accessible combobox popover;
   in the prototype the input also carries a native <datalist>, so typing
   really suggests items. The depiction itself is not interactive (no listbox
   role): it specifies what the popover shows.
   <div class="suggest"><p class="suggest__head">…</p>
     <ul class="suggest__list"><li class="suggest__item is-highlighted">
       <span class="suggest__name">…</span><span class="suggest__meta">…</span>
     </li></ul></div> */
.suggest {
  max-width: 36rem;
  overflow: hidden;
  border: 1px solid var(--e-border);
  border-radius: var(--r-md);
  background: var(--e-raised);
  box-shadow: var(--shadow-2);
}

.suggest__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-1) var(--sp-3);
  padding: 6px 10px;
  border-bottom: 1px solid var(--e-border);
  background: var(--e-subtle);
  color: var(--e-muted);
  font-size: var(--fs-xs);
  white-space: normal;
}

.suggest__list {
  margin: 0;
  padding: var(--sp-1);
  list-style: none;
}

.suggest__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 2px var(--sp-4);
  padding: 6px 8px;
  border-radius: var(--r-sm);
  white-space: normal;
}

.suggest__item.is-highlighted {
  background: var(--e-primary-soft);
  box-shadow: inset 3px 0 0 var(--e-primary);
}

.suggest__name {
  color: var(--e-fg);
  font-weight: var(--fw-semibold);
}

.suggest__name mark {
  background: transparent;
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 2px;
}

.suggest__meta {
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}

/* Priority columns — a wide table keeps every column when the page has room
   and folds secondary columns into sub-lines when it is narrower. Two tiers,
   measured on the space the sidebar leaves (container "main"):
     th/td.col-wide  shown when main ≥ 1400px (1920 desktops)
     .sub-narrow     the same value as a sub-line in another cell, shown below 1400px
     th/td.col-mid   shown when main ≥ 1100px (1366 laptops)
     .sub-mid        the same value as a sub-line, shown below 1100px (tablets, phones)
   Screen readers get exactly one copy: the other is display:none.           */
@container main (max-width: 1399.98px) {
  .table .col-wide {
    display: none;
  }
}

@container main (min-width: 1400px) {
  .table .sub-narrow {
    display: none;
  }
}

@container main (max-width: 1099.98px) {
  .table .col-mid {
    display: none;
  }
}

@container main (min-width: 1100px) {
  .table .sub-mid {
    display: none;
  }
}

/* ── 20 · ext: promoted from batch 2–3 (G04–G09, 28 Sep 2026) ─────────────
   Each block replaces page-local copies that the groups listed for
   promotion. Style guide: Tables → Statement, Set-off, Dr / Cr entry,
   Allocation; Forms → Header bands, File upload; Components → Cards,
   Segmented. Usage: 05-page-patterns §10.                                  */

/* a link in a card foot sits on --e-subtle, where the theme colour misses
   4.5:1; the darker step (lighter in dark mode) passes (04 §6.4). :where()
   keeps it under the pagination's own colours.                             */
:where(.card__foot) a:not(.btn) {
  color: var(--e-primary-dark);
}

/* stacked tables on phones: a row head that is not a record field (a tax
   head, a particulars label) is a block, not a stub cell with a border */
@media (max-width: 639.98px) {
  .table--stack tbody th[scope="row"]:not([data-col]) {
    display: block;
    padding: var(--sp-2) var(--sp-4) 0;
    border: 0;
  }
}

/* the control line under a statement or register ("Tallies. Debits =
   credits …"): <p class="card__foot card__foot--note check-note">{{icon
   circle-check}}<span>…</span></p>, .check-note--err when it does not
   tally; tr.check-row is the same control as a table row.                  */
.check-note .icon,
.check-row .icon {
  color: var(--e-ok-fg);
}

.check-note--err .icon {
  color: var(--e-err-fg);
}

.check-row .icon {
  margin-inline-end: var(--sp-1);
  vertical-align: -2px;
}

/* a nil or zero figure in a statement reads quieter than a real one */
.muted-num {
  color: var(--e-muted);
}

/* Statement table — a financial statement or computation (trial balance,
   P&L, balance sheet, cash flow, day book totals, GSTR-3B, the GST
   computation). <table class="table table--dense stmt"> with rows:
     tr.stmt__group   a group heading (th scope="rowgroup" colspan=…)
     th.stmt__l1 / .stmt__l2   indented particulars (level 1 / 2)
     tr.stmt__head    a heading line that carries figures
     tr.stmt__sub     a subtotal (strong rule above)
     tr.stmt__key     the line the statement exists for (gross / net profit,
                      a total): strong rule, surface ground, bold
     tr.stmt__net     a tinted net line inside a computation
     tr.stmt__memo    a quiet memo line under the row before it (no rule)
   .stmt--heads: a computation split by tax head (IGST · CGST · SGST · Cess)
   with a total cell td.stmt__tot — group rows are quiet captions, and on
   phones each row is one line (particulars · total) with the heads as a
   quiet line under it (tr.stmt__none: that one line only).                 */
.table.stmt tbody tr.stmt__group > * {
  background: var(--e-subtle);
  color: var(--e-fg);
  font-weight: var(--fw-semibold);
}

.table.stmt tbody tr.stmt__sub > * {
  border-top: 1px solid var(--e-border-strong);
  font-weight: var(--fw-semibold);
}

.table.stmt tbody tr.stmt__key > * {
  border-top: 1px solid var(--e-border-strong);
  background: var(--e-surface);
  font-weight: var(--fw-bold);
}

.table.stmt tbody tr.stmt__sub + tr > *,
.table.stmt tbody tr.stmt__key + tr > * {
  border-top: 0;
}

.table.stmt .stmt__l1 {
  padding-left: calc(var(--sp-4) + var(--sp-4));
}

.table.stmt .stmt__l2 {
  padding-left: calc(var(--sp-4) + var(--sp-8));
}

.table.stmt tbody tr.stmt__head > * {
  font-weight: var(--fw-semibold);
}

.table.stmt th[scope="row"] {
  font-weight: inherit;
}

.table.stmt .stmt__group .muted {
  font-weight: var(--fw-regular);
}

.table.stmt .stmt__net > * {
  background: var(--e-subtle);
}

.table.stmt tr:has(+ .stmt__memo) > * {
  border-bottom: 0;
}

/* a split computation keeps the table's semibold row heads, in the foot too */
.table.stmt--heads th[scope="row"] {
  font-weight: var(--fw-semibold);
}

.table.stmt--heads tbody tr.stmt__group > th {
  padding-top: var(--sp-3);
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  background: var(--e-subtle);
}

.table.stmt--heads tbody tr.stmt__memo > th {
  padding-top: 0;
  color: var(--e-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
  white-space: normal;
}

@container main (max-width: 639.98px) {
  .table.stmt .stmt__l1 {
    padding-left: var(--sp-6);
  }

  .table.stmt .stmt__l2 {
    padding-left: var(--sp-8);
  }
}

@media (max-width: 639.98px) {
  .table.stmt--heads tr { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px var(--sp-2); padding: var(--sp-2) var(--sp-4); }
  .table.stmt--heads tr > th[scope="row"] { flex: 1 0 calc(100% - 7rem - var(--sp-2)); min-width: 0; padding: 0; border: 0; white-space: normal; }
  .table.stmt--heads tr > th[scope="row"] small { white-space: normal; }
  .table.stmt--heads tr > th[colspan] { flex-basis: 100%; }
  .table.stmt--heads tr > td { display: block; flex: none; width: auto; padding: 0; border: 0; text-align: left; }
  .table.stmt--heads tr > td.stmt__tot { order: 1; flex: 0 0 7rem; text-align: right; }
  .table.stmt--heads tfoot tr { background: var(--e-subtle); }
  .table.stmt--heads tfoot tr > * { background: none; }
  .table.stmt--heads tr > td.stmt__tot::before { content: none; }
  .table.stmt--heads tr > td:not(.stmt__tot) { order: 2; color: var(--e-muted); font-size: var(--fs-xs); }
  .table.stmt--heads tr > td:not(.stmt__tot)::before { content: attr(data-label) " "; font-size: inherit; font-weight: var(--fw-regular); }
  .table.stmt--heads tr.stmt__net { background: var(--e-subtle); }
  .table.stmt--heads .stmt__net > * { background: none; }
  .table.stmt--heads tr.stmt__none > td:not(.stmt__tot) { display: none; }
  .table.stmt--heads tr.stmt__group { padding: 0; }
  .table.stmt--heads tbody tr.stmt__group > th { padding: var(--sp-2) var(--sp-4); }
  .table.stmt--heads tr.stmt__memo { padding-top: 0; }
  .table.stmt--heads tbody tr.stmt__sub > * { border-top: 0; }
  .table.stmt--heads tr.stmt__sub { border-top: 1px solid var(--e-border-strong); }
}

/* Phone tier of the priority columns (beside .col-mid / .col-wide): below
   640 px of main, .fold-phone cells (and links) go; .only-phone shows — a
   table cell as a cell (Trial balance "Closing (₹)" with Dr / Cr), anything
   else as a block (a phone-only summary card).                             */
.only-phone {
  display: none;
}

@container main (max-width: 639.98px) {
  .table .fold-phone {
    display: none;
  }

  .table .only-phone {
    display: table-cell;
  }

  .only-phone:not(td, th) {
    display: block;
  }
}

/* record rows on phones: a negative figure reads "−₹5.80", not "₹−5.80"
   (write the absolute value, class is-neg on the amount cell)              */
@container main (max-width: 639.98px) {
  .table--records tbody [data-col="amount"].is-neg::before {
    content: "−₹";
  }
}

/* a list toolbar whose four filters have long option texts (purchases,
   expenses): <details class="toolbar__filters toolbar__filters--narrow">
   narrows the selects so they and Apply stay on one row at 1366 (the golden
   list has three filters; a list whose options are short needs nothing)    */
@container main (min-width: 640px) {
  .toolbar__filters--narrow > .select {
    max-width: 9rem;
  }

  .toolbar__filters--narrow > .select[name="period"] {
    max-width: 11rem;
  }
}

/* a period filter's From / To (.toolbar__range) shows only while the Period
   select is on "Custom range" (<option value="custom">) */
.toolbar:not(:has(select[name="period"] option[value="custom"]:checked)) .toolbar__range {
  display: none;
}

/* Section 49 set-off matrix (GST summary, settlement wizard and detail;
   GSTR-3B Table 6.1): <table class="table table--dense table--stack setoff">
   with one column group th.setoff__group "Paid through ITC", a step no. as
   <small> under each cell, a prohibited cell td.setoff__no holding
   .setoff__ban "Not allowed", the cash column td.setoff__cash, and a head
   short of cash tr.is-short. The numbered list under it is ol.setoff-steps
   (li > .setoff-steps__n · text · .money; li.setoff-steps__nil for a nil step). */
.table.setoff th small,
.table.setoff td small {
  display: block;
  color: var(--e-faint);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
}

.table.setoff thead .setoff__group {
  text-align: center;
  border-bottom: 1px solid var(--e-border);
}

.table.setoff td.setoff__no {
  background: var(--e-subtle);
  color: var(--e-faint);
}

.table.setoff .setoff__ban {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--e-muted);
}

.table.setoff .setoff__cash {
  border-left: 1px solid var(--e-border);
}

.table.setoff tr.is-short > td.setoff__cash,
.table.setoff tr.is-short > td.setoff__cash small {
  color: var(--e-err-fg);
}

.table.setoff tbody tr.is-short > td.setoff__cash {
  background: var(--e-err-soft);
}

.setoff-steps { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; font-size: var(--fs-sm); }
.setoff-steps > li { display: grid; grid-template-columns: 4.25rem minmax(0, 1fr) auto; gap: var(--sp-3); align-items: baseline; padding: 6px 0; border-bottom: 1px solid var(--e-border); }
.setoff-steps > li:last-child { border-bottom: 0; }
.setoff-steps__n { color: var(--e-muted); font-size: var(--fs-xs); font-weight: var(--fw-semibold); }
.setoff-steps > li.setoff-steps__nil { padding: 3px 0; color: var(--e-muted); font-size: var(--fs-xs); }
.setoff-steps > li.setoff-steps__nil .setoff-steps__n { font-weight: var(--fw-regular); }

@media (max-width: 639.98px) {
  /* the step list names every step, so no cell repeats it; only the short
     cash cell keeps its note (ledger balance · short) */
  .table.setoff td small { display: none; }
  .table.setoff tr.is-short td.setoff__cash small { display: block; flex-basis: 100%; text-align: right; }
  .table.setoff .setoff__cash { border-left: 0; }
  .table.setoff thead .setoff__group { border: 0; }
}

/* GSTIN switch — a .segmented of the company's registrations, each with its
   status ("₹551.07 to pay", or a warn dot + the shortfall):
   <span class="segmented gstin-switch" role="group" aria-labelledby="…">
     <a href="#gstin-19" aria-current="page">19 · West Bengal <span
       class="gstin-switch__st"><span class="money">₹551.07</span><span
       class="gstin-switch__unit"> to pay</span></span></a> …</span>
   On phones the status goes under the registration.                        */
.gstin-switch > a { gap: 6px; }
.gstin-switch__st { display: inline-flex; align-items: center; gap: 4px; color: var(--e-muted); font-weight: var(--fw-regular); font-variant-numeric: tabular-nums; }
.gstin-switch__st.is-warn { color: var(--e-warn-fg); font-weight: var(--fw-semibold); }
.gstin-switch__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--e-warn); }
.gstin-switch__st::before { content: "·"; color: var(--e-faint); font-weight: var(--fw-regular); }
.gstin-switch__st.is-warn::before { content: none; }

@container main (max-width: 639.98px) {
  .gstin-switch > a { flex-direction: column; gap: 0; padding-block: 3px; line-height: 1.25; }
  .gstin-switch__st { font-size: var(--fs-xs); }
  .gstin-switch__st::before { content: none; }
}

/* a segmented option that cannot be chosen yet: <label><input type="radio"
   disabled aria-describedby="why">{{icon lock icon--sm}}…</label> — faint,
   not-allowed cursor, the reason in the described-by note                  */
.segmented > label:has(> input:disabled),
.segmented > label:has(> input:disabled) > input {
  cursor: not-allowed;
}

.segmented > label:has(> input:disabled) {
  color: var(--e-faint);
  font-weight: var(--fw-regular);
  background: transparent;
}

.segmented > label:has(> input:disabled):hover {
  color: var(--e-faint);
}

/* File choice. .file-pick: a secondary button wrapping a visually hidden
   file input — <label class="btn btn--secondary btn--sm file-pick">{{icon
   upload}}Choose another file<input class="visually-hidden" type="file" …>
   </label> + a .field__hint naming the file. .dropzone: the first-time
   upload target — icon, label, the native input (its "No file chosen" text
   hidden, the button styled), then p.dropzone__chosen naming the file.     */
.file-pick {
  justify-self: start;
  cursor: pointer;
}

.file-pick:has(input:focus-visible) {
  outline: 2px solid var(--e-focus);
  outline-offset: 2px;
}

.file-pick + .field__hint {
  overflow-wrap: anywhere;
}

.dropzone { display: grid; justify-items: center; gap: var(--sp-2); padding: var(--sp-6) var(--sp-4); border: 1px dashed var(--e-border-strong); border-radius: var(--r-md); background: var(--e-subtle); text-align: center; }
.dropzone:focus-within { outline: 2px solid var(--e-focus); outline-offset: 2px; }
.dropzone > .icon { width: 24px; height: 24px; color: var(--e-muted); }
.dropzone input[type="file"] { width: 8.5rem; color: transparent; font: inherit; }
.dropzone input[type="file"]::file-selector-button { color: var(--e-fg); padding: 6px 12px; border: 1px solid var(--e-border-strong); border-radius: var(--r-md); background: var(--e-raised); font: inherit; cursor: pointer; }
.dropzone__chosen { display: inline-flex; align-items: center; gap: var(--sp-2); margin: 0; font-size: var(--fs-sm); }
.dropzone__chosen .icon { color: var(--e-ok-fg); }

/* Header band variants (the band itself is .doc-head, §19).
   .doc-head--4  an editor with no party: a wide first field + three
                 (journal: Narration · Date · Voucher no. · Reference); the
                 first field takes the full width on phones
   .doc-head--3  a return / period band: GSTIN (wide) · period · branch
   .doc-head--3x2  six fields whose values are long, as two rows of three
                 (a GST challan: GSTIN · CIN · date / period · bank · mode);
                 on phones the pair is date · period
   A free-text field in a band (a note's Reason) is a two-row
   <textarea class="textarea" rows="2"> over two columns, so a sentence
   shows whole instead of scrolling inside one line.
   .field__hint.is-kept  a hint that is a check (the balance of the account
                 under Deposit to) stays visible below 1100 px
   A toolbar right under the band in the same card is ruled off from it.
   A band whose fields are not the invoice's six sets columns that show every
   value whole at 1366 and 1024 (tools/inputfit.mjs finds a value cut off). */
@container main (min-width: 1100px) {
  .doc-head.doc-head--4 { grid-template-columns: minmax(16rem, 3fr) minmax(9.75rem, 1fr) minmax(10.5rem, 1.1fr) minmax(10rem, 1.2fr); }
  .doc-head.doc-head--3 { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr); }
}

@container main (min-width: 640px) {
  .doc-head.doc-head--3x2 { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr); }
  .doc-head.doc-head--3x2 > .field:first-child { grid-column: auto; }
}

.doc-head > .field:has(> .textarea) { grid-column: span 2; }
.doc-head .textarea { min-height: 36px; resize: vertical; }

@container main (max-width: 639.98px) {
  .doc-head.doc-head--4 > .field:first-child,
  .doc-head.doc-head--3 > .field:first-child { grid-column: 1 / -1; }
  .doc-head.doc-head--3 > .field:nth-child(2) { grid-column: auto; }
  .doc-head.doc-head--3x2 > .field:is(:nth-child(1), :nth-child(2), :nth-child(6)) { grid-column: 1 / -1; }
  .doc-head > .field:has(> .textarea) { grid-column: 1 / -1; }
}

.doc-head .field__hint.is-kept { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; }
.card > .doc-head + .toolbar { border-top: 1px solid var(--e-border); }
.card > .toolbar + .doc-head { padding-top: var(--sp-4); }

/* Dr / Cr line grid — a journal's lines (any two-sided entry):
   <table class="table table--entry table--stack table--journal" data-grid-keys>
   with td[data-col=line|account|dr|cr|note|remove]; the foot's totals row
   tr.entry-total holds th[data-col=label], td[data-col=dr|cr] and
   td[data-col=diff] with the .balance-fact. On phones a line is a card
   (account / Debit | Credit / narration); the totals read label · Debits |
   Credits · difference.                                                    */
.table--entry.table--journal [data-col="account"] { --_in-min: 15rem; }
.table--entry.table--journal th[data-col="account"] { width: 34%; }
.table--entry.table--journal :is([data-col="dr"], [data-col="cr"]) { --_in-min: 7rem; }
.table--entry.table--journal [data-col="note"] { --_in-min: 10rem; }
.table--entry tfoot tr.entry-total > * { border-top: 1px solid var(--e-border-strong); }

/* a computed cell that fails validation (the difference of a rejected post)
   is outlined like an invalid input */
.table--entry tfoot td.is-invalid { outline: 2px solid var(--e-err-fg); outline-offset: -2px; border-radius: var(--r-sm); }

/* the balanced / not-balanced fact of a two-sided entry (journal totals;
   contra, GST settlement): <span class="balance-fact is-ok">{{icon
   circle-check icon--sm}} Balanced …</span>, is-err + circle-alert when it is
   not; aria-live="polite" on its cell. A likely cause under it is
   small.balance-hint.                                                      */
.balance-fact { font-weight: var(--fw-semibold); white-space: nowrap; }
.balance-fact.is-ok { color: var(--e-ok-fg); }
.balance-fact.is-err { color: var(--e-err-fg); }
.balance-hint,
.table td > .balance-hint { display: block; margin-top: 2px; color: var(--e-muted); font-size: var(--fs-xs); white-space: normal; }

@media (max-width: 639.98px) {
  .table--entry.table--journal.table--stack tbody tr:not(.entry-subrow) { grid-template-areas: "line line line remove" "account account account account" "dr dr cr cr" "note note note note"; }
  .table--entry.table--journal.table--stack :is([data-col="account"], [data-col="dr"], [data-col="cr"], [data-col="note"]) { grid-area: var(--_area); }
  .table--entry.table--journal.table--stack [data-col="account"] { --_area: account; }
  .table--entry.table--journal.table--stack [data-col="account"]::before { display: none; }
  .table--entry.table--journal.table--stack [data-col="dr"] { --_area: dr; }
  .table--entry.table--journal.table--stack [data-col="cr"] { --_area: cr; }
  .table--entry.table--journal.table--stack [data-col="note"] { --_area: note; }
  .table--entry.table--journal.table--stack tfoot tr { flex-wrap: wrap; }
  .table--entry.table--journal.table--stack tfoot tr.entry-total { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-areas: "label label" "dr cr" "diff diff"; gap: var(--sp-1) var(--sp-4); padding: var(--sp-2) var(--sp-4); border-top: 1px solid var(--e-border-strong); }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > * { width: auto; padding: 0; border: 0; }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > td:empty { display: none; }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > th[data-col="label"] { grid-area: label; font-weight: var(--fw-semibold); }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > :is([data-col="dr"], [data-col="cr"]) { display: flex; justify-content: space-between; gap: var(--sp-2); font-weight: var(--fw-semibold); }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > [data-col="dr"] { grid-area: dr; }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > [data-col="cr"] { grid-area: cr; }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > [data-col="diff"] { grid-area: diff; display: block; text-align: right; }
  .table--entry.table--journal.table--stack tfoot tr.entry-total > [data-col="diff"].is-invalid { padding: var(--sp-1) var(--sp-2); }
  .table--entry.table--journal.table--stack tfoot .balance-fact { white-space: normal; }
}

/* Dr / Cr entry sheet — account | Debit | Credit with inputs in the cells,
   for a fixed list of accounts (opening balances; a bank / cash opening):
   <table class="table table--entry table--sheet"> with th[scope=row] account
   heads, td[data-col=dr|cr][data-label] > .input, tr.sheet__group (a group
   caption row) and tr.sheet__sum (a group's sums). An amount changed since
   the last save is .input.is-changed. Every amount cell shows a hairline
   input edge at every width; on phones a line is a small card (account,
   then Debit | Credit side by side).                                       */
.table--entry.table--sheet [data-col="dr"],
.table--entry.table--sheet [data-col="cr"] { --_in-min: 7.5rem; width: 9.5rem; }
.table--entry.table--sheet tbody th { font-weight: var(--fw-regular); }
.table--entry.table--sheet td > .input { border-color: var(--e-border); background: var(--e-raised); }
.table--entry.table--sheet td > .input:hover { border-color: var(--e-border-strong); }
.table--entry.table--sheet tbody :is(th, td) { vertical-align: middle; }
.table--entry.table--sheet tbody th a { color: inherit; }
.table--entry.table--sheet tbody th small { display: block; }
/* group captions and sum rows: the account heads keep the regular weight */
.table--entry.table--sheet .sheet__group th { padding-top: var(--sp-3); color: var(--e-muted); font-size: var(--fs-xs); background: var(--e-subtle); }
.table--entry.table--sheet .sheet__sum > * { background: var(--e-subtle); }
.table--entry.table--sheet .sheet__sum > td { font-weight: var(--fw-semibold); }
.table--entry.table--sheet .input.is-changed { border-color: var(--e-warn); background: var(--e-warn-soft); }

@container main (max-width: 639.98px) {
  .table--entry.table--sheet thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .table--entry.table--sheet, .table--entry.table--sheet tbody, .table--entry.table--sheet tfoot { display: block; width: 100%; }
  .table--entry.table--sheet tr { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px var(--sp-2); padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--e-border); }
  .table--entry.table--sheet tr > * { display: block; width: auto; padding: 0 !important; border: 0 !important; }
  .table--entry.table--sheet tr > th { grid-column: 1 / -1; min-width: 0; white-space: normal; }
  .table--entry.table--sheet td:empty { display: none; }
  .table--entry.table--sheet td[data-label]::before { content: attr(data-label); display: block; color: var(--e-muted); font-size: var(--fs-xs); font-weight: var(--fw-medium); text-align: left; }
  .table--entry.table--sheet td > .input { width: 100%; min-width: 0; border-color: var(--e-border-strong); background: var(--e-raised); }
  .table--entry.table--sheet tr.sheet__group, .table--entry.table--sheet tr.sheet__sum { background: var(--e-subtle); }
  .table--entry.table--sheet .sheet__sum td:not(:empty) { grid-column: 1 / -1; display: flex; justify-content: space-between; }
  .table--entry.table--sheet .sheet__sum td:not(:empty)::before { display: inline; }
  .table--entry.table--sheet tfoot td { display: flex; flex-direction: column; align-items: flex-end; }
}

/* Allocation grid — a party's open invoices / bills with ONE editable column:
   <table class="table table--entry table--stack table--alloc" data-grid-keys
   data-grid-fixed> with td[data-col=line|item|due|open|alloc|after|status]
   (item = the document no. + a date sub-line; date and total are .col-wide).
   At rest the Allocate / Pay inputs keep a field edge, so the editable
   column reads as editable. tfoot tr.alloc-note carries the advance left
   over. On phones a line is a card: no. + document / Due / Open · Allocate
   · Open after / the status line.                                          */
.table--entry.table--alloc td[data-col="alloc"] > .input { border-color: var(--e-border-strong); background: var(--e-raised); }
.table--entry.table--alloc tfoot tr.alloc-note > td { padding-top: var(--sp-2); padding-bottom: var(--sp-2); white-space: normal; font-weight: var(--fw-regular); }
.table--entry.table--alloc tfoot tr.alloc-note p + p { margin-top: 2px; }
.table--entry.table--alloc tfoot tr.alloc-note a { text-decoration: underline; text-underline-offset: 2px; }

@media (max-width: 639.98px) {
  .table--entry.table--alloc.table--stack tbody tr:not(.entry-subrow) {
    grid-template-columns: 1.25rem repeat(3, minmax(0, 1fr));
    grid-template-areas: "line item item item" ". due due due" ". open alloc after" ". status status status";
    row-gap: 4px;
  }
  .table--entry.table--alloc.table--stack tbody tr > .col-wide { display: none; }
  .table--entry.table--alloc.table--stack tbody [data-col="line"]::before { content: none; }
  .table--entry.table--alloc.table--stack tbody [data-col="item"] { font-weight: var(--fw-semibold); }
  .table--entry.table--alloc.table--stack tbody [data-col="item"] > small { font-weight: var(--fw-regular); }
  .table--entry.table--alloc.table--stack tbody [data-col="due"] { grid-area: due; }
  .table--entry.table--alloc.table--stack tbody [data-col="open"] { grid-area: open; }
  .table--entry.table--alloc.table--stack tbody [data-col="alloc"] { grid-area: alloc; }
  .table--entry.table--alloc.table--stack tbody [data-col="after"] { grid-area: after; }
  .table--entry.table--alloc.table--stack tbody [data-col="status"] { grid-area: status; }
  .table--entry.table--alloc.table--stack tbody td:is([data-col="due"], [data-col="status"]) { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: 2px var(--sp-2); }
  .table--entry.table--alloc.table--stack tbody td:is([data-col="due"], [data-col="status"]) > small { flex-basis: auto; margin-top: 0; }
  .table--entry.table--alloc.table--stack tbody :is([data-col="open"], [data-col="alloc"], [data-col="after"]),
  .table--entry.table--alloc.table--stack tbody :is([data-col="open"], [data-col="alloc"], [data-col="after"])::before { text-align: right; }
  .table--entry.table--alloc.table--stack tbody :is([data-col="open"], [data-col="after"]) > small { text-align: right; }
  .table--entry.table--alloc.table--stack tbody [data-col="status"] > .sub-mid { display: none; }
  .table--entry.table--alloc.table--stack tfoot td[data-label]::before { content: attr(data-label) " "; }
  .table--entry.table--alloc.table--stack tfoot tr.alloc-note > td[data-col] { flex: 1 1 100%; text-align: left; }
}

/* a one-line keyboard legend in a card foot, for a small fixed grid (an
   allocation grid): <div class="card__foot card__foot--note grid-keys-line">
   {{icon keyboard}}<p class="text-sm muted"><kbd class="kbd">Enter</kbd> next
   invoice · …</p></div>. Hidden on phones, like the .grid-keys card.       */
@container main (max-width: 639.98px) {
  .grid-keys-line { display: none; }
}

/* the Chart of Accounts tree on phones: each row is a small grid — the name
   on its own line, code and balance under it — with shallower indents     */
@container main (max-width: 639.98px) {
  .tree ul { margin-left: 7px; padding-left: 8px; }
  .tree__row { display: grid; grid-template-columns: 14px minmax(0, 1fr) auto; grid-template-areas: "chev label label" ". code amount"; column-gap: var(--sp-2); row-gap: 0; padding-block: 6px; }
  .tree__row::before { grid-area: chev; align-self: center; }
  .tree__label { grid-area: label; white-space: normal; overflow: visible; }
  .tree__row > .code { grid-area: code; justify-self: start; }
  .tree__amount { grid-area: amount; min-width: 0; }
}

/* a print-only header for a report or statement printed as a working paper
   (legal name, PAN, GSTINs, statement + period, "Amounts in ₹", basis,
   printed by + date): <div class="print-head">…</div> first in the page.
   print.css shows it; on screen it never renders.                          */
.print-head {
  display: none;
}

/* ── 21 · ext: promoted from batch 4 (G10–G11, 28 Sep 2026) ───────────────
   Each block replaces a G11 page-local copy (settings / subscription /
   audit) and is what G12 (Head Admin: plans, tenant usage, global audit,
   platform payments) starts from. Style guide: Components → Theme cards,
   Plan cards and usage, Paper preview, Person cell; Tables → Audit diff.
   Usage: 05-page-patterns §11.                                             */

/* Theme cards — one <button data-set-theme="<id>" aria-pressed> per theme
   (app.js applies it and keeps aria-pressed), in <ul class="theme-cards">:
   <button class="theme-card"><span class="theme-card__swatch"
   data-swatch="<id>" aria-hidden="true"><span class="theme-card__side"><i></i>
   <i class="is-act"></i><i></i></span><span class="theme-card__light"><i></i>
   </span><span class="theme-card__dark"><i></i></span></span><span
   class="theme-card__name">Name <span class="theme-card__on">…In use</span>
   </span><span class="theme-card__meta">…</span></button>. The swatch paints
   with the --sw-* preview tokens of [data-swatch] (tokens.css); a theme that
   is always dark leaves out __dark.                                         */
.theme-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(14.5rem, 1fr)); gap: var(--sp-3); margin: 0; padding: 0; list-style: none; }
.theme-card { position: relative; display: grid; gap: var(--sp-2); width: 100%; height: 100%; padding: var(--sp-3); border: 1px solid var(--e-border); border-radius: var(--r-lg); background: var(--e-raised); color: var(--e-fg); font: inherit; text-align: left; cursor: pointer; }
.theme-card:hover { border-color: var(--e-border-strong); }
.theme-card[aria-pressed="true"] { border-color: var(--e-primary); box-shadow: inset 0 0 0 1px var(--e-primary); }
.theme-card__swatch { display: grid; grid-template-columns: 2.25rem 1fr 1fr; height: 3.25rem; overflow: hidden; border: 1px solid var(--e-border); border-radius: var(--r-md); }
.theme-card__swatch:not(:has(.theme-card__dark)) { grid-template-columns: 2.25rem 1fr; }
.theme-card__side { display: grid; align-content: center; gap: 4px; padding: 0 6px; background: var(--sw-sidebar); }
.theme-card__side i { display: block; height: 4px; border-radius: 2px; background: var(--sw-sidebar-muted); opacity: 0.55; }
.theme-card__side i.is-act { height: 7px; background: var(--sw-sidebar-active); }
.theme-card__light, .theme-card__dark { display: grid; place-items: center; background: var(--sw-raised-l); }
.theme-card__dark { background: var(--sw-raised-d); }
.theme-card__light i, .theme-card__dark i { display: block; width: 62%; height: 12px; border-radius: 3px; background: var(--sw-primary-l); }
.theme-card__dark i { background: var(--sw-primary-d); }
.theme-card__name { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--sp-2); font-weight: var(--fw-semibold); }
.theme-card__meta { color: var(--e-muted); font-size: var(--fs-xs); line-height: var(--lh-snug); }
.theme-card__on { display: none; align-items: center; gap: 4px; margin-left: auto; padding: 2px 8px; border-radius: var(--r-full); background: var(--e-primary); color: var(--e-on-primary); font-size: var(--fs-xs); font-weight: var(--fw-semibold); white-space: nowrap; }
.theme-card[aria-pressed="true"] .theme-card__on { display: inline-flex; }

/* Plan cards — radio cards for a subscription plan, with prices that follow
   a Duration switch, no script: <form class="plan-picker"> holds
   <fieldset class="field plan-dur"> (a .segmented of radios name="duration"
   value="1|2|3") and <fieldset class="plan-cards"> of <label class="plan-card">
   (__name with the radio + a badge, one <span data-dur="1|2|3"> per duration
   holding __price + __line, __list of facts). A plan that cannot be chosen
   has a disabled radio and its reason as a __line in aria-describedby. The
   duration keys are fixed (1–3 years), like the fixed "invalid" state key.  */
.plan-dur .segmented { width: max-content; max-width: 100%; }
.plan-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--sp-3); }
.plan-card { position: relative; display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-4); border: 1px solid var(--e-border); border-radius: var(--r-lg); background: var(--e-raised); cursor: pointer; }
.plan-card:has(input:checked) { border-color: var(--e-primary); box-shadow: inset 0 0 0 1px var(--e-primary); }
.plan-card:has(input:disabled) { cursor: not-allowed; background: var(--e-subtle); }
.plan-card:has(input:focus-visible) { outline: 2px solid var(--e-focus); outline-offset: 2px; }
.plan-card__name { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-lg); font-weight: var(--fw-semibold); }
.plan-card__name input { margin: 0; accent-color: var(--e-primary); }
.plan-card__name .badge { margin-left: auto; }
.plan-card__price { display: block; font-family: var(--font-display); font-size: var(--fs-xl); font-stretch: var(--display-stretch); font-weight: var(--display-weight); letter-spacing: var(--display-tracking); }
.plan-card__line { display: block; color: var(--e-muted); font-size: var(--fs-sm); }
.plan-card__list { display: block; margin: 0; padding: var(--sp-2) 0 0; border-top: 1px solid var(--e-border); font-size: var(--fs-sm); }
.plan-card__list > span { display: block; padding: 1px 0; }
.plan-picker [data-dur] { display: none; }
.plan-picker [data-dur="1"],
.plan-picker:has(input[name="duration"][value="2"]:checked) [data-dur="2"],
.plan-picker:has(input[name="duration"][value="3"]:checked) [data-dur="3"] { display: block; }
.plan-picker:has(input[name="duration"][value="2"]:checked, input[name="duration"][value="3"]:checked) [data-dur="1"] { display: none; }

/* Usage against plan limits — <div class="usage"> of <div class="usage__row">
   <p>label (a link to the list)</p><p class="tabular">12 of 15</p><progress
   class="progress" value max aria-label="Users: 12 of 15"></progress></div>.
   The meter is neutral: a limit is a fact, not a status (03-brand §6.3).   */
.usage { display: grid; gap: var(--sp-4); }
.usage__row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px var(--sp-3); align-items: baseline; }
.usage__row p { margin: 0; }
.usage__row .progress { grid-column: 1 / -1; }

/* Paper preview inside the app — a document or print detail shown on paper
   in any theme: <div class="paper stack" data-surface="paper"> (tokens.css
   maps it to the light, ink-only palette). A Validure-owned document (the
   SaaS tax invoice, 03-brand §4.4) opens with .paper__head (.paper__brand:
   the emblem + supplier; .paper__title: the statutory title). .paper--row is
   a one-line preview box (a logo, a signature block) in a settings field.  */
.paper { padding: var(--sp-5); border: 1px solid var(--e-border); border-radius: var(--r-md); background: var(--e-raised); color: var(--e-fg); }
.paper--row { display: flex; align-items: center; gap: var(--sp-4); min-height: 4.5rem; padding: var(--sp-3) var(--sp-4); }
.paper--row p { margin: 0; }
.paper__head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--e-border-strong); }
.paper__brand { display: flex; align-items: center; gap: var(--sp-3); }
.paper__brand img { width: 45px; height: 45px; }
.paper__title { margin: 0; font-family: var(--font-display); font-size: var(--fs-lg); font-weight: var(--display-weight); letter-spacing: 0.06em; }

/* Person cell — an avatar beside a name and a second line (email, role):
   <span class="user-cell"><span class="avatar avatar--sm" aria-hidden="true">
   RS</span><span><strong>Name</strong><small>email</small></span></span>   */
.user-cell { display: inline-flex; align-items: center; gap: var(--sp-2); }
.user-cell > span:last-child { display: grid; }
.user-cell small { color: var(--e-muted); font-size: var(--fs-xs); }

/* Audit diff — a record's old → new values. JSON panes: <pre class="json-diff"
   tabindex="0" aria-label="Old values, JSON"> of <span class="json-diff__line
   is-changed|is-added|is-removed"><span class="json-diff__mark"
   aria-hidden="true">~ + −</span><span>"key": value</span></span>, titled by
   h3.json-diff__title; the readable table beside them is
   table.table--dense.table--stack.table--diff with tr.is-changed. A changed
   line is a highlight, like a selected row (primary-soft), never a status
   colour: a change is neither good nor bad.                                  */
.json-diff { margin: 0; padding: var(--sp-2) 0; overflow-x: auto; border: 1px solid var(--e-border); border-radius: var(--r-md); background: var(--e-subtle); font-family: var(--font-mono); font-size: var(--fs-xs); line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
.json-diff__line { display: grid; grid-template-columns: 1.5rem minmax(0, 1fr); padding: 0 var(--sp-3) 0 0; }
.json-diff__mark { color: var(--e-muted); font-weight: var(--fw-bold); text-align: center; }
.json-diff__line:is(.is-changed, .is-added, .is-removed) { background: var(--e-primary-soft); color: var(--e-fg); }
.json-diff__line:is(.is-changed, .is-added, .is-removed) .json-diff__mark { color: var(--e-primary-dark); }
.json-diff__title { margin: 0; font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
.table--diff tbody tr.is-changed > * { background: var(--e-primary-soft); }
.table--diff tr.is-changed a { color: var(--e-primary-dark); }
/* the table fits its card at every width, whatever the values: fixed
   columns (Field 26 %, Change one word, Before / After share the rest);
   a long field name, text or code wraps inside its cell and a row reads
   from its top line; phones stack it */
.table--diff { table-layout: fixed; width: 100%; }
.table--diff :is(th, td) { white-space: normal; overflow-wrap: anywhere; }
.table--diff tbody :is(th, td) { vertical-align: top; }
.table--diff .wrap { min-width: 0; }
.table--diff thead th:first-child { width: 26%; }
.table--diff thead th:last-child { width: 6.5rem; }
.table--diff tbody td:last-child { white-space: nowrap; }

/* ── 22 · ext: promoted from batch 5 (G13 Field CRM, 29 Sep 2026) ──────────
   The mobile layout (layouts/mobile.html → .layout-mobile > .phone > .m-main)
   and its components. Replaces the 116-line page-local block every /m page
   carried (.g13-m); the page generator (tools/G13/mobile.mjs) now emits
   these classes and no <style>. Two kinds of rule:
   · touch-target variants of shared components, scoped to .m-main (the
     mobile layout content area; the app bar and tab bar size themselves in
     layout.css): .btn 44 px (.btn--lg 52 px), square .btn--icon, .chip,
     .input / .select, the segmented control, an in-flow .toast-stack;
   · Field CRM components (m-*), usable only inside the mobile layout:
     .m-head · .m-bar · .m-chips(--scroll) · .m-line · .m-lead (__top,
     __name) · .m-next (.is-overdue|is-today|is-ready|is-muted) · .m-actions
     · .m-more (details) · .m-dock (--one) · .m-hero (__name) · .m-rows
     (--link) with .m-when / .m-what · .m-pin (__input) · .m-keypad / .m-key
     (--fn) · .m-dial (__no, __go) · .m-choice / .m-tile (--wide) ·
     .m-outcome (--none|--closes|--retry|--fu, fixed call-disposition keys
     inside .m-form) · .m-pair · .m-qline (__item, __sum, __hint) ·
     .m-readonly · .m-confirm.
   And two general .segmented modifiers: --fill and --2x2.
   Style guide: Components → Field CRM (touch targets). Usage: 05-page-patterns §12. */

/* touch targets (02-ia §5, NFR-ACC-001): inside the mobile layout (.m-main) every control is at least 44 px tall */
.m-main .btn { --btn-h: 44px; }
.m-main .btn--lg { --btn-h: 52px; }
.m-main .btn--icon { width: var(--btn-h); min-width: var(--btn-h); }
.m-main .chip { min-height: 44px; padding-inline: 14px; }
.m-main .toast-stack { position: static; width: auto; }
.m-main .input, .m-main .select { min-height: 44px; }
.m-main .m-back { margin-bottom: calc(-1 * var(--sp-2)); }
/* segmented control: 44 px options on a phone (02-ia §5); --fill = equal-width options in one row, --2x2 = two rows of two (any layout) */
.m-main .segmented > :is(label, button, a) { min-height: 44px; }
.segmented--fill { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.segmented--2x2 { display: grid; grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* section head with a muted count on the right: <h2 class="m-head">My leads <small>21</small></h2> */
.m-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2); margin: 0; font-size: var(--fs-md); }
.m-head small { color: var(--e-muted); font-family: var(--font-ui); font-size: var(--fs-sm); font-weight: var(--fw-medium); letter-spacing: 0; }
/* search + one action on one row */
.m-bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; }
.m-bar > form { min-width: 0; }
.m-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
/* one scrollable row of filter chips: the cut-off chip at the edge says there are more */
.m-chips--scroll { flex-wrap: nowrap; overflow-x: auto; margin-inline: calc(-1 * var(--sp-4)); padding: 4px var(--sp-4); scrollbar-width: none; scroll-padding-inline: var(--sp-4); }
.m-chips--scroll::-webkit-scrollbar { display: none; }
.m-chips--scroll > .chip { flex: none; }
/* muted fact line under a name */
.m-line { margin: 0; color: var(--e-muted); font-size: var(--fs-sm); line-height: var(--lh-snug); }
.m-line strong { color: var(--e-fg); }
/* lead card */
.m-lead > .card__body { display: grid; gap: 6px; }
.m-lead__top { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin: 0; }
.m-lead__name { margin: 0; font-size: var(--fs-lg); line-height: 1.25; }
.m-lead__name a { color: var(--e-fg); text-decoration: none; }
.m-lead__name a:hover { color: var(--e-primary-dark); text-decoration: underline; }
.m-next { display: flex; align-items: flex-start; gap: 6px; margin: 0; color: var(--e-fg); font-size: var(--fs-sm); font-weight: var(--fw-semibold); line-height: var(--lh-snug); }
.m-next > .icon { flex: none; width: 16px; height: 16px; margin-top: 1px; }
.m-next.is-overdue { color: var(--e-err-fg); }
.m-next.is-today, .m-next.is-ready { color: var(--e-primary-dark); }
.m-next.is-muted { color: var(--e-muted); font-weight: var(--fw-medium); }
.m-actions { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--sp-2); margin-top: var(--sp-1); }
.m-more > summary { display: flex; align-items: center; gap: var(--sp-2); min-height: 44px; cursor: pointer; list-style: none; }
.m-more > summary::-webkit-details-marker { display: none; }
.m-more > summary::before { content: ""; width: 8px; height: 8px; border-right: 2px solid var(--e-muted); border-bottom: 2px solid var(--e-muted); rotate: -45deg; transition: rotate var(--dur-fast) var(--ease-out); }
.m-more[open] > summary::before { rotate: 45deg; }
.m-more > summary > .m-head { flex: 1; }
.m-more[open] > summary { margin-bottom: var(--sp-3); }
/* thumb-reach action bar, just above the tab bar (02-ia §7.9) */
.m-dock { position: sticky; bottom: calc(59px + env(safe-area-inset-bottom)); z-index: var(--z-sticky); display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--sp-2); margin: 0 calc(-1 * var(--sp-4)); padding: var(--sp-3) var(--sp-4); background: var(--e-raised); border-top: 1px solid var(--e-border); box-shadow: 0 -6px 16px -12px color-mix(in srgb, var(--e-fg) 40%, transparent); }
/* record hero */
.m-hero > .card__body { display: grid; gap: var(--sp-2); }
.m-hero__name { margin: 0; font-family: var(--font-display); font-size: var(--fs-2xl); font-weight: var(--display-weight); line-height: 1.15; letter-spacing: -0.01em; }
.m-hero .alert { margin-top: var(--sp-1); }
/* rows: follow-ups, recent calls */
.m-rows { margin: 0; padding: 0; list-style: none; }
.m-rows > li { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr) auto; align-items: center; gap: 2px var(--sp-3); padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--e-border); }
.m-rows > li:first-child { border-top: 0; }
.m-when { display: grid; gap: 2px; margin: 0; line-height: 1.2; font-variant-numeric: tabular-nums; }
.m-when strong { font-size: var(--fs-md); }
.m-when small { color: var(--e-muted); font-size: var(--fs-xs); font-weight: var(--fw-medium); }
.m-rows > .is-overdue .m-when, .m-rows > .is-overdue .m-when small { color: var(--e-err-fg); }
.m-what { display: grid; gap: 2px; min-width: 0; }
.m-what > p { margin: 0; }
.m-what a { color: var(--e-fg); font-weight: var(--fw-semibold); text-decoration: none; }
.m-what a:hover { color: var(--e-primary-dark); text-decoration: underline; }
.m-what .code { margin-left: 2px; font-size: var(--fs-xs); }
/* a row that opens its lead: the name link covers the whole row (stretched link); Call stays its own button */
.m-rows--link > li { position: relative; min-height: 44px; }
.m-rows--link .m-what a::after { content: ""; position: absolute; inset: 0; }
.m-rows--link .m-what a:focus-visible { outline: none; }
.m-rows--link > li:has(.m-what a:focus-visible) { outline: 2px solid var(--e-focus); outline-offset: -2px; }
.m-rows--link > li:hover { background: var(--e-subtle); }
.m-rows--link > li > .btn { position: relative; z-index: 1; }
/* PIN lock and dialer keypads */
.m-pin { display: grid; justify-items: center; gap: var(--sp-3); text-align: center; }
.m-pin h2 { margin: 0; font-size: var(--fs-lg); }
.m-pin p { margin: 0; }
.m-pin > .field { justify-items: center; }
.m-pin__input { width: 12rem; height: 56px; padding: 0; font-family: var(--font-mono); font-size: 1.75rem; letter-spacing: 0.6em; text-align: center; text-indent: 0.6em; }
.m-keypad { display: grid; grid-template-columns: repeat(3, 76px); justify-content: center; gap: var(--sp-2) var(--sp-5); }
.m-key { display: grid; place-content: center; justify-items: center; height: 56px; padding: 0; border: 1px solid var(--e-border); border-radius: var(--r-lg); background: var(--e-raised); color: var(--e-fg); font-family: var(--font-display); font-size: 1.5rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; cursor: pointer; }
.m-key small { margin-top: 3px; color: var(--e-muted); font-family: var(--font-ui); font-size: 0.625rem; font-weight: var(--fw-semibold); letter-spacing: 0.08em; }
.m-key:hover { background: var(--e-surface); }
.m-key:focus-visible { outline: 2px solid var(--e-focus); outline-offset: 2px; }
.m-key--fn { border-color: transparent; background: transparent; color: var(--e-muted); }
.m-key--fn .icon { width: 24px; height: 24px; }
.m-dial__no { height: 56px; font-family: var(--font-mono); font-size: 1.375rem; letter-spacing: 0.04em; }
.m-dial__go { display: grid; grid-template-columns: 76px 1fr 76px; gap: var(--sp-5); justify-content: center; width: min(100%, 268px); margin-inline: auto; }
.m-dial__go > .btn--primary { grid-column: 2; }
/* big choice tiles (call disposition): two taps to log a call (01-research §2.5) */
.m-choice { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); min-width: 0; margin: 0; padding: 0; border: 0; }
.m-choice > legend { margin-bottom: var(--sp-2); padding: 0; font-weight: var(--fw-semibold); }
.m-tile { position: relative; display: flex; align-items: center; gap: var(--sp-2); min-height: 52px; padding: var(--sp-2) var(--sp-3); border: 1px solid var(--e-border-strong); border-radius: var(--r-md); background: var(--e-raised); color: var(--e-fg); font-weight: var(--fw-semibold); line-height: 1.25; cursor: pointer; }
.m-tile > input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.m-tile > .icon { flex: none; color: var(--e-muted); }
.m-tile::after { content: ""; flex: none; width: 18px; height: 18px; margin-left: auto; border: 2px solid var(--e-border-strong); border-radius: 50%; box-sizing: border-box; }
.m-tile:has(> input:checked) { border-color: var(--e-primary); background: var(--e-primary-soft); color: var(--e-primary-dark); box-shadow: inset 0 0 0 1px var(--e-primary); }
.m-tile:has(> input:checked) > .icon { color: var(--e-primary-dark); }
.m-tile:has(> input:checked)::after { border: 6px solid var(--e-primary); background: var(--e-raised); }
.m-tile:has(> input:focus-visible) { outline: 2px solid var(--e-focus); outline-offset: 2px; }
.m-tile--wide { grid-column: 1 / -1; }
/* parts that follow the call outcome, one of five values (no script): wrappers shown by :has().
   A single on/off choice uses the shared .toggle-scope; this is its multi-value form. */
.m-outcome { display: none; }
.m-form:not(:has(input[name="disposition"]:checked)) .m-outcome--none,
.m-form:has(input[name="disposition"]:is([value="Connected"], [value="Invalid Number"], [value="Follow-up Scheduled"]):checked) .m-outcome--closes,
.m-form:has(input[name="disposition"]:is([value="No Answer"], [value="Busy"]):checked) .m-outcome--retry,
.m-form:has(input[name="disposition"][value="Follow-up Scheduled"]:checked) .m-outcome--fu { display: grid; gap: var(--sp-3); }
.m-outcome > .m-line { display: flex; align-items: flex-start; gap: 6px; }
.m-outcome > .m-line > .icon { flex: none; width: 16px; height: 16px; margin-top: 1px; }
.m-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
/* quotation-request lines on Convert */
.m-qline { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2) var(--sp-3); padding-bottom: var(--sp-4); border-bottom: 1px solid var(--e-border); }
.m-qline > .m-qline__item, .m-qline > .m-qline__sum, .m-qline > .m-qline__hint { grid-column: 1 / -1; }
.m-qline__hint { margin: -4px 0 0; color: var(--e-muted); font-size: var(--fs-sm); }
.m-qline__sum { display: flex; justify-content: space-between; gap: var(--sp-2); margin: 0; color: var(--e-muted); font-size: var(--fs-sm); }
.m-qline__sum strong { color: var(--e-fg); font-variant-numeric: tabular-nums; }
.m-readonly { display: flex; align-items: center; min-height: 44px; margin: 0; font-variant-numeric: tabular-nums; }
.m-dock--one { grid-auto-flow: row; }
/* the Discard confirmation (native popover): its two buttons share the row */
.m-main .m-confirm .dialog__foot > .btn { flex: 1; }

/* Days picker — promoted from G12 (Extend validity on the Head Admin company list
   and company page). A .segmented radiogroup of input[name=days] with the fixed
   values 7 | 15 | 30 | 90 | custom; each preview part carries data-days="<value>"
   and shows only while its value is checked (no script). "custom" also opens
   .days-picker__custom (the number field). New values are added here, never
   page-locally:  <dialog class="dialog days-picker"> … <div class="segmented">
   …</div><div class="field days-picker__custom">…</div> <dl class="dl
   days-picker__preview"><dd data-days="7">…</dd>…</dl> </dialog>          */
.days-picker .days-picker__custom,
.days-picker [data-days] { display: none; }
.days-picker:has(input[value="7"]:checked) [data-days="7"],
.days-picker:has(input[value="15"]:checked) [data-days="15"],
.days-picker:has(input[value="30"]:checked) [data-days="30"],
.days-picker:has(input[value="90"]:checked) [data-days="90"],
.days-picker:has(input[value="custom"]:checked) [data-days="custom"] { display: block; }
.days-picker:has(input[value="custom"]:checked) .days-picker__custom { display: grid; margin-top: var(--sp-3); }
.days-picker .segmented { width: max-content; max-width: 100%; }

/* Yes / no matrix cell — promoted from G12 (plan features). <td class="yn"> holds
   a check icon (with visually hidden "Included") or a muted dash ("Not
   included"); centred, the icon in ok-fg. */
.table .yn { text-align: center; }
.table .yn .icon { color: var(--e-ok-fg); }
