/* ── App overrides — deliberately UNLAYERED ─────────────────────────────────
   styles.css declares @layer tokens…overrides; unlayered rules here win over
   every system layer by cascade design, so !important is never needed.
   Rules: variant classes only (.aj-*), tokens only, never redefine a system
   class, logical properties only (RTL). */

/* Wider rail — 212px cramped the longer nav labels ("Users & Roles",
   "Follow-ups" + badge). 248px is ~17% more. */
:root { --sidebar-w: 248px; }

/* ── Chart palette ────────────────────────────────────────────────────────
   The DS status tones are pill colours, not a chart palette: as adjacent
   segments its red↔green pair measures ΔE 1.4 under protanopia — invisible
   to red-green colourblind readers. These four slots are validated for both
   modes (lightness band, chroma floor, CVD separation, contrast). Assign in
   fixed order; never cycle. */
:root {
  --viz-1: #2a78d6;  /* blue   */
  --viz-2: #eb6834;  /* orange */
  --viz-3: #1baf7a;  /* aqua   */
  --viz-4: #eda100;  /* yellow */
  --viz-grid: var(--md-outline-variant);
}
:root[data-theme-mode="dark"] {
  --viz-1: #3987e5;
  --viz-2: #d95926;
  --viz-3: #199e70;
  --viz-4: #c98500;
}

.aj-kpi-grid {
  display: grid;
  gap: var(--gap-card);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

/* Softly tinted KPI cards — the "premium, easy on the eye" ask. The tint is
   mixed from the chart slot into the card surface, so it stays a whisper in
   light mode and doesn't glow in dark mode. */
.aj-kpi-tint-1 { background: color-mix(in srgb, var(--viz-1) 7%, var(--md-surface)); }
.aj-kpi-tint-2 { background: color-mix(in srgb, var(--viz-2) 7%, var(--md-surface)); }
.aj-kpi-tint-3 { background: color-mix(in srgb, var(--viz-3) 7%, var(--md-surface)); }
.aj-kpi-tint-4 { background: color-mix(in srgb, var(--viz-4) 7%, var(--md-surface)); }
.aj-kpi-dot {
  inline-size: 8px; block-size: 8px; border-radius: 999px;
  display: inline-block; margin-inline-end: 7px; vertical-align: middle;
}

/* Chart chrome */
.aj-chart-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  justify-content: space-between;
}
.aj-legend { display: flex; flex-direction: column; gap: 8px; min-inline-size: 150px; }
.aj-legend-row {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-body-sm);
}
.aj-legend-sw { inline-size: 10px; block-size: 10px; border-radius: 3px; flex: none; }
.aj-legend-val { margin-inline-start: auto; font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); }
.aj-donut-wrap { position: relative; display: grid; place-items: center; }
.aj-donut-center {
  position: absolute; text-align: center; line-height: 1.2;
}
.aj-donut-center b { display: block; font-size: 22px; font-variant-numeric: tabular-nums; }
.aj-donut-center span { font-size: var(--fs-caption); color: var(--text-muted); }
.aj-bars { display: flex; align-items: flex-end; gap: 6px; block-size: 168px; }
.aj-bar-col {
  flex: 1; min-inline-size: 22px; display: flex; flex-direction: column;
  align-items: center; gap: 5px; cursor: default;
}
/* many buckets: let them shrink so the row always fits its card. The strided
   axis labels still set a min-content floor, so the container scrolls in its
   own box rather than pushing the page — the DS rule for wide content. */
.aj-bars.dense { gap: 3px; padding-block-end: 18px; }
.aj-bars.dense .aj-bar-col { min-inline-size: 0; position: relative; }
.aj-bars.dense .aj-bar-v { display: none; }
/* Axis labels are wider than a dense column, so they must not take part in
   layout — otherwise each one pushes its neighbours and they collide. */
.aj-bars.dense .aj-bar-x {
  position: absolute; inset-block-end: -16px; inset-inline-start: 50%;
  transform: translateX(-50%); font-size: 10px;
}
.aj-bar-track { inline-size: 100%; block-size: 130px; display: flex; align-items: flex-end; }
.aj-bar {
  inline-size: 100%; background: var(--viz-1);
  border-radius: 4px 4px 0 0;   /* 4px rounded data-end, anchored to baseline */
  transition: filter var(--dur);
  min-block-size: 2px;
}
.aj-bar-col:hover .aj-bar { filter: brightness(1.12); }
.aj-bar-x { font-size: 10.5px; color: var(--text-muted); white-space: nowrap; }
.aj-bar-v { font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* Phones: KPIs sit two-up like the offline desk. One-per-row pushed the
   actual content (My Day, tables) below the fold on a 390px screen. */
@media (max-width: 599px) {
  .aj-kpi-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .aj-kpi-grid .card-pad { padding: 12px; }
  .aj-kpi-value { font-size: 20px; }
  .aj-kpi-label { font-size: var(--fs-micro); }
}

/* Row-of-facts pattern (My Day, follow-up rows, order rows): on a phone these
   read as a stacked block instead of squeezing 3–4 columns into 390px. */
@media (max-width: 599px) {
  .aj-row-stack { flex-wrap: wrap; row-gap: 4px; }
  .aj-row-stack > .aj-row-grow { flex-basis: 100%; }
}

/* KPI typography as one rule instead of repeated inline styles, so every
   KPI card on every page reads identically. */
.aj-kpi-label {
  font-size: var(--fs-caption);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.aj-kpi-value {
  font-size: 24px;
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  margin-block-start: 4px;
}

/* ── Form controls ────────────────────────────────────────────────────────
   The DS styles inputs/selects only inside `.field`. Every select we render
   outside one (topbar switcher, per-row selects in tables, inline filters)
   was falling back to the raw browser control — measured on 16/16 pages.
   Give them the exact `.field select` treatment, including the chevron and
   its RTL mirror. DS composites that own their look are exempted. */
select:not(.filter-select):not([class*="dt-"]):not(.seg-btn) {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  min-height: 36px;
  padding: 8px 36px 8px 12px;
  border: 1px solid var(--md-outline);
  border-radius: var(--r-sm);
  background-color: var(--md-sc-lowest);
  color: var(--md-on-surface);
  font: inherit;
  font-size: var(--fs-body-sm);
  line-height: 1.3;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%2345464f' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  transition: border-color var(--dur);
}
/* Same story for text inputs and textareas: the DS styles them only inside
   `.field`, so any input in a table cell or an inline row rendered as a raw
   browser box — measured 15 of them on the product form alone, which is the
   "black boxes of different sizes" look. */
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]),
textarea {
  padding: 8px 12px;
  min-block-size: 36px;
  border: 1px solid var(--md-outline);
  border-radius: var(--r-sm);
  background: var(--md-sc-lowest);
  color: var(--md-on-surface);
  font: inherit;
  font-size: var(--fs-body-sm);
  line-height: 1.3;
  transition: border-color var(--dur);
}
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])::placeholder,
textarea::placeholder { color: var(--md-on-surface-variant); opacity: .7; }
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):focus-visible,
textarea:focus-visible {
  outline: none;
  box-shadow: 0 0 0 var(--ring-w) var(--ring);
  border-color: var(--md-primary);
}
/* inputs inside a data table shouldn't fight the row height */
.dtable input { min-block-size: 34px; inline-size: 100%; }

select:not(.filter-select):hover { border-color: var(--md-outline); }
select:not(.filter-select):focus-visible {
  outline: none;
  box-shadow: 0 0 0 var(--ring-w) var(--ring);
  border-color: var(--md-primary);
}
html[dir="rtl"] select:not(.filter-select) {
  padding: 8px 12px 8px 36px;
  background-position: left 10px center;
}

/* A checkbox inside `.field` inherited `width:100%; min-height:36px; border`
   from the DS text-input rule and rendered as a 189×36 bordered slab.
   `accent-color` fixed the size but left the OS's own square, which reads as
   unstyled next to the rounded inputs beside it — so the box is drawn here:
   same border, radius and focus ring as every other control on the page. */
input[type="checkbox"], input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  inline-size: 18px;
  block-size: 18px;
  min-block-size: 0;
  padding: 0;
  margin: 0;
  flex: 0 0 auto;
  display: inline-grid;
  place-content: center;
  border: 1.5px solid var(--md-outline);
  border-radius: var(--r-xs);
  background: var(--md-sc-lowest);
  cursor: pointer;
  transition: background var(--dur), border-color var(--dur);
}
input[type="radio"] { border-radius: var(--r-full); }
input[type="checkbox"]::after, input[type="radio"]::after {
  content: "";
  inline-size: 10px;
  block-size: 10px;
  transform: scale(0);
  transition: transform var(--dur) var(--ease);
  background: var(--md-on-primary);
  /* a drawn tick, so no glyph font has to cooperate */
  clip-path: polygon(13% 47%, 6% 60%, 39% 88%, 94% 22%, 82% 12%, 37% 66%);
}
input[type="radio"]::after { clip-path: none; border-radius: var(--r-full); inline-size: 7px; block-size: 7px; }
input[type="checkbox"]:checked, input[type="radio"]:checked {
  background: var(--md-primary);
  border-color: var(--md-primary);
}
input[type="checkbox"]:checked::after, input[type="radio"]:checked::after { transform: scale(1); }
input[type="checkbox"]:hover:not(:checked), input[type="radio"]:hover:not(:checked) {
  border-color: var(--md-on-surface-variant);
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 var(--ring-w) var(--ring);
}
input[type="checkbox"]:disabled, input[type="radio"]:disabled { opacity: .5; cursor: default; }
.field input[type="checkbox"], .field input[type="radio"] { inline-size: 18px; }
/* A checkbox's tap target is its label. Opt-in class, not `label:has(...)` —
   :has() is off-limits here (this ships to phone WebViews). */
.aj-check {
  display: flex; align-items: center; gap: 9px;
  min-block-size: 32px; cursor: pointer;
  font-size: var(--fs-body-sm);
}
/* The filter row above a list. `.field` carries a bottom margin for stacked
   forms; in a centred flex row that margin shifts the input's centre up by
   half of it, so the search box sat 7px above the chip beside it — the
   "uneven" look. One class, margin cleared, everything on one baseline. */
.aj-filters {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-block-end: var(--gap-card);
}
.aj-filters .field { margin-block-end: 0; }
.aj-filters .field input, .aj-filters .field select { min-block-size: 36px; }

/* Filter variant: a bare tick floating beside a 36px search box looked
   unfinished. As a chip it matches the field's height, border and radius, so
   the filter row reads as one row of controls. */
.aj-check-chip {
  min-block-size: 36px;
  padding-inline: 12px;
  border: 1px solid var(--md-outline);
  border-radius: var(--r-full);
  background: var(--md-sc-lowest);
  white-space: nowrap;
  transition: background var(--dur), border-color var(--dur);
}
.aj-check-chip:hover { border-color: var(--md-on-surface-variant); }
.aj-check-chip.on {
  border-color: var(--md-primary);
  background: color-mix(in srgb, var(--md-primary) 8%, var(--md-surface));
  color: var(--md-primary);
  font-weight: var(--fw-semibold);
}

/* ── Mobile app shell ─────────────────────────────────────────────────────
   suite.css declares `.bottom-nav{display:flex}` with NO media guard, so it
   would sit on top of desktop pages. Base rule first (the trap this project
   has hit before), then reveal it under the app's 900px breakpoint. */
.bottom-nav, .aj-more { display: none; }

/* ── breadcrumb trail ─────────────────────────────────────────────────────
   Sits above the page heading on any page nested below another. Quiet by
   design: it is orientation, not a call to action, so it must not compete with
   the h2 underneath it. The current page is plain text — a link to the page you
   are already on is a dead end that costs a click to discover. */
.aj-crumbs { margin-block-end: 10px; }
.aj-crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-body-sm);
}
.aj-crumbs a { color: var(--md-primary); text-decoration: none; }
.aj-crumbs a:hover { text-decoration: underline; text-underline-offset: 2px; }
.aj-crumbs a:focus-visible { outline: 2px solid var(--md-primary); outline-offset: 2px; border-radius: 3px; }
.aj-crumbs [aria-current="page"] { color: var(--md-on-surface-variant); }
.aj-crumbs-sep { color: var(--md-outline-variant); user-select: none; }
/* Comfortable to hit with a thumb without making the trail shout. */
@media (pointer: coarse) {
  .aj-crumbs ol { gap: 8px; }
  .aj-crumbs a { padding-block: 6px; }
}

/* ── inline checkbox + its text ───────────────────────────────────────────
   `.inline-check` is used in 28 places across the leads list, the lead form and
   the lead settings tabs — and its ONLY definition lived in doc.css, which is
   loaded on the document preview page and nowhere else. So on every app page the
   label was completely unstyled: no flex, no gap, the box butting straight
   against its text ("[x]Card [x]List [x]Filter"). That is the jammed look
   reported on the lead settings tabs; it was never a spacing value that needed
   tuning, the rule simply was not there.

   Base rule only. The pages that need a bigger touch target, or the centred
   box-only variant used in table cells, already override this with higher
   specificity and continue to win. */
.inline-check {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 500;
  color: var(--md-on-surface);
  cursor: pointer;
}
.inline-check input { flex: 0 0 auto; margin: 0; }

/* ── "there is no such name" tick ─────────────────────────────────────────
   Sits under the name input it belongs to, on the buyer and lead forms. Its own
   row with real height, because ticks jammed against the field above them is a
   complaint this app has already had. Normal weight: it is an answer to a
   question, not a field label. */
.aj-nonecheck {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-block-start: 6px;
  min-block-size: 30px;
  font-weight: 400;
  font-size: var(--fs-body-sm);
  color: var(--md-on-surface-variant);
}
.aj-nonecheck input { flex: 0 0 auto; inline-size: 16px; block-size: 16px; margin: 0; }
@media (pointer: coarse) { .aj-nonecheck { min-block-size: 40px; } }
/* A ticked name field is emptied AND disabled. Without this it looks exactly
   like a field somebody simply has not filled in yet — the difference is the
   whole point of asking, so it has to be visible and not only in the DOM.

   --md-sc-high, not --md-surface-variant: that token does not exist in this
   project. An undefined var() with no fallback is invalid at computed-value
   time, so the whole declaration is dropped and the field stayed white. I wrote
   it that way first and the screenshot caught it.

   !important is not laziness here, it is arithmetic. The global input rule below
   —
     input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=color])
          :not([type=file]):not([type=submit]):not([type=button]), textarea
   — carries EIGHT :not() attribute selectors, so its specificity is 0-8-1. This
   selector is 0-3-1 and loses on specificity, not on source order, so moving it
   later in the file changes nothing. Chrome's matched-rule list was what finally
   said so; CSSOM enumeration from page script reports nothing for a `background:`
   shorthand holding a var().

   Worth knowing: that same global rule means EVERY disabled input in this app
   still paints as enabled. Left alone deliberately — it is shared by every page
   and is not this change's to alter. */
.field input[data-none-for]:disabled {
  background: var(--md-sc-high) !important;
  color: var(--md-on-surface-variant);
  cursor: not-allowed;
}

/* ── Invoice editor save bar ──────────────────────────────────────────────
   This was an inline style on the element, and an inline style beats a
   stylesheet rule at any specificity — including the phone override 20 lines
   below, whose whole job is to lift the bar above the bottom nav. So `bottom:0`
   was in force at every width: at 390px the bar overlapped the nav by 57px and
   a tap on the centre of "Save & Preview" landed on a nav item instead of the
   button. Measured, not guessed. Base rule FIRST, phone override after — the
   cascade order this project keeps re-learning.

   padding-inline is 17px rather than 16px on purpose: a card is 16px padded
   plus a 1px border, so 17px lands the primary button's right edge exactly on
   the right edge of the fields above it. The old inline value had NO horizontal
   padding, so the button hung 17px further out, flush with the card's edge,
   which is what made it read as sitting on the band rather than inside it.

   The background is not decoration — the bar is sticky, and it must stay opaque
   so the form scrolling underneath is masked instead of showing through the
   buttons. It used to be the page canvas colour with a single top rule, which
   mid-scroll read as a grey seam laid across a white card. It is now a floating
   toolbar: surface fill, a 1px border on all four sides, rounded, lifted clear
   of the viewport edge — so it reads as a deliberate object rather than a band.
   Depth is border + tint, never a shadow, per the design system.

   padding-inline is 16px, matching .card-pad exactly: the bar's own 1px border
   supplies the remaining pixel, so the primary button's right edge lands on the
   right edge of the fields above it (17px in from the card's outer edge). */
.aj-savebar {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: flex-end;
  flex-wrap: wrap;
  position: sticky;
  /* Lifted, so the rounded bottom corners have something to sit against and the
     bar reads as floating over the form rather than welded to the window. */
  bottom: 12px;
  z-index: 5;
  /* Hugs its buttons instead of spanning the column. A full-width surface-filled
     bar over a white card read as a HOLE cut out of the card — the border was
     white-on-white and invisible, so there was no object, just a gap with
     buttons in it. Shrink-wrapped and pushed to the end, it is unmistakably a
     floating toolbar, and content scrolling past to its left is expected. */
  inline-size: fit-content;
  max-inline-size: 100%;
  margin-block: var(--gap-card);
  margin-inline-start: auto;
  padding: 12px 16px;
  background: var(--md-surface);
  border: 1px solid var(--md-outline-variant);
  border-radius: var(--r-lg, 14px);
}

@media (max-width: 899px) {
  .bottom-nav { display: flex; }
  .app-scroll { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }
  /* The save bar goes back to being a STRIP on a phone. The desktop treatment is
     a shrink-wrapped floating toolbar, but at 390px the buttons already fill the
     width, so a surface-filled pill loses any sense of floating and reads as a
     hole in the card behind it — and it wrapped onto two rows, eating a third of
     the little vertical space there is. Full width, one row, page-canvas fill so
     it stays legible against the white cards it overlays.

     bottom is 57px, not 56: the nav is border-box 56px of content plus a 1px top
     border, so at 56px the bar's bottom edge landed on that border line. No
     extra float gap here — the strip sits directly on the nav. Measured. */
  .aj-savebar {
    bottom: calc(57px + env(safe-area-inset-bottom));
    inline-size: auto;
    max-inline-size: none;
    margin-inline-start: 0;
    flex-wrap: nowrap;
    padding: 12px 17px;
    background: var(--color-canvas);
    border: 0;
    border-block-start: 1px solid var(--border);
    border-radius: 0;
  }
  .aj-more.open {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 95;
    background: var(--md-scrim, rgba(0,0,0,.45));
  }
  .aj-more-panel {
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    background: var(--md-surface);
    border-start-start-radius: var(--r-lg);
    border-start-end-radius: var(--r-lg);
    padding: 10px 10px calc(18px + env(safe-area-inset-bottom));
    max-block-size: 76vh;
    overflow-y: auto;
    animation: barUp var(--dur) var(--ease);
  }
  .aj-more-grip {
    inline-size: 38px; block-size: 4px; border-radius: 999px;
    background: var(--md-outline-variant); margin: 2px auto 10px;
  }
  .aj-more-panel a, .aj-more-panel button {
    display: flex; align-items: center; gap: 12px;
    inline-size: 100%; min-block-size: 48px; padding: 0 14px;
    border: none; background: none; font: inherit; text-align: start;
    color: var(--md-on-surface); text-decoration: none;
    border-radius: var(--r-sm); cursor: pointer;
  }
  .aj-more-panel a:hover, .aj-more-panel button:hover { background: var(--md-sc-high); }
  .aj-more-title {
    font-size: var(--fs-overline); letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-muted); padding: 10px 14px 4px;
  }
}

/* Topbar company switcher — a button that opens a DS menu. */
.aj-co-btn {
  display: inline-flex; align-items: center; gap: 8px;
  max-inline-size: 230px;
}
.aj-co-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: var(--fw-semibold);
}

/* Profile avatar button — a round control, not a text link. `.avatar` is a
   span in the DS, so the button form needs the reset. */
.aj-profile-btn {
  border: 1px solid transparent;
  cursor: pointer;
  font: inherit;
  font-weight: var(--fw-semibold);
  padding: 0;
  flex: none;
  transition: border-color var(--dur);
}
.aj-profile-btn:hover { border-color: var(--md-outline); }
.aj-profile-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 var(--ring-w) var(--ring);
}

/* Buttons sitting ON the inverse-surface bar (portal header) must take their
   colours from the inverse tokens — the default .btn is styled for a light
   surface and renders as dark-on-dark there. */
.aj-onbar {
  background: transparent;
  color: var(--md-inverse-on-surface);
  border-color: color-mix(in srgb, var(--md-inverse-on-surface) 45%, transparent);
}
.aj-onbar:hover {
  background: color-mix(in srgb, var(--md-inverse-on-surface) 14%, transparent);
  color: var(--md-inverse-on-surface);
}

/* Topbar crowding on phones: company switcher + theme + language + role pill +
   user name overflow a 390px viewport (measured: 80px past the edge). Below
   the DS adaptive breakpoint, drop the two decorative items — the role is
   still on Users & Roles, the name is still on the password page — and let the
   switcher shrink. Nothing functional is removed. */
@media (max-width: 720px) {
  /* the avatar stays — it is the account menu; the company name shortens */
  .topbar .actions .pill-status { display: none; }
  .aj-co-btn { max-inline-size: 150px; }
}

/* ── Settings & form layout patterns ─────────────────────────────────────
   One shape for every "list + add" screen, instead of each tab inventing its
   own row of mismatched controls. */
.aj-help {
  color: var(--text-muted);
  font-size: var(--fs-body-sm);
  margin: 0 0 4px;
  max-inline-size: 70ch;
}
/* an input + its confirm button, sized as one unit inside a table cell */
.aj-inline-edit { display: flex; gap: 8px; align-items: center; }
.aj-inline-edit input { flex: 1; min-inline-size: 0; }
/* the "add a thing" bar: fields grow, the primary action sits at the end and
   aligns with the field boxes, not with their labels */
.aj-addbar {
  display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap;
}
.aj-addbar .field { flex: 1; min-inline-size: 200px; margin-bottom: 0; }
.aj-addbar .btn { min-block-size: 36px; }
@media (max-width: 599px) {
  .aj-addbar { align-items: stretch; }
  .aj-addbar .btn { inline-size: 100%; }
}

/* ── Expandable list item ────────────────────────────────────────────────
   A scannable row that opens into its editor, instead of stacking every
   record's full form down the page. */
.aj-item { border-block-start: 1px solid var(--border); }
.aj-item:first-of-type { border-block-start: none; }
.aj-item-summary {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; cursor: pointer; list-style: none;
  min-block-size: 56px;
}
.aj-item-summary::-webkit-details-marker { display: none; }
.aj-item-summary:hover { background: var(--md-sc-high); }
.aj-item-title { font-weight: var(--fw-semibold); white-space: nowrap; }
.aj-item-preview {
  flex: 1; min-inline-size: 0;
  color: var(--text-muted); font-size: var(--fs-body-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aj-item-action {
  color: var(--md-primary); font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
}
.aj-item-chev { transition: transform var(--dur); flex: none; color: var(--text-muted); }
.aj-item[open] > .aj-item-summary .aj-item-chev { transform: rotate(180deg); }
.aj-item[open] > .aj-item-summary { background: var(--md-sc-high); }
.aj-item-body { padding: 4px 16px 18px; }
@media (max-width: 599px) {
  .aj-item-preview, .aj-item-action { display: none; }
}

/* One action row for every form: primary first, destructive last, real gap,
   right-aligned, separated from the fields above. */
.aj-form-actions {
  display: flex; gap: 10px; justify-content: flex-end; align-items: center;
  flex-wrap: wrap;
  margin-block-start: 16px; padding-block-start: 14px;
  border-block-start: 1px solid var(--border);
}
.aj-form-actions .btn { min-block-size: 36px; }

/* State on list rows: the status pill in the row already names it, so there
   is no coloured edge stripe here. (There was one — a 3px tinted bar on the
   first cell. Removed on purpose: an accent rail down the side of a row or
   card is the tell of a template, and it was repeating information the row
   already carried in words. Don't reintroduce it.) The attribute stays for
   the tests and for future non-colour affordances. */

/* ── Tables ──────────────────────────────────────────────────────────────
   `.table-wrap` carries its own 1px border AND radius, and `.card` carries
   both too — so a table inside a card drew two rounded boxes, one inside the
   other, with the header's rounded blue band pinched between them. Inside a
   card the card is the container; the wrap is only a scroll box. */
.card .table-wrap, .aj-panel .table-wrap {
  border: none;
  border-radius: 0;
}
/* The DS header is a filled band closed by a 2px brand rule. At the top of
   every table on every page that reads as a branded stripe rather than a
   column heading. Same band, quieter: one hairline, in the table's own ink. */
.dtable th {
  border-bottom: 1px solid var(--md-outline-variant);
  background: var(--md-sc-high);
  color: var(--text-muted);
}

/* ── Section panel ───────────────────────────────────────────────────────
   Every block on a record page says what it is: an icon, a name, and one line
   of what it's for. Identical white cards with bold text made a page read as
   one undifferentiated surface — you had to read the contents to know which
   section you were in. */
.aj-panel {
  background: var(--md-sc-lowest);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  margin-block-end: var(--gap-card);
}
.aj-panel-head {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 16px;
  background: var(--md-sc-high);
  border-block-end: 1px solid var(--border);
}
.aj-panel-ic {
  inline-size: 34px; block-size: 34px; flex: none;
  display: grid; place-content: center;
  border-radius: var(--r-sm);
  background: var(--md-sc-lowest);
  border: 1px solid var(--border);
  color: var(--md-primary);
}
.aj-panel-titles { min-inline-size: 0; }
.aj-panel-title {
  font-weight: var(--fw-semibold);
  font-size: var(--fs-body);
  line-height: 1.25;
}
.aj-panel-hint {
  color: var(--text-muted);
  font-size: var(--fs-caption);
  margin-block-start: 1px;
}
.aj-panel-meta {
  margin-inline-start: auto; flex: none;
  color: var(--text-muted); font-size: var(--fs-caption);
}
.aj-panel-body { padding: 16px; }
.aj-panel-body.flush { padding: 0; }
.aj-panel-body > :first-child { margin-block-start: 0; }
.aj-panel-body > :last-child { margin-block-end: 0; }

/* A block within a panel — related things stay in one panel instead of
   becoming three floating cards down a column. */
.aj-sub + .aj-sub {
  margin-block-start: 18px;
  padding-block-start: 16px;
  border-block-start: 1px solid var(--border);
}
.aj-sub-title {
  font-size: var(--fs-micro); letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-muted); margin-block-end: 9px;
}

/* ── Order checklist ─────────────────────────────────────────────────────
   A numbered step badge instead of ①②③ glyphs: those sat beside real counts
   and read as a second number, and no font renders them consistently. */
.aj-step { font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.aj-step-done {
  background: color-mix(in srgb, var(--viz-3) 16%, var(--md-surface));
  border-color: color-mix(in srgb, var(--viz-3) 40%, transparent);
  color: var(--viz-3);
}
.aj-step-part {
  background: color-mix(in srgb, var(--viz-4) 18%, var(--md-surface));
  border-color: color-mix(in srgb, var(--viz-4) 45%, transparent);
  color: var(--md-on-surface);
}
.aj-order-top { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.aj-order-pct {
  display: block; font-size: 28px; font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.aj-order-meter { flex: 1; min-inline-size: 200px; }
.aj-order-meter .aj-meter { block-size: 8px; }

.aj-task {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px;
  transition: background var(--dur);
}
.aj-task + .aj-task { border-block-start: 1px solid var(--border); }
.aj-task:hover { background: var(--md-sc-high); }
.aj-task-tick { display: flex; flex: none; }
.aj-tick {
  inline-size: 22px; block-size: 22px; flex: none; padding: 0;
  display: inline-grid; place-content: center;
  border: 1.5px solid var(--md-outline); border-radius: var(--r-full);
  background: var(--md-sc-lowest); color: transparent; cursor: pointer;
  transition: background var(--dur), border-color var(--dur);
}
.aj-tick:hover { border-color: var(--md-on-surface-variant); }
.aj-tick:focus-visible { outline: none; box-shadow: 0 0 0 var(--ring-w) var(--ring); }
.aj-tick.on {
  background: var(--viz-3); border-color: var(--viz-3); color: var(--md-on-primary);
}
.aj-task-title { flex: 1; min-inline-size: 0; font-size: var(--fs-body-sm); }
.aj-task.is-done .aj-task-title { text-decoration: line-through; color: var(--text-muted); }
.aj-task-date {
  color: var(--text-muted); font-size: var(--fs-caption);
  font-variant-numeric: tabular-nums; white-space: nowrap; flex: none;
}
.aj-task-who { flex: none; }
.aj-task-who select { min-inline-size: 150px; }
.aj-inline-form { display: inline-flex; gap: 6px; align-items: center; }
@media (max-width: 720px) {
  .aj-task { flex-wrap: wrap; row-gap: 8px; }
  .aj-task-title { flex-basis: calc(100% - 34px); }
  .aj-task-who select { min-inline-size: 130px; }
}

/* A labelled statistic row: name · proportion · the figure WITH its unit.
   Bare counts down a column ("3", "1", "7") make the reader infer what is
   being counted from a heading several lines up. */
.aj-stat-row {
  display: flex; align-items: center; gap: 12px;
  padding-block: 9px;
}
.aj-stat-row + .aj-stat-row { border-block-start: 1px solid var(--border); }
.aj-stat-label { min-inline-size: 130px; font-size: var(--fs-body-sm); }
.aj-stat-bar {
  flex: 1; min-inline-size: 40px;
  block-size: 8px; border-radius: var(--r-full);
  background: var(--md-sc-high); overflow: hidden;
}
.aj-stat-bar > span {
  display: block; block-size: 100%;
  background: var(--viz-1); border-radius: var(--r-full);
}
.aj-stat-n {
  font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold);
  font-size: var(--fs-body-sm); white-space: nowrap;
}
.aj-stat-sub {
  color: var(--text-muted); font-size: var(--fs-caption);
  min-inline-size: 84px; text-align: end; white-space: nowrap;
}
@media (max-width: 599px) {
  .aj-stat-row { flex-wrap: wrap; }
  .aj-stat-bar { order: 3; flex-basis: 100%; }
}

/* Retention buttons: the count IS the label, so you know what you are
   deleting before you click rather than after. */
.aj-retain { display: flex; gap: 10px; flex-wrap: wrap; }
.aj-retain-btn {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-inline-size: 150px; padding: 11px 14px;
  border: 1px solid var(--md-outline); border-radius: var(--r-md);
  background: var(--md-sc-lowest); color: var(--md-on-surface);
  font: inherit; cursor: pointer; text-align: start;
  transition: background var(--dur), border-color var(--dur), color var(--dur);
}
.aj-retain-btn:hover:not(:disabled) {
  border-color: var(--md-error); color: var(--md-error);
  background: color-mix(in srgb, var(--md-error) 6%, var(--md-surface));
}
.aj-retain-btn:focus-visible { outline: none; box-shadow: 0 0 0 var(--ring-w) var(--ring); }
.aj-retain-btn:disabled { opacity: .5; cursor: default; }
.aj-retain-n { font-size: 20px; font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.aj-retain-l { font-size: var(--fs-caption); color: var(--text-muted); }
.aj-retain-btn:hover:not(:disabled) .aj-retain-l { color: inherit; }

/* Money reads as a column of figures, not prose. */
.aj-num { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.aj-num-strong { font-weight: var(--fw-semibold); }

/* ── Icon action button ──────────────────────────────────────────────────
   Repeating "WA / Email / Snooze / Done / delete" as five identical grey
   pills gave every row the same visual weight and no hierarchy. Channels
   become icons, the row's real action stays a labelled button. Every one of
   these needs title + aria-label — an icon alone has no accessible name. */
.aj-iconbtn {
  display: inline-grid; place-content: center;
  inline-size: 34px; block-size: 34px; flex: none;
  padding: 0;
  border: 1px solid var(--md-outline);
  border-radius: var(--r-sm);
  background: var(--md-sc-lowest);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--dur), color var(--dur), border-color var(--dur);
}
.aj-iconbtn:hover {
  background: var(--md-sc-high);
  color: var(--md-on-surface);
  border-color: var(--md-on-surface-variant);
}
.aj-iconbtn:focus-visible { outline: none; box-shadow: 0 0 0 var(--ring-w) var(--ring); }
.aj-iconbtn-wa:hover    { color: var(--viz-3); border-color: var(--viz-3); }
.aj-iconbtn-mail:hover  { color: var(--viz-1); border-color: var(--viz-1); }
.aj-iconbtn-danger:hover { color: var(--md-error); border-color: var(--md-error); }

/* ── Agenda row (follow-ups page, lead workspace) ────────────────────────
   Date · who/what · actions. NOTE: no coloured edge rail here, and none
   anywhere else in this app — the tinted-left-border card is a house style
   we've deliberately dropped. Urgency is carried by the date pill's tone and
   the section it sits in, both of which also say it in words. */
.aj-agenda { display: flex; flex-direction: column; }
.aj-fu {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 2px 16px;
  padding: 12px;
  border-radius: var(--r-sm);
  transition: background var(--dur);
}
/* A row's actions sit ~700px from its name on a desktop. Without a rule
   between rows the eye loses the line on the way across, which is what made
   this page hard to read rather than the controls themselves. */
.aj-fu + .aj-fu { border-block-start: 1px solid var(--border); }
.aj-fu:hover { background: var(--md-sc-high); }
.aj-fu-date {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.aj-fu-main { min-inline-size: 0; }
.aj-fu-who {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-weight: var(--fw-semibold);
}
.aj-fu-note {
  color: var(--text-muted); font-size: var(--fs-body-sm);
  margin-block-start: 2px;
}
.aj-fu.is-done .aj-fu-who, .aj-fu.is-done .aj-fu-note {
  text-decoration: line-through; color: var(--text-muted); font-weight: 400;
}
.aj-fu-acts { display: flex; align-items: center; gap: 6px; flex: none; }
.aj-fu-acts form { display: contents; }
@media (max-width: 720px) {
  .aj-fu { grid-template-columns: auto minmax(0, 1fr); }
  .aj-fu-acts { grid-column: 1 / -1; margin-block-start: 8px; flex-wrap: wrap; }
}

/* Card headings sat ON the bottom edge of their box: the DS gives .card-head
   `padding: 16px 16px 0`, because it is meant to butt straight onto .card-pad.
   Measured, the title's centre was 8px below the head's own centre — which is
   what "the heading and the count hug the border" looks like. Once the head is
   tinted it reads as a band, so it needs real padding on both sides and a rule
   to separate it from the content. */
.card-head { padding-block-end: 13px; }
.card-head + .card-pad { padding-block-start: 14px; }
/* A rule under the heading ONLY where the head is a tinted band and plain
   content follows. Adding it to every .card-head put a line directly above
   the table header's own rule — two hairlines a few pixels apart, which is
   the "doubled border" that reads as a mistake. */
.card-head.aj-tone-bad, .card-head.aj-tone-warn,
.card-head.aj-tone-info, .card-head.aj-tone-good {
  border-block-end: 1px solid var(--border);
}

/* Tinted section head — the bucket's urgency is legible before reading it,
   and the page stops being a stack of identical white cards. */
.aj-sec { display: flex; align-items: center; gap: 10px; }
.aj-sec-count { margin-inline-start: auto; }
.card-head.aj-tone-bad  { background: color-mix(in srgb, var(--md-error) 7%, var(--md-surface)); }
.card-head.aj-tone-warn { background: color-mix(in srgb, var(--viz-4) 9%, var(--md-surface)); }
.card-head.aj-tone-info { background: color-mix(in srgb, var(--viz-1) 7%, var(--md-surface)); }
.card-head.aj-tone-good { background: color-mix(in srgb, var(--viz-3) 7%, var(--md-surface)); }

/* ── Record workspace (lead view) ────────────────────────────────────────
   Identity and its facts in one hero, actions grouped by weight at the end
   instead of five outline buttons of equal importance in a row. */
.aj-hero { margin-block-end: var(--gap-card); }
.aj-hero-top {
  display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap;
}
.aj-hero-id { min-inline-size: 0; flex: 1; }
.aj-hero-id h2 { margin: 0; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.aj-hero-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.aj-hero-sep {
  inline-size: 1px; align-self: stretch; min-block-size: 24px;
  background: var(--border); margin-inline: 2px;
}
/* Facts read as labelled pairs, not one long dot-separated sentence. */
.aj-facts {
  display: flex; flex-wrap: wrap; gap: 12px 30px;
  margin-block-start: 14px; padding-block-start: 14px;
  border-block-start: 1px solid var(--border);
}
.aj-fact-k {
  display: block;
  font-size: var(--fs-micro); letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-muted); margin-block-end: 3px;
}
.aj-fact-v { font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); }
.aj-hero-avatar { flex: none; }
/* Progress meter — the order checklist as a bar. The percentage is printed
   beside it, so the bar is never the only carrier of the number. */
.aj-meter {
  margin-block-start: 6px;
  block-size: 5px;
  inline-size: min(100%, 260px);
  border-radius: var(--r-full);
  background: var(--md-sc-high);
  overflow: hidden;
}
.aj-meter > span {
  display: block; block-size: 100%;
  background: var(--viz-1);
  border-radius: var(--r-full);
}
.aj-hero-note {
  margin-block-start: 14px; padding: 10px 12px;
  border-radius: var(--r-sm);
  background: var(--md-sc-high);
  color: var(--md-on-surface);
  white-space: pre-wrap;
  font-size: var(--fs-body-sm);
}
@media (max-width: 720px) {
  .aj-hero-acts { inline-size: 100%; }
  .aj-hero-acts .aj-hero-sep { display: none; }
}

/* A single headline figure (best quote, biggest number on a page). Emphasis
   comes from type size and a whisper of tint on the whole surface — not from
   a coloured bar down one edge. */
.aj-headline { margin-block-end: var(--gap-card); }
.aj-headline-in {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.aj-headline-sub { display: block; font-weight: var(--fw-semibold); }
.aj-headline-num {
  font-size: 26px; font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.aj-headline-unit { font-size: var(--fs-body-sm); color: var(--text-muted); font-weight: 400; }

/* Quote row (enquiry → one block per supplier). The fields and their Save
   button stay one group: without a max width the notes field ate the row and
   pushed Save to the far edge, where it read as unrelated to the form. */
.aj-quote {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 12px 14px;
  margin-block-end: 10px;
  background: var(--md-sc-lowest);
}
.aj-quote-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-block-end: 10px;
}
.aj-quote-name { font-weight: var(--fw-semibold); }
.aj-quote-form { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.aj-quote-form .field { margin: 0; }
.aj-quote-form .aj-q-notes { flex: 1; min-inline-size: 180px; max-inline-size: 420px; }
.aj-quote-form .btn { min-block-size: 36px; }

/* Composer: an input and its button as ONE unit. The `.field` wrapper
   reserves a label slot, so an input inside it and a plain button beside it
   never share a baseline — that is the misaligned "Add" button. */
.aj-composer { display: flex; gap: 8px; align-items: stretch; margin-block-end: 16px; }
.aj-composer .field { flex: 1; min-inline-size: 0; margin: 0; }
.aj-composer .btn { flex: none; min-block-size: 36px; }

/* Timeline with a rail — entries belong to one thread instead of being
   separate bordered blocks. */
.aj-tl { position: relative; margin: 0; padding: 0; list-style: none; }
.aj-tl-item {
  position: relative;
  padding: 0 0 18px 26px;
}
html[dir="rtl"] .aj-tl-item { padding: 0 26px 18px 0; }
.aj-tl-item::before {          /* the rail */
  content: ""; position: absolute; inset-block: 6px 0; inset-inline-start: 5px;
  inline-size: 2px; background: var(--border);
}
.aj-tl-item:last-child::before { inset-block-end: auto; block-size: 0; }
.aj-tl-dot {
  position: absolute; inset-inline-start: 0; inset-block-start: 4px;
  inline-size: 12px; block-size: 12px; border-radius: var(--r-full);
  border: 2px solid var(--md-surface);
  background: var(--md-outline);
}
.aj-tl-dot.t-note, .aj-tl-dot.t-system { background: var(--md-outline); }
.aj-tl-dot.t-whatsapp { background: var(--viz-3); }
.aj-tl-dot.t-email, .aj-tl-dot.t-call { background: var(--viz-1); }
.aj-tl-dot.t-stage { background: var(--viz-4); }
.aj-tl-meta {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: var(--fs-caption); color: var(--text-muted);
}
.aj-tl-type { font-weight: var(--fw-semibold); color: var(--md-on-surface); text-transform: capitalize; }
.aj-tl-text { margin-block-start: 3px; white-space: pre-wrap; }

/* ── Permission matrix ───────────────────────────────────────────────────
   Grouped by module, each grant saying what it lets someone do. A bare list
   of `invoices.status` codes is not something a business owner can audit. */
.aj-perm-group { margin-block-end: 18px; }
.aj-perm-head {
  font-size: var(--fs-micro); letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-muted);
  padding-block-end: 6px; margin-block-end: 10px;
  border-block-end: 1px solid var(--border);
}
.aj-perm-list {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.aj-perm { align-items: flex-start; gap: 10px; min-block-size: 0; }
.aj-perm input { margin-block-start: 2px; }
.aj-perm b { display: block; font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); }
.aj-perm small { color: var(--text-muted); font-size: var(--fs-caption); }
.aj-perm input:disabled + span { opacity: .62; }

/* ── Product profile & sharing ───────────────────────────────────────────*/
.aj-prod-thumb {
  inline-size: 92px; block-size: 92px; flex: none;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--md-sc-high);
}
.aj-speclist { margin: 0; padding-inline-start: 18px; display: grid; gap: 6px; }
.aj-speclist li { font-size: var(--fs-body-sm); }

/* Share targets: one row each, icon + what it does. Big enough to hit on a
   phone, and each says its outcome so nothing has to be guessed. */
.aj-share-grid { display: grid; gap: 8px; }
.aj-share {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--md-sc-lowest);
  color: var(--md-on-surface);
  text-decoration: none;
  transition: background var(--dur), border-color var(--dur);
}
.aj-share:hover { background: var(--md-sc-high); border-color: var(--md-on-surface-variant); }
.aj-share .ic { color: var(--text-muted); flex: none; }
.aj-share:hover .ic { color: var(--md-primary); }
.aj-share b { display: block; font-size: var(--fs-body-sm); }
.aj-share small { color: var(--text-muted); font-size: var(--fs-caption); }
.aj-msg-preview {
  margin: 0; padding: 12px;
  background: var(--md-sc-high);
  border-radius: var(--r-sm);
  font: inherit; font-size: var(--fs-caption);
  white-space: pre-wrap; word-break: break-word;
  max-block-size: 300px; overflow: auto;
  color: var(--md-on-surface);
}

/* ── Public product sheet (no app shell) ─────────────────────────────────*/
.aj-pub {
  max-inline-size: 760px;
  margin: 0 auto;
  padding: 24px 20px 60px;
}
.aj-pub-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-block-end: 14px; border-block-end: 1px solid var(--border);
}
.aj-pub-co { font-weight: var(--fw-semibold); font-size: var(--fs-body); }
.aj-pub-web { color: var(--text-muted); font-size: var(--fs-caption); }
.aj-pub-img {
  inline-size: 100%; max-block-size: 380px; object-fit: cover;
  border-radius: var(--r-md); border: 1px solid var(--border);
  margin-block-start: 20px;
}
.aj-pub-title { margin: 22px 0 4px; font-size: 26px; line-height: 1.2; }
.aj-pub-sub { color: var(--text-muted); font-size: var(--fs-body-sm); }
.aj-pub-desc { margin-block: 18px 0; max-inline-size: 62ch; }
.aj-pub-h2 {
  margin: 26px 0 10px; font-size: var(--fs-body);
  padding-block-end: 6px; border-block-end: 1px solid var(--border);
}
.aj-pub-foot {
  margin-block-start: 34px; padding-block-start: 16px;
  border-block-start: 1px solid var(--border);
  color: var(--text-muted); font-size: var(--fs-caption);
}
@media print {
  .no-print { display: none !important; }
  .aj-pub { max-inline-size: none; padding: 0; }
}

/* Screen-reader-only helper (DS ships one for inputs; this is the generic). */
.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
