/*
 * app.css - shared layers for the invoice_ai dashboard (ADR-0001 section 4, R54).
 * Load order: css/tokens.css, then this file, then optional css/pages/<page>.css.
 * Rules: var(--...) only - no hex, rgb(), gradients, backdrop-filter or raster url()
 * (AC-UI-07, R07, R08, R48). No inline styles are needed by anything here (ADR-0014).
 * Only transform and opacity animate; every duration is a --motion-* token (ADR-0010).
 * Visual direction: docs/design/tokens.md; motion inventory: docs/design/motion.md.
 */
@layer reset, base, components, utilities;

/* =====================================================================
 * RESET
 * ===================================================================== */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }
  body,
  h1, h2, h3, h4, h5, h6,
  p, figure, blockquote, dl, dd, ol, ul, menu {
    margin: 0;
  }
  ol[class],
  ul[class],
  menu {
    padding: 0;
    list-style: none;
  }
  img,
  svg,
  video,
  canvas {
    display: block;
    max-inline-size: 100%;
  }
  svg.icon {
    display: inline-block;
  }
  input,
  button,
  select,
  textarea {
    font: inherit;
    color: inherit;
  }
  button {
    cursor: pointer;
  }
  table {
    border-collapse: collapse;
    border-spacing: 0;
  }
  /* hidden always wins: earlier-layer !important beats every later layer. */
  [hidden]:not([hidden="until-found"]) {
    display: none !important;
  }
}

/* =====================================================================
 * BASE - faces, type, numerals, browser surfaces
 * ===================================================================== */
@layer base {
  /* Barlow Semi Condensed (Google Fonts Latin), SIL OFL 1.1, fonts/OFL-BarlowSemiCondensed.txt */
  @font-face {
    font-family: "Barlow Semi Condensed";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/barlow-semi-condensed-latin-400.woff2?v=d03a7a5f6b") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face {
    font-family: "Barlow Semi Condensed";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/barlow-semi-condensed-latin-500.woff2?v=0d7513795e") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face {
    font-family: "Barlow Semi Condensed";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("../fonts/barlow-semi-condensed-latin-600.woff2?v=f158417e92") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  /* IBM Plex Sans 1.1.0 (IBM, Latin1 split), SIL OFL 1.1, fonts/OFL-IBMPlexSans.txt */
  @font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/ibm-plex-sans-latin-400.woff2?v=b5ad7bd39f") format("woff2");
    unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02;
  }
  @font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/ibm-plex-sans-latin-500.woff2?v=b5610af04d") format("woff2");
    unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02;
  }
  @font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("../fonts/ibm-plex-sans-latin-600.woff2?v=fff0ab3a88") format("woff2");
    unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02;
  }
  /* Metric shims: local() only, no download. They are NOT font families for the ≤ 2 rule (see §12 F2). */
  @font-face {
    font-family: "IBM Plex Sans Fallback";
    font-style: normal;
    font-weight: 100 549;
    src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans"), local("Arimo");
    size-adjust: 101.52%;
    ascent-override: 100.97%;
    descent-override: 27.09%;
    line-gap-override: 0%;
  }
  @font-face {
    font-family: "IBM Plex Sans Fallback";
    font-style: normal;
    font-weight: 550 1000;
    src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"), local("LiberationSans-Bold"), local("Arimo Bold");
    size-adjust: 99.09%;
    ascent-override: 103.44%;
    descent-override: 27.75%;
    line-gap-override: 0%;
  }
  /* --- Document ------------------------------------------------------ */
  html {
    font-family: var(--font-family-sans);
    font-size: 100%;               /* never 16px: respect the user's browser font size */
    font-synthesis: style;         /* no faux bold, faux oblique allowed */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    color-scheme: light;
    background-color: var(--color-bg-canvas);
    color: var(--color-text-primary);
    accent-color: var(--color-accent);
    /* Focus is never hidden under the sticky topbar or the toast region (AC-UI-13, WCAG 2.4.11). */
    scroll-padding-block-start: calc(var(--layout-topbar-height) + var(--space-4));
    scroll-padding-block-end: calc(var(--space-16) + var(--space-8));
  }
  body {
    min-block-size: 100vh;
    font-size: var(--text-md);
    line-height: var(--leading-normal);
    font-weight: var(--font-weight-regular);
    overflow-wrap: break-word;     /* long supplier names, e-mails, references never overflow */
    background-color: var(--color-bg-canvas);
    color: var(--color-text-primary);
  }
  b, strong { font-weight: var(--font-weight-semibold); }
  h1, h2, h3, h4, .page-title, .login-headline { text-wrap: balance; }
  .prose p, .prose li { text-wrap: pretty; }

  /* --- Type scale (typography.md section 7 roles, design-tokens.md values) --- */
  h1,
  .page-title {
    font-family: var(--font-family-display);
    font-size: var(--text-2xl);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--tracking-tight);
    font-variant-numeric: lining-nums proportional-nums;
    color: var(--color-text-heading);
  }
  h2 {
    font-size: var(--text-xl);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
  }
  h3 {
    font-size: var(--text-lg);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
  }
  h4 {
    font-size: var(--text-md);
    line-height: var(--leading-normal);
    font-weight: var(--font-weight-semibold);
  }
  small,
  .text-caption {
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
  }
  p + p { margin-block-start: var(--space-3); }
  .prose,
  .measure {
    max-inline-size: var(--size-measure);
  }
  .prose :is(p, li):lang(id),
  .prose :is(p, li):lang(en) {
    hyphens: auto;
  }
  @media (max-width: 39.9375rem) {
    h1,
    .page-title {
      font-size: var(--text-xl);
    }
  }

  /* --- Numerals (typography.md section 6.1, exact) -------------------- */
  .money,
  .money-cell,
  .tabular,
  .percent,
  .kpi-tile-value,
  .money-input,
  input[inputmode="numeric"],
  input[inputmode="decimal"],
  td.is-numeric,
  th.is-numeric {
    font-family: var(--font-family-sans);
    font-variant-numeric: lining-nums tabular-nums;
  }
  .money,
  .money-cell,
  .percent,
  .kpi-tile-value {
    white-space: nowrap;           /* "Rp 12.500.000" never breaks between prefix and number */
  }
  .money-cell,
  th.is-numeric,
  td.is-numeric { text-align: right; }
  .code {
    font-family: var(--font-family-mono);
    font-size: 0.9375em;
    font-variant-numeric: slashed-zero;
    overflow-wrap: anywhere;
  }
  time { font-variant-numeric: lining-nums tabular-nums; }

  /* --- Browser surfaces (design-direction.md section 6) --------------- */
  ::selection {
    background-color: var(--color-selection-bg);
    color: var(--color-selection-fg);
  }
  :focus-visible {
    /* The 2px offset is load-bearing: it keeps the ring on the page surface,
       never on a solid accent/danger fill (design-tokens.md A11Y-02). Never 0. */
    outline: var(--border-width-focus) solid var(--color-focus-ring);
    outline-offset: var(--border-offset-focus);
  }
  a {
    color: var(--color-link);
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: var(--text-underline-offset);
  }
  a:hover {
    color: var(--color-link-hover);
    text-decoration-thickness: 2px;
  }
  input,
  textarea {
    caret-color: var(--color-caret);
  }
  ::placeholder {
    color: var(--color-text-muted);
    opacity: 1;
  }
  hr {
    border: 0;
    border-block-start: var(--border-width-hairline) solid var(--color-border-subtle);
    margin-block: var(--space-6);
  }
}

/* =====================================================================
 * COMPONENTS
 * ===================================================================== */
@layer components {
  /* --- Icon (icons.md section 4) ------------------------------------ */
  .icon {
    inline-size: var(--size-icon-md);
    block-size: var(--size-icon-md);
    flex: none;
    vertical-align: middle;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  .table-region .icon,
  .status-pill .icon,
  .sort-control .icon,
  .alert .icon,
  .flag-badge .icon,
  .language-switch .icon,
  .form-error .icon,
  .error-summary-title .icon { inline-size: var(--size-icon-sm); block-size: var(--size-icon-sm); }
  .empty-state .icon,
  .list-state .icon { inline-size: var(--size-icon-lg); block-size: var(--size-icon-lg); }
  /* Chevron rotation for disclosures: a state, not an animation. */
  [aria-expanded="true"] .icon-rotate,
  details[open] > summary .icon-rotate { transform: rotate(180deg); }

  /* --- Skip link (layout-ia-navigation.md sections 4, 8) ------------- */
  .skip-link {
    position: fixed;
    inset-block-start: var(--space-2);
    inset-inline-start: var(--space-2);
    z-index: var(--z-skip-link);
    padding: var(--space-2) var(--space-4);
    background-color: var(--color-surface-1);
    color: var(--color-accent);
    border: var(--border-width-hairline) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-2);
    font-size: var(--text-base);
    font-weight: var(--font-weight-medium);
    transform: translateY(-300%);
    /* P4: fully clipped while off-screen, so it is never a (hidden) pointer target that
       overlaps content scrolled above the viewport (axe target-size); still focusable. */
    clip-path: inset(50%);
  }
  .skip-link:focus,
  .skip-link:focus-visible {
    transform: none;
    clip-path: none;
  }

  /* --- App shell ---------------------------------------------------- */
  .app-shell {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    min-block-size: 100vh;
  }
  .app-main {
    display: flex;
    flex-direction: column;
    min-inline-size: 0;
  }
  /* #main is a skip-link target (tabindex="-1"), not a control: no ring on the whole page. */
  #main:focus { outline: none; }
  .page-content {
    flex: 1 0 auto;
    display: grid;
    align-content: start;
    row-gap: var(--space-6);
    inline-size: 100%;
    max-inline-size: var(--layout-content-max);
    margin-inline: auto;
    padding: var(--space-6) var(--layout-gutter-sm) var(--space-12);
  }
  /* Shell notice slot (components/role_review_banner.html): same measure and gutters as
     .page-content so the banner lines up with the page below it; .page-content's own
     block-start padding supplies the gap under it. */
  .page-notices {
    display: grid;
    row-gap: var(--space-3);
    inline-size: 100%;
    max-inline-size: var(--layout-content-max);
    margin-inline: auto;
    padding: var(--space-6) var(--layout-gutter-sm) 0;
  }
  .app-footer {
    padding: var(--space-4) var(--layout-gutter-sm);
    border-block-start: var(--border-width-hairline) solid var(--color-border-subtle);
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
  }

  /* Sidebar: the solid ruby brand column (Impact Hub Jakarta maroon).
     Focus inside it switches to a rose ring: a ruby ring would vanish on ruby. */
  .sidebar-nav {
    --color-focus-ring: var(--color-sidebar-focus);
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    padding: var(--space-6) var(--space-3);
    background-color: var(--color-sidebar-bg);
    color: var(--color-sidebar-fg);
    scrollbar-width: thin;
    scrollbar-color: var(--color-sidebar-scrollbar-thumb) var(--color-scrollbar-track);
  }
  /* FINORA name and expansion over the Impact Hub Jakarta organization mark. */
  .sidebar-wordmark {
    display: grid;
    gap: var(--space-2);
    padding: 0 var(--space-3) var(--space-5);
    border-block-end: var(--border-width-hairline) solid var(--color-sidebar-rule);
    color: var(--color-sidebar-fg-strong);
    text-decoration: none;
  }
  .sidebar-wordmark:hover {
    color: var(--color-sidebar-fg-strong);
  }
  .sidebar-wordmark .brand-name {
    font-family: var(--font-family-display);
    font-size: var(--text-2xl);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--tracking-tight);
    text-wrap: balance;
  }
  .sidebar-wordmark:hover .brand-name {
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: var(--text-underline-offset);
  }
  .brand-expansion {
    color: var(--color-sidebar-fg);
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
    text-wrap: balance;
  }
  .brand-mark {
    margin-block-start: var(--space-1);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-sidebar-fg);
    font-size: var(--text-md);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-semibold);
  }
  .brand-mark b {
    padding: var(--space-1);
    border: var(--border-width-hairline) solid currentColor;
    color: var(--color-sidebar-fg-strong);
    font-size: 0.625rem;
    line-height: 0.95;
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--tracking-wide);
  }
  .sidebar-nav nav,
  .nav-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-0-5);
  }
  .nav-group-label {
    padding-inline: var(--space-3);
    margin-block-end: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
  }
  .sidebar-nav .nav-group-label {
    color: var(--color-sidebar-fg-muted);
    font-weight: var(--font-weight-semibold);
  }
  /* A labelled group opens a new section: set it well apart from the group above,
     so the label reads as belonging to the items below it (4px), not the ones above. */
  .nav-group-label:not(:first-child) {
    margin-block-start: var(--space-5);
  }
  .nav-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-block-size: var(--size-control-md);
    padding: var(--space-1) var(--space-3);
    border: var(--border-width-hairline) solid transparent;
    border-radius: var(--radius-md);
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    hyphens: manual;
  }
  .nav-item:hover {
    background-color: var(--color-surface-hover);
    color: var(--color-text-primary);
  }
  /* Current page: ruled box + semibold + tint. Shape and weight carry it, not colour (R53). */
  .nav-item.is-active,
  .nav-item[aria-current="page"] {
    background-color: var(--color-accent-subtle);
    border-color: var(--color-accent);
    color: var(--color-accent);
    font-weight: var(--font-weight-semibold);
  }
  /* On the ruby sidebar: rose text, a deeper-ruby hover, and the current page as a
     filled rose tab with a dark inline-start bar and semibold label. The fill, the
     bar and the weight carry "current" without relying on hue (R53). */
  .sidebar-nav .nav-item {
    position: relative;
    color: var(--color-sidebar-fg);
  }
  .sidebar-nav .nav-item:hover {
    background-color: var(--color-sidebar-hover-bg);
    border-color: transparent;
    color: var(--color-sidebar-fg-strong);
  }
  .sidebar-nav .nav-item.is-active,
  .sidebar-nav .nav-item[aria-current="page"] {
    background-color: var(--color-sidebar-active-bg);
    border-color: var(--color-sidebar-active-bg);
    color: var(--color-sidebar-active-fg);
    font-weight: var(--font-weight-semibold);
  }
  .sidebar-nav .nav-item.is-active::before,
  .sidebar-nav .nav-item[aria-current="page"]::before {
    content: "";
    position: absolute;
    inset-block: var(--space-1);
    inset-inline-start: var(--space-1);
    inline-size: var(--border-width-total);
    border-radius: var(--radius-full);
    background-color: var(--color-sidebar-active-bar);
  }
  .nav-toggle {
    display: inline-flex;
  }
  @media (min-width: 64rem) {
    .app-shell {
      grid-template-columns: var(--layout-sidebar-width) minmax(0, 1fr);
    }
    .sidebar-nav {
      position: sticky;
      inset-block-start: 0;
      /* min, not a fixed block-size: a fixed 100vh caps the box at the viewport
         height and overrides the grid's default item stretch, so on pages taller
         than one viewport the sidebar surface stops mid-page while the content
         column keeps going. min-block-size lets the box stretch to match the
         (taller) content row while still guaranteeing full-viewport coverage on
         short pages; overflow-y:auto keeps a very long nav list scrollable. */
      min-block-size: 100vh;
      overflow-y: auto;
    }
    .topbar .nav-toggle {
      display: none;
    }
    .page-content,
    .page-notices,
    .app-footer {
      padding-inline: var(--layout-gutter);
    }
  }
  @media (min-width: 48rem) and (max-width: 63.9375rem) {
    .page-content,
    .page-notices,
    .app-footer {
      padding-inline: var(--layout-gutter);
    }
  }
  /* Below lg the sidebar is off-canvas once js/behaviors/nav-toggle.js has set
     data-state. Without JS (no data-state) it stays in flow, so every page link is
     reachable. When scripting is on, the not-yet-initialised sidebar is hidden from
     first paint: otherwise it is painted in flow and then collapsed to "closed" by the
     module, shifting the whole page up (AC-MOT-07, measured CLS 0.397 at 390x844).
     Once nav-toggle.js sets data-state this selector stops matching. */
  @media (max-width: 63.9375rem) and (scripting: enabled) {
    .sidebar-nav:not([data-state]) {
      display: none;
    }
  }
  @media (max-width: 63.9375rem) {
    .sidebar-nav {
      border-inline-end: 0;
      border-block-end: var(--border-width-hairline) solid var(--color-sidebar-rule);
    }
    .sidebar-nav[data-state="closed"] {
      display: none;
    }
    .sidebar-nav[data-state="open"],
    .sidebar-nav[data-state="closing"] {
      position: fixed;
      inset-block: 0;
      inset-inline-start: 0;
      z-index: var(--z-offcanvas);
      inline-size: min(20rem, 85vw);
      overflow-y: auto;
      border-inline-end: var(--border-width-hairline) solid var(--color-sidebar-rule);
      border-block-end: 0;
      box-shadow: var(--shadow-3);
    }
  }

  /* Topbar: no title (R52). The h1 lives in the page header. */
  .topbar {
    position: sticky;
    inset-block-start: 0;
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-block-size: var(--layout-topbar-height);
    padding: var(--space-2) var(--layout-gutter-sm);
    background-color: var(--color-surface-1);
    border-block-end: var(--border-width-hairline) solid var(--color-border-subtle);
  }
  .topbar-search {
    flex: 1 1 16rem;
    max-inline-size: 28rem;
  }
  .topbar-search input {
    inline-size: 100%;
  }
  .topbar-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-inline-start: auto;
    /* P1 close-out A2: 320px overflowed by 4px. As a flex item this box kept its
       automatic min size (its min-content: the whole language link plus the whole
       user-menu summary), so it could not shrink below ~268px and pushed past the
       topbar's right padding. It may shrink now; the user menu absorbs it. */
    min-inline-size: 0;
  }
  .topbar > .nav-toggle,
  .topbar-actions > .language-switch {
    flex-shrink: 0;
  }
  /* The name gives way first: details, summary and span all shrink, and the span
     ellipsises (below). The popup is absolutely positioned, so it keeps its width. */
  .user-menu {
    min-inline-size: 0;
  }
  .user-menu > summary {
    max-inline-size: 100%;
    min-inline-size: 0;
  }
  @media (max-width: 39.9375rem) {
    .topbar-search { display: none; }
  }
  @media (min-width: 48rem) {
    .topbar { padding-inline: var(--layout-gutter); }
  }
  /* Long display names (and longer id copy) never push the topbar controls off
     screen; the name is the user's own, so a visual ellipsis loses nothing. */
  .user-menu > summary > span {
    min-inline-size: 0;
    max-inline-size: min(12rem, 30vw);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Segmented link pairs: density toggle, language switch. */
  .density-toggle,
  .language-switch {
    display: inline-flex;
    padding: var(--space-0-5);
    gap: var(--space-0-5);
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-md);
  }
  .density-toggle a,
  .language-switch a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);           /* the language link is icon + label; they must not touch */
    min-block-size: var(--size-control-sm);
    min-inline-size: var(--size-control-sm);
    padding: 0 var(--space-2);
    border: var(--border-width-hairline) solid transparent;
    border-radius: var(--radius-sm);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
  }
  .density-toggle a:hover,
  .language-switch a:hover {
    color: var(--color-text-primary);
  }
  .density-toggle a[aria-current],
  .language-switch a[aria-current] {
    background-color: var(--color-surface-1);
    border-color: var(--color-border-strong);
    color: var(--color-text-primary);
    font-weight: var(--font-weight-semibold);
  }

  /* --- Buttons ------------------------------------------------------ */
  .button,
  .confirm-dialog menu button:not(.button) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-block-size: var(--size-control-md);
    min-inline-size: var(--size-control-md);
    padding: 0 var(--space-4);
    border: var(--border-width-hairline) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
    font-family: var(--font-family-sans);
    font-size: var(--text-base);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-medium);
    text-align: center;
    text-decoration: none;
    hyphens: manual;
    cursor: pointer;
    transition: transform var(--motion-duration-instant) var(--motion-ease-standard);
  }
  .button:hover,
  .confirm-dialog menu button:not(.button):hover {
    background-color: var(--color-surface-hover);
    color: var(--color-text-primary);
  }
  .button:active,
  .button.secondary:active {
    background-color: var(--color-surface-sunken);
  }
  .button:active:not(:disabled):not([aria-disabled="true"]) {
    transform: translateY(1px);
  }
  .button.primary {
    background-color: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-accent-fg);
  }
  .button.primary:hover {
    background-color: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
    color: var(--color-accent-fg);
  }
  .button.primary:active {
    background-color: var(--color-accent-active);
    border-color: var(--color-accent-active);
  }
  .button.ghost {
    background-color: transparent;
    border-color: transparent;
    color: var(--color-accent);
  }
  .button.ghost:hover {
    background-color: var(--color-surface-hover);
    color: var(--color-accent-hover);
  }
  .button.danger {
    background-color: var(--color-danger-solid);
    border-color: var(--color-danger-solid);
    color: var(--color-text-inverse);
  }
  .button.danger:hover {
    background-color: var(--color-danger-solid-hover);
    border-color: var(--color-danger-solid-hover);
    color: var(--color-text-inverse);
  }
  .button.small {
    min-block-size: var(--size-control-sm);
    padding: 0 var(--space-3);
    font-size: var(--text-sm);
  }
  .button.block {
    display: flex;
    inline-size: 100%;
  }
  .button:disabled,
  .button[aria-disabled="true"],
  .button.primary:disabled,
  .button.primary[aria-disabled="true"],
  .button.danger:disabled,
  .button.danger[aria-disabled="true"],
  .button.ghost:disabled,
  .button.ghost[aria-disabled="true"] {
    background-color: var(--color-neutral-bg);
    border-color: var(--color-border-subtle);
    color: var(--color-text-disabled);
    cursor: not-allowed;
  }
  .button[aria-busy="true"] {
    cursor: progress;
  }
  /* Icon-only controls: the box, not the glyph, clears 24x24 (A11Y-06, AC-UI-14). */
  .icon-button,
  .alert-dismiss,
  .nav-toggle,
  .copilot-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--size-control-sm);
    min-block-size: var(--size-control-sm);
    padding: var(--space-1);
    border: var(--border-width-hairline) solid transparent;
    border-radius: var(--radius-md);
    background-color: transparent;
    color: var(--color-text-secondary);
  }
  .icon-button:hover,
  .alert-dismiss:hover,
  .nav-toggle:hover,
  .copilot-trigger:hover {
    background-color: var(--color-surface-hover);
    color: var(--color-text-primary);
  }
  .copilot-trigger[aria-expanded="true"] {
    background-color: var(--color-accent-subtle);
    border-color: var(--color-accent);
    color: var(--color-accent);
  }

  /* --- Page header -------------------------------------------------- */
  .page-header {
    display: grid;
    gap: var(--space-2);
  }
  .back-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    justify-self: start;
    min-block-size: var(--size-touch-min);
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
  }
  .back-link:hover {
    color: var(--color-text-primary);
    text-decoration-line: underline;
  }
  .back-link .icon {
    inline-size: var(--size-icon-sm);
    block-size: var(--size-icon-sm);
  }
  .page-header-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3) var(--space-6);
  }
  .page-header-row > :last-child:not(h1) {
    flex: none;
  }
  .page-header-description {
    max-inline-size: var(--size-measure);
    color: var(--color-text-secondary);
    font-size: var(--text-md);
    line-height: var(--leading-normal);
  }

  /* --- Page templates (layout-ia-navigation.md section 5) ------------ */
  .page-grid {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--space-6);
    row-gap: var(--space-8);
    align-items: start;
  }
  .page-grid > * {
    grid-column: 1 / -1;
    min-inline-size: 0;
  }
  .page-primary,
  .page-secondary,
  .settings-content {
    display: grid;
    row-gap: var(--space-6);
    min-inline-size: 0;
  }
  .settings-subnav {
    display: flex;
    gap: var(--space-1);
    overflow-x: auto;
    border-block-end: var(--border-width-hairline) solid var(--color-border-subtle);
    scrollbar-width: thin;
  }
  .settings-subnav .nav-item {
    flex: none;
  }
  @media (min-width: 64rem) {
    .page-grid--detail > .page-primary,
    .page-grid--form > .page-primary {
      grid-column: span 8;
    }
    .page-grid--detail > .page-secondary,
    .page-grid--form > .page-secondary {
      grid-column: span 4;
    }
    .page-grid--settings > .settings-subnav {
      grid-column: span 3;
      position: sticky;
      inset-block-start: calc(var(--layout-topbar-height) + var(--space-6));
      flex-direction: column;
      overflow-x: visible;
      border-block-end: 0;
    }
    .page-grid--settings > .settings-content {
      grid-column: span 9;
    }
  }
  /* --- Settings pages (P1 critique M1; layout-ia-navigation.md section 5) ---
     The settings templates stack header, sub-nav tabs, notices and panels as siblings
     in .settings-page / .settings-body (the tab strip is the below-lg form of the
     settings sub-nav). These give them the records pages' rhythm. */
  .settings-page,
  .settings-body {
    display: grid;
    row-gap: var(--space-6);
    align-content: start;
    min-inline-size: 0;
  }
  /* The sub-nav belongs to the header above it, not to the next section. */
  .settings-page > .page-header + .tabs {
    margin-block-start: calc(var(--space-2) * -1);
  }
  .settings-egress > [role="status"] {
    display: grid;
    gap: var(--space-4);
    min-inline-size: 0;
  }
  .settings-form {
    display: grid;
    gap: var(--space-3);
    align-content: start;
    min-inline-size: 0;
  }
  .settings-form > .button {
    justify-self: start;
  }
  /* Reason and value fields keep a readable line length on wide panels. */
  .settings-form > .form-field {
    max-inline-size: var(--size-measure);
  }
  /* One editable setting per row, ruled off from its neighbour inside the panel. */
  .settings-row + .settings-row {
    padding-block-start: var(--space-5);
    border-block-start: var(--border-width-hairline) solid var(--color-border-subtle);
  }
  /* Heading and its env/runtime/effective caption read as one unit. */
  .settings-row > h3 + .text-caption {
    margin-block-start: calc(var(--space-2) * -1);
  }
  .settings-row > h3 code {
    margin-inline-start: var(--space-1);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    font-weight: var(--font-weight-regular);
  }
  .settings-env-list {
    display: grid;
    gap: var(--space-2);
  }
  /* Settings hub: a ruled index of the sections the user may open. */
  .settings-links > ul {
    display: grid;
    margin: 0;
    padding: 0;
    list-style: none;
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
  }
  .settings-links li {
    display: grid;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-5);
  }
  .settings-links li + li {
    border-block-start: var(--border-width-hairline) solid var(--color-border-subtle);
  }
  .settings-links a {
    justify-self: start;
    font-weight: var(--font-weight-semibold);
  }
  /* Settings facts (<dl class="settings-facts">): the read-only values a settings
     panel reports (env default, runtime override, effective value, requested by,
     requested at). A ledger: label over value on phones, then a ruled two-column
     label/value table from 40rem. Wrapped pairs (a <div> around each dt/dd, valid
     in a <dl>) become fact tiles on an auto-fit grid instead. */
  .settings-facts {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0 var(--space-6);
    margin: 0;
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .settings-facts dt {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--font-weight-medium);
  }
  .settings-facts dd {
    margin: 0;
    min-inline-size: 0;
    color: var(--color-text-primary);
    font-variant-numeric: var(--font-feature-numeric);
    overflow-wrap: anywhere;
  }
  .settings-facts > dd + dt {
    margin-block-start: var(--space-3);
  }
  .settings-facts > dt + dd {
    margin-block-start: var(--space-0-5);
  }
  @media (min-width: 40rem) {
    .settings-facts:not(:has(> div)) {
      grid-template-columns: fit-content(16rem) minmax(0, 1fr);
    }
    .settings-facts > dt,
    .settings-facts > dd {
      padding-block: var(--space-2);
      border-block-start: var(--border-width-hairline) solid var(--color-border-subtle);
    }
    .settings-facts > dd + dt,
    .settings-facts > dt + dd {
      margin-block-start: 0;
    }
    .settings-facts > dt:first-of-type,
    .settings-facts > dt:first-of-type + dd {
      padding-block-start: 0;
      border-block-start: 0;
    }
    .settings-facts > dt {
      font-size: var(--text-base);
    }
  }
  .settings-facts:has(> div) {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
    gap: var(--space-4) var(--space-6);
  }
  .settings-facts > div {
    display: grid;
    gap: var(--space-0-5);
    align-content: start;
    min-inline-size: 0;
  }
  .report-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
  }
  @media (min-width: 48rem) {
    .report-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (min-width: 80rem) {
    .report-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }
  @media (min-width: 96rem) {
    .report-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  }
  .report-grid > .span-2 { grid-column: span 2; }
  @media (max-width: 47.9375rem) {
    .report-grid > .span-2 { grid-column: auto; }
  }

  /* Panels: a white ruled page on the desk. Flat; the hairline is the edge. */
  .panel {
    display: grid;
    gap: var(--space-4);
    align-content: start;
    min-inline-size: 0;
    padding: var(--space-5);
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
  }
  .panel-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
  }
  .panel-title {
    font-size: var(--text-lg);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
  }
  .detail-list {
    display: grid;
    /* fit-content, not max-content: one long translated label ("Jumlah yang
       dikonfirmasi manusia") wraps at 16rem instead of starving the value column. */
    grid-template-columns: fit-content(16rem) minmax(0, 1fr);
    column-gap: var(--space-6);
    row-gap: var(--space-2);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .detail-list dt {
    color: var(--color-text-secondary);
    font-weight: var(--font-weight-medium);
  }
  .detail-list dd {
    color: var(--color-text-primary);
    min-inline-size: 0;
  }
  @media (max-width: 39.9375rem) {
    .detail-list { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-1); }
    .detail-list dd + dt { margin-block-start: var(--space-2); }
  }

  /* --- Data table (components.md section 4) ------------------------- */
  .table-region {
    position: relative;
    display: grid;
    /* Explicit minmax(0, 1fr), not the implicit single "auto" column a bare
       display:grid falls back to: an auto track's growth limit is the max-content
       size of its items, so a wide table inside .table-scroll (itself correctly
       clipped to its own box by overflow:auto) still forced this grid container's
       own scrollWidth past its clientWidth, and with overflow left at the default
       visible, that leaked out as page-level horizontal scroll at narrow widths
       (WCAG 1.4.10) on any table wide enough to need its scroll container (wide
       workflow-settings / project-members tables). minmax(0, 1fr) clamps the
       track to the available width instead, matching .report-grid's convention. */
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-3);
    min-inline-size: 0;
  }
  .table-scroll {
    /* Containing block for anything a table positions absolutely (row actions,
       pinned-cell shadows), so it scrolls with the table, not the region. */
    position: relative;
    overflow: auto;
    max-block-size: calc(100vh - var(--layout-topbar-height) - var(--space-8));
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    scrollbar-width: thin;
    scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
    /* The html scroll-padding does not reach this inner scroller: without this, a row
       link focused by keyboard scrolls to the top edge and hides under the sticky
       header (WCAG 2.4.11, AC-UI-13). */
    scroll-padding-block-start: calc(var(--size-row-header) + var(--space-1));
    /* Scroll shadows (CSS only): an edge shade shows on whichever side has more
       table to scroll to. The two surface-coloured covers scroll with the content
       (local) and sit over the two shades pinned to the viewport edges (scroll), so
       a shade is hidden while its edge of the table is in view. Opaque cells (the
       header row, hover/selected rows, pinned cells) paint over it; plain body rows
       let it through, which is where the eye reads the table. */
    background-image:
      linear-gradient(to right, var(--color-surface-1) 40%, transparent),
      linear-gradient(to left, var(--color-surface-1) 40%, transparent),
      radial-gradient(farthest-side at 0 50%, var(--color-surface-overlay), transparent),
      radial-gradient(farthest-side at 100% 50%, var(--color-surface-overlay), transparent);
    background-position: left center, right center, left center, right center;
    background-size: var(--space-8) 100%, var(--space-8) 100%, var(--space-3) 100%, var(--space-3) 100%;
    background-repeat: no-repeat;
    background-attachment: local, local, scroll, scroll;
  }
  .table-scroll table {
    inline-size: 100%;
    font-size: var(--text-table);
    line-height: var(--leading-table);
  }
  .table-scroll caption {
    padding: var(--space-3) var(--space-cell-x);
    text-align: start;
    font-size: var(--text-base);
    font-weight: var(--font-weight-semibold);
  }
  .table-scroll thead th {
    position: sticky;
    inset-block-start: 0;
    z-index: 1;
    block-size: var(--size-row-header);
    padding: var(--space-cell-y) var(--space-cell-x);
    background-color: var(--color-surface-2);
    border-block-end: var(--border-width-hairline) solid var(--color-border-strong);
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
    text-align: start;
    vertical-align: bottom;
    white-space: normal;
    hyphens: manual;
  }
  /* P4 UI-3a: also .table-scroll, so a table set directly in a .panel (settings env-only
     keys, AI audit suggestions) gets the same cell padding instead of text on the rule. */
  .table-region td,
  .table-scroll td {
    block-size: var(--size-row);
    padding: var(--space-cell-y) var(--space-cell-x);
    border-block-end: var(--border-width-hairline) solid var(--color-border-subtle);
    font-size: var(--text-table);
    line-height: var(--leading-table);
    vertical-align: middle;
  }
  .table-scroll tbody tr:last-child td {
    border-block-end: 0;
  }
  .table-scroll tbody tr:hover td {
    background-color: var(--color-surface-hover);
  }
  /* Selection is carried by the checked row-select box; the tint only follows it.
     P4 UI-3a: scoped to .col-select, so an "active" / "offered" setting checkbox
     (workflow settings rows) no longer paints its row as selected. */
  .table-scroll tbody tr:has(> .col-select input[type="checkbox"]:checked) td,
  .table-scroll tbody tr.is-selected td {
    background-color: var(--color-surface-selected);
  }
  /* Ledger total: a double rule, then the figure in semibold. */
  .table-scroll tfoot td,
  .table-scroll tr.is-total td {
    border-block-start: var(--border-width-total) double var(--color-border-strong);
    border-block-end: 0;
    font-weight: var(--font-weight-semibold);
  }
  .table-scroll th.text-right,
  .table-scroll td.text-right {
    text-align: right;
  }
  .col-select {
    inline-size: calc(var(--size-touch-min) + var(--space-cell-x) * 2);
    text-align: center;
  }
  input[type="checkbox"],
  input[type="radio"] {
    inline-size: var(--size-touch-min);
    block-size: var(--size-touch-min);
    margin: 0;
    accent-color: var(--color-accent);
    vertical-align: middle;
  }
  .sort-control {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-block-size: var(--size-touch-min);
    /* P4 D4-04/D3-21: the target stays touch-min tall, but its extra height is taken
       back by negative block margins, so the label shares the baseline of a plain
       header label instead of riding (touch-min - 1lh) / 2 higher. */
    margin-block: min(0px, calc((1lh - var(--size-touch-min)) / 2));
    color: inherit;
    font-weight: inherit;
    text-decoration: none;
  }
  .sort-control:hover {
    color: var(--color-text-primary);
    text-decoration-line: underline;
  }
  th[aria-sort="ascending"] .sort-control,
  th[aria-sort="descending"] .sort-control {
    color: var(--color-text-primary);
    font-weight: var(--font-weight-semibold);
  }
  .money-cell.is-negative,
  .money.is-negative {
    color: var(--color-fin-negative-fg);
  }
  .money-cell.is-positive,
  .money.is-positive {
    color: var(--color-fin-positive-fg);
  }
  .table-region .code,
  .table-region time {
    white-space: nowrap;
  }
  /* A claim-detail link in a table cell is always the claim reference
     ("CLM-2026-0914"): keep it on one line instead of breaking at its hyphens. */
  .table-region td > a[href^="/dashboard/claims/"] {
    white-space: nowrap;
  }
  /* AI audit tables mark numeric columns with .num (tokens, cost, latency). */
  .table-scroll th.num,
  .table-scroll td.num {
    text-align: right;
  }
  /* Hidden-field-only forms that sit in a list region (claims export forms) are not
     boxes: without this each added one grid gap above the table (P4 UI-3a). */
  .table-region > form:not(:has(> :not(input[type="hidden"]))) {
    display: contents;
  }
  .cell-truncate {
    max-inline-size: 24rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Opt-in stacked cards below md (layout-ia-navigation.md section 7).
     Each td carries data-label="<column label>"; the header row is dropped. */
  @media (max-width: 47.9375rem) {
    .table-region--cards .table-scroll {
      max-block-size: none;
      overflow: visible;
      background-color: transparent;
      background-image: none;
      border: 0;
    }
    .table-region--cards table,
    .table-region--cards tbody,
    .table-region--cards tr,
    .table-region--cards td {
      display: block;
    }
    .table-region--cards thead {
      display: none;
    }
    .table-region--cards tbody {
      display: grid;
      /* minmax(0, 1fr), not the implicit auto track: an auto track grows to the
         widest card's max-content (a long project name or amount), which pushed the
         page 17-48px wide at 320px (P1 close-out A2; same cause as .table-region). */
      grid-template-columns: minmax(0, 1fr);
      gap: var(--space-3);
    }
    .table-region--cards tbody tr {
      padding: var(--space-3) var(--space-4);
      background-color: var(--color-surface-1);
      border: var(--border-width-hairline) solid var(--color-border-subtle);
      border-radius: var(--radius-md);
    }
    .table-region--cards tbody tr:hover td {
      background-color: transparent;
    }
    .table-region--cards td {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
      align-items: baseline;
      gap: var(--space-4);
      block-size: auto;
      padding: var(--space-1) 0;
      border: 0;
      text-align: end;
    }
    .table-region--cards td[data-label]::before {
      content: attr(data-label);
      margin-inline-end: auto;
      color: var(--color-text-secondary);
      font-weight: var(--font-weight-medium);
      text-align: start;
    }
    /* P4 UI-3a: a value too long for the label's line wraps to its own line and stays
       on the value edge (end), like every other value in the card; with
       space-between a lone wrapped item fell back to the start edge. The label's
       auto margin keeps it at the start on the first line. */
    .table-region--cards td[data-label] {
      justify-content: flex-end;
    }
    /* P4 D3-09: size the figure, not the cell, so the data-label caption (::before)
       reads at the same size as every other caption in the card. */
    .table-region--cards td.money-cell > .tabular,
    .table-region--cards td.money-cell > .money-with-flag > .tabular {
      font-size: var(--text-lg);
      font-weight: var(--font-weight-semibold);
    }
    .table-region--cards .col-select {
      inline-size: auto;
    }
  }
  @media (min-width: 48rem) {
    /* Money and status stay visible while a wide table scrolls (section 7, md-lg). */
    .table-scroll .is-pinned {
      position: sticky;
      inset-inline-end: 0;
      background-color: var(--color-surface-1);
    }
    .table-scroll thead .is-pinned {
      z-index: 2;
      background-color: var(--color-surface-2);
    }
  }

  /* Pagination (pagination_control) */
  .pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    color: var(--color-text-secondary);
    font-size: var(--text-base);
  }
  .pagination-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
  }
  .pagination a,
  .pagination [aria-current="page"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--size-control-sm);
    min-block-size: var(--size-control-sm);
    padding: 0 var(--space-2);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
    font-variant-numeric: lining-nums tabular-nums;
    text-decoration: none;
  }
  .pagination a:hover {
    background-color: var(--color-surface-hover);
  }
  .pagination [aria-current="page"] {
    background-color: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-accent-fg);
    font-weight: var(--font-weight-semibold);
  }

  /* --- Status pill (components.md section 6, design-tokens.md 6.3) --- */
  .status-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-0-5) var(--space-2);
    border: var(--border-width-hairline) solid var(--color-neutral-border);
    border-radius: var(--radius-sm);
    background-color: var(--color-neutral-bg);
    color: var(--color-neutral-fg);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
    vertical-align: middle;
  }
  .status-pill--info { background-color: var(--color-info-bg); border-color: var(--color-info-border); color: var(--color-info-fg); }
  .status-pill--success { background-color: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success-fg); }
  .status-pill--warning { background-color: var(--color-warning-bg); border-color: var(--color-warning-border); color: var(--color-warning-fg); }
  .status-pill--danger { background-color: var(--color-danger-bg); border-color: var(--color-danger-border); color: var(--color-danger-fg); }
  /* Budget line above 100%: the one solid, loud state. */
  .status-pill--over-budget {
    background-color: var(--color-fin-over-budget-bg);
    border-color: var(--color-fin-over-budget-border);
    color: var(--color-fin-over-budget-fg);
  }
  /* Outside a table cell a long translated label may wrap on a narrow screen rather
     than overflow the page (WCAG 1.4.10). In cells it stays on one line: the table
     scrolls horizontally, and wrapped pills would make rows uneven. */
  @media (max-width: 39.9375rem) {
    :not(td) > .status-pill {
      white-space: normal;
    }
  }
  .status-marker {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
  }

  /* --- SVG micro-charts (ADR-0002 section 6, design-tokens.md 14.3) --- */
  .progress-bar,
  .bullet-chart,
  .stacked-bar {
    inline-size: 100%;
    block-size: auto;
    overflow: visible;
  }
  .sparkline {
    inline-size: 6.25rem;
    block-size: 1.5rem;
  }
  .progress-bar-track,
  .progress-track,
  .bullet-track {
    fill: var(--color-surface-sunken);
    stroke: var(--color-border-strong);
    stroke-width: 1;
  }
  .progress-bar-fill,
  .progress-bar--ok .progress-bar-fill,
  .progress-fill--ok,
  .bullet-chart--ok .bullet-fill {
    fill: var(--color-fin-positive-border);
  }
  .progress-bar--warn_80 .progress-bar-fill,
  .progress-bar--warn .progress-bar-fill,
  .progress-fill--warn,
  .bullet-chart--warn .bullet-fill {
    fill: var(--color-fin-warning-border);
  }
  .progress-bar--over_100 .progress-bar-fill,
  .progress-bar--over .progress-bar-fill,
  .progress-fill--over,
  .bullet-chart--over .bullet-fill {
    fill: var(--color-fin-over-budget-bg);
  }
  .bullet-band-warn,
  .chart-band--warn { fill: var(--chart-warn-band); }
  .chart-band--over { fill: var(--chart-over-band); }
  .bullet-target-marker { stroke: var(--color-text-primary); }
  /* Warn tick and 100% marker paint after the fill (charts.html bullet), each on a
     paper knockout so it reads on the green, amber or red fill as well as the track. */
  .bullet-warn-tick { stroke: var(--chart-threshold-warn); }
  .bullet-tick-halo { stroke: var(--color-surface-1); }
  .sparkline-path { fill: none; stroke: var(--chart-series-1); }
  .stacked-bar-seg--neutral { fill: var(--chart-series-8); }
  .stacked-bar-seg--info { fill: var(--chart-series-1); }
  .stacked-bar-seg--success { fill: var(--color-fin-positive-border); }
  .stacked-bar-seg--warning { fill: var(--color-fin-warning-border); }
  .stacked-bar-seg--danger { fill: var(--color-fin-over-budget-bg); }
  .chart-fill-1, .stacked-bar-seg--1 { fill: var(--chart-series-1); }
  .chart-fill-2, .stacked-bar-seg--2 { fill: var(--chart-series-2); }
  .chart-fill-3, .stacked-bar-seg--3 { fill: var(--chart-series-3); }
  .chart-fill-4, .stacked-bar-seg--4 { fill: var(--chart-series-4); }
  .chart-fill-5, .stacked-bar-seg--5 { fill: var(--chart-series-5); }
  .chart-fill-6, .stacked-bar-seg--6 { fill: var(--chart-series-6); }
  .chart-fill-7, .stacked-bar-seg--7 { fill: var(--chart-series-7); }
  .chart-fill-8, .stacked-bar-seg--8 { fill: var(--chart-series-8); }
  .stacked-bar-seg { stroke: var(--color-surface-1); stroke-width: 1; }
  /* P4 D3-15: one ageing ramp for AP (bills aging) and AR (receivables). Five buckets,
     each fill darker than the one before (relative luminance .208 > .146 > .065 > .040
     > .020), walking taupe -> amber -> brown -> maroon. Every fill is >= 3.9:1 against
     surface-1, and the 1px surface stroke between segments carries the boundary; the
     bucket table under each bar is the exact data equivalent. The legend swatch uses
     the same key so a bucket reads the same in the bar and in its legend. */
  .stacked-bar-seg--age-not_due, .age-swatch--not_due { fill: var(--palette-neutral-500); background-color: var(--palette-neutral-500); }
  .stacked-bar-seg--age-1_30, .age-swatch--1_30 { fill: var(--palette-amber-600); background-color: var(--palette-amber-600); }
  .stacked-bar-seg--age-31_60, .age-swatch--31_60 { fill: var(--palette-amber-800); background-color: var(--palette-amber-800); }
  .stacked-bar-seg--age-61_90, .age-swatch--61_90 { fill: var(--palette-red-800); background-color: var(--palette-red-800); }
  .stacked-bar-seg--age-over_90, .age-swatch--over_90 { fill: var(--palette-red-900); background-color: var(--palette-red-900); }
  .age-swatch {
    display: inline-block;
    flex: none;
    inline-size: var(--space-3);
    block-size: var(--space-3);
    border-radius: var(--radius-sm);
  }

  /* --- Chart wrapper (components.md section 21, R42) ---------------- */
  /* Charts always sit on a white surface, never bare canvas (design-tokens.md A11Y-01). */
  .chart-figure {
    display: grid;
    gap: var(--space-3);
    min-inline-size: 0;
    padding: var(--space-5);
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
  }
  .chart-figure > figcaption {
    font-size: var(--text-lg);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
  }
  /* A chart-figure nested directly in a .panel would otherwise draw its own
     surface/border/padding inside the panel's identical surface/border,
     producing a visible double frame. Drop the redundant frame but keep the
     background transparent (not surface-2/sunken) so the chart still sits on
     the panel's paper --color-surface-1, which every --chart-series-* (lowest:
     series-8 3.91:1) was contrast-checked against. */
  .panel > .chart-figure {
    padding: 0;
    background-color: transparent;
    border: 0;
    border-radius: 0;
  }
  .chart-frame {
    position: relative;
    min-inline-size: 0;
    block-size: 18rem;
  }
  .chart-canvas { width: 100%; height: 100%; }
  .chart-error {
    color: var(--color-danger-fg);
    font-size: var(--text-base);
  }

  /* --- KPI tile and summary strip (components.md section 26, R50) ---- */
  .kpi-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
  }
  /* Below sm a single 13rem track stacks four tall tiles; pair them 2x2 instead.
     The value never wraps (Rp prefix + figure stay together), so the narrower tile
     takes a tighter inline padding and the value steps down to at most text-lg,
     scaled to the tile's own width: "Rp 1.250.000.000" (about 8.2em wide) stays on
     one line inside the tile at 390px (18px) and still at 320px (about 14.5px). */
  @media (max-width: 39.9375rem) {
    .kpi-strip {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    .kpi-strip .kpi-tile {
      container-type: inline-size;
      padding-inline: var(--space-3);
    }
    .kpi-strip .kpi-tile-value {
      font-size: var(--text-lg);
      font-size: clamp(var(--text-base), 12cqi, var(--text-lg));
    }
  }
  .kpi-strip > .kpi-tile,
  .kpi-strip > a {
    border-inline-start: var(--border-width-hairline) solid var(--color-border-subtle);
    margin-inline-start: calc(var(--border-width-hairline) * -1);
    border-block-end: var(--border-width-hairline) solid var(--color-border-subtle);
    margin-block-end: calc(var(--border-width-hairline) * -1);
  }
  .kpi-tile {
    display: grid;
    align-content: start;
    gap: var(--space-1);
    padding: var(--space-4) var(--space-5);
    min-inline-size: 0;
  }
  a:has(> .kpi-tile) {
    display: block;
    color: inherit;
    text-decoration: none;
  }
  a:has(> .kpi-tile):hover {
    background-color: var(--color-surface-hover);
  }
  /* .kpi-strip clips with overflow:hidden (for the shared hairlines), which would cut
     an outside ring on edge tiles: draw it inside the tile, on the tile's own surface. */
  .kpi-strip > a:focus-visible {
    outline-offset: calc(var(--border-offset-focus) * -2);
  }
  .kpi-tile-label {
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
  }
  .kpi-tile-value {
    color: var(--color-text-primary);
    font-size: var(--text-2xl);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--tracking-tight);
  }
  .kpi-tile--positive .kpi-tile-value { color: var(--color-fin-positive-fg); }
  .kpi-tile--negative .kpi-tile-value { color: var(--color-fin-negative-fg); }
  .kpi-tile--warning .kpi-tile-value { color: var(--color-fin-warning-fg); }
  .kpi-tile-trend {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
  }
  .kpi-tile-trend .icon {
    inline-size: var(--size-icon-sm);
    block-size: var(--size-icon-sm);
  }
  .kpi-tile-note {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
  }
  @media (max-width: 39.9375rem) {
    .kpi-tile-value { font-size: var(--text-xl); }
  }

  /* --- AI suggestion card (components.md section 9) ----------------- */
  /* Pencil, not ink: dashed violet until a human accepts it. */
  .ai-card {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    background-color: var(--color-ai-suggestion-bg);
    border: var(--border-width-hairline) var(--border-style-ai-suggestion) var(--color-ai-suggestion-border);
    border-radius: var(--radius-md);
    color: var(--color-ai-suggestion-fg);
  }
  .ai-card-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
  }
  .ai-card-label .icon {
    inline-size: var(--size-icon-sm);
    block-size: var(--size-icon-sm);
  }
  .ai-card-value {
    color: var(--color-ai-suggestion-fg);
    font-size: var(--text-md);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
  }
  .ai-card-rationale,
  .ai-card-readonly-note {
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .ai-card form,
  .flag-badge form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
  }
  .ai-card[aria-busy="true"],
  .flag-badge[aria-busy="true"] {
    cursor: progress;
  }
  /* Accepted/rejected: committed ink, solid hairline. */
  .ai-card--decided {
    background-color: var(--color-surface-1);
    border-style: solid;
    border-color: var(--color-border-subtle);
    color: var(--color-text-primary);
  }
  .ai-field {
    background-color: var(--color-ai-suggestion-bg);
    border-style: var(--border-style-ai-suggestion);
    border-color: var(--color-ai-suggestion-border);
    color: var(--color-ai-suggestion-fg);
  }

  /* --- Flag badge (components.md section 10) ------------------------ */
  .flag-badge {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-3) var(--space-4);
    background-color: var(--color-ai-suggestion-bg);
    border: var(--border-width-hairline) var(--border-style-ai-suggestion) var(--color-warning-border);
    border-radius: var(--radius-md);
    color: var(--color-text-primary);
  }
  .flag-badge > .icon {
    margin-block-start: var(--space-0-5);
    color: var(--color-warning-fg);
  }
  .flag-badge > form {
    grid-column: 2;
  }
  .flag-badge-title {
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
    color: var(--color-warning-fg);
  }
  .flag-badge-detail {
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .flag-badge--decided {
    background-color: var(--color-surface-1);
    border-style: solid;
    border-color: var(--color-border-subtle);
  }

  /* --- List states (components.md section 11) ----------------------- */
  .list-state {
    display: grid;
    justify-items: start;
    gap: var(--space-2);
    padding: var(--space-8) var(--space-6);
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
  }
  .list-state > .icon {
    color: var(--color-text-muted);
    margin-block-end: var(--space-1);
  }
  .list-state--error > .icon {
    color: var(--color-danger-fg);
  }
  .list-state-title {
    font-size: var(--text-lg);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
  }
  .list-state-body {
    max-inline-size: var(--size-measure);
    color: var(--color-text-secondary);
    font-size: var(--text-md);
    line-height: var(--leading-normal);
  }
  .list-state > .button {
    margin-block-start: var(--space-2);
  }
  /* Inside a panel the state sits on the panel's own page: no second frame
     (a bordered box inside a bordered panel is a nested card). */
  .panel .list-state {
    padding: var(--space-4) 0;
    background-color: transparent;
    border: 0;
    border-radius: 0;
  }
  .list-skeleton {
    inline-size: 100%;
  }
  .list-skeleton td {
    block-size: var(--size-row);
    padding: var(--space-cell-y) var(--space-cell-x);
  }
  .skeleton-block {
    display: block;
    inline-size: 70%;
    block-size: 0.75rem;
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-sm);
  }
  td:first-child > .skeleton-block { inline-size: 45%; }
  td.money-cell > .skeleton-block,
  td.text-right > .skeleton-block { margin-inline-start: auto; inline-size: 55%; }

  /* Request indicator: the ledger rule, 2px, over the region; no layout shift (M-04). */
  .region-progress {
    position: absolute;
    inset-inline: 0;
    inset-block-start: 0;
    z-index: 3;
    block-size: var(--border-width-strong);
    background-color: var(--color-accent);
    pointer-events: none;
  }
  [aria-busy="true"] {
    cursor: progress;
  }

  /* --- Forms (components.md sections 12-16) ------------------------- */
  .form-field {
    display: grid;
    align-content: start;
    gap: var(--space-1);
    min-inline-size: 0;
  }
  .form-field > label,
  .form-label,
  .confirm-dialog label {
    color: var(--color-text-primary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
    hyphens: manual;
  }
  .required-mark {
    margin-inline-start: var(--space-0-5);
    color: var(--color-danger-fg);
  }
  .form-hint {
    color: var(--color-text-muted);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    max-inline-size: var(--size-measure);
  }
  .form-hint--warning {
    display: flex;
    align-items: flex-start;
    gap: var(--space-1);
    color: var(--color-warning-fg);
  }
  .form-hint--warning .icon {
    inline-size: var(--size-icon-sm);
    block-size: var(--size-icon-sm);
    margin-block-start: var(--space-0-5);
  }
  .form-error {
    display: flex;
    align-items: flex-start;
    gap: var(--space-1);
    color: var(--color-danger-fg);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
  }
  .form-error .icon,
  .error-summary-title .icon {
    margin-block-start: var(--space-0-5);
  }
  .form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
  }
  fieldset {
    margin: 0;
    padding: 0;
    border: 0;
    min-inline-size: 0;
  }
  legend {
    padding: 0;
    margin-block-end: var(--space-2);
    font-size: var(--text-md);
    font-weight: var(--font-weight-semibold);
  }

  /* Text-like inputs, selects, textareas. */
  :where(
    input:not([type]),
    input[type="text"],
    input[type="search"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="tel"],
    input[type="url"],
    input[type="date"],
    input[type="month"],
    select,
    textarea
  ) {
    inline-size: 100%;
    min-block-size: var(--size-control-md);
    padding: 0 var(--space-3);
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    color: var(--color-text-primary);
    font-family: var(--font-family-sans);
    font-size: var(--text-md);
    line-height: var(--leading-snug);
  }
  textarea {
    min-block-size: calc(var(--size-control-md) * 3);
    padding-block: var(--space-2);
    resize: vertical;
  }
  :where(input, select, textarea):hover:not(:disabled) {
    border-color: var(--color-text-muted);
  }
  :where(input, select, textarea):disabled,
  :where(input, select, textarea)[readonly] {
    background-color: var(--color-surface-sunken);
    color: var(--color-text-secondary);
    cursor: not-allowed;
  }
  :where(input, select, textarea)[readonly] {
    cursor: default;
  }
  /* Invalid: danger border AND a heavier stroke, so it survives greyscale. */
  :where(input, select, textarea)[aria-invalid="true"],
  .has-error .money-input-group {
    border-color: var(--color-danger-border);
    border-width: var(--border-width-strong);
    padding-inline: calc(var(--space-3) - var(--border-width-hairline));
  }
  .has-error .money-input-group {
    padding-inline: 0;
  }
  .error-summary {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-4);
    background-color: var(--color-danger-bg);
    border: var(--border-width-hairline) solid var(--color-danger-border);
    border-radius: var(--radius-md);
    color: var(--color-danger-fg);
    scroll-margin-block-start: var(--layout-topbar-height);
  }
  .error-summary-title {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: var(--text-md);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-semibold);
  }
  .error-summary ul {
    padding-inline-start: var(--space-5);
  }
  .error-summary a {
    color: var(--color-danger-fg);
  }

  /* Money input (components.md section 13): prefix and field read as one control. */
  .money-input-group {
    display: flex;
    align-items: stretch;
    min-block-size: var(--size-control-md);
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    overflow: hidden;
  }
  .money-input-group:hover {
    border-color: var(--color-text-muted);
  }
  .money-input-group:focus-within {
    outline: var(--border-width-focus) solid var(--color-focus-ring);
    outline-offset: var(--border-offset-focus);
  }
  .money-input-prefix {
    display: inline-flex;
    align-items: center;
    padding: 0 var(--space-3);
    background-color: var(--color-surface-2);
    border-inline-end: var(--border-width-hairline) solid var(--color-border-subtle);
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    font-weight: var(--font-weight-medium);
  }
  .money-input-group input {
    flex: 1 1 auto;
    min-inline-size: 0;
    min-block-size: 0;
    border: 0;
    border-radius: 0;
    text-align: end;
  }
  .money-input-group input:focus-visible {
    outline: none;
  }
  .money-input-group input[aria-invalid="true"] {
    border-width: 0;
    padding-inline: var(--space-3);
  }

  /* Filter bar (components.md section 16). form.filter-bar > details.filter-bar-fold
     > (summary.filter-bar-summary, .filter-bar-body > fields + actions).
     Below 40rem the <details> is the fold: closed by default, the summary names it
     and counts the active filters. From 40rem the body always shows and the summary
     goes (where ::details-content exists; elsewhere it stays a working toggle, so the
     fields are never out of reach). No JS either way. */
  .filter-bar {
    padding: var(--space-3) var(--space-4);
    background-color: var(--color-surface-2);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
  }
  .filter-bar-body {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
    min-inline-size: 0;
  }
  .filter-bar-summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--size-control-md);
    /* The whole bar is the hit area: bleed over the form's padding. */
    margin: calc(var(--space-3) * -1) calc(var(--space-4) * -1);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    color: var(--color-text-primary);
    font-size: var(--text-base);
    font-weight: var(--font-weight-semibold);
    line-height: var(--leading-tight);
    list-style: none;
    cursor: pointer;
  }
  .filter-bar-summary::-webkit-details-marker {
    display: none;
  }
  .filter-bar-summary:hover .filter-bar-summary-label {
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: var(--text-underline-offset);
  }
  .filter-bar-summary > .icon {
    margin-inline-start: auto;
    color: var(--color-text-secondary);
  }
  /* A state, not an animation: the chevron flips when the fold is open. */
  .filter-bar-fold[open] > .filter-bar-summary > .icon {
    transform: rotate(180deg);
  }
  .filter-bar-fold[open] > .filter-bar-summary {
    margin-block-end: var(--space-3);
  }
  .filter-bar-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: 1.375rem;
    block-size: 1.375rem;
    padding-inline: var(--space-1);
    border-radius: var(--radius-full);
    background-color: var(--color-accent);
    color: var(--color-accent-fg);
    font-size: var(--text-xs);
    font-weight: var(--font-weight-semibold);
    font-variant-numeric: var(--font-feature-numeric);
    line-height: 1;
  }
  .filter-bar-fold--static > .filter-bar-summary {
    display: none;
  }
  @media (min-width: 40rem) {
    @supports selector(::details-content) {
      .filter-bar-fold > .filter-bar-summary {
        display: none;
      }
      .filter-bar-fold::details-content {
        content-visibility: visible;
      }
    }
  }
  @media (forced-colors: active) {
    .filter-bar-count {
      border: var(--border-width-hairline) solid CanvasText;
    }
  }
  .filter-bar-fields {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
    flex: 1 1 auto;
    min-inline-size: 0;
  }
  .filter-bar-fields > .form-field {
    flex: 1 1 11rem;
    max-inline-size: 18rem;
  }
  /* Apply + Clear, and a table region's export pair: one wrapping row with a real gap
     (before this they sat on inline whitespace and touched when wrapped). */
  .filter-bar-actions,
  .table-region-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
  }
  @media (max-width: 39.9375rem) {
    .filter-bar-fields > .form-field {
      max-inline-size: none;
    }
  }

  /* --- Alerts and toasts (components.md section 17) ----------------- */
  .alert {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: var(--border-width-hairline) solid var(--color-info-border);
    border-radius: var(--radius-md);
    background-color: var(--color-info-bg);
    color: var(--color-info-fg);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .alert > .icon {
    margin-block-start: var(--space-0-5);
  }
  .alert > p {
    grid-column: 2;
    color: inherit;
  }
  .alert > .alert-dismiss {
    grid-column: 3;
    grid-row: 1;
    margin-block: calc(var(--space-1) * -1);
    margin-inline-end: calc(var(--space-2) * -1);
    color: inherit;
  }
  .alert--success { background-color: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success-fg); }
  .alert--warning { background-color: var(--color-warning-bg); border-color: var(--color-warning-border); color: var(--color-warning-fg); }
  .alert--danger { background-color: var(--color-danger-bg); border-color: var(--color-danger-border); color: var(--color-danger-fg); }
  .alert-dismiss:hover {
    background-color: var(--color-surface-1);
  }
  .toast-region {
    position: fixed;
    inset-block-end: var(--space-6);
    inset-inline-end: var(--layout-gutter);
    z-index: var(--z-toast);
    display: grid;
    gap: var(--space-2);
    inline-size: min(24rem, calc(100vw - var(--layout-gutter) * 2));
    pointer-events: none;
  }
  .toast-region > .alert {
    pointer-events: auto;
    background-color: var(--color-surface-1);
    border-color: var(--color-border-subtle);
    box-shadow: var(--shadow-2);
    color: var(--color-text-primary);
  }
  .toast-region > .alert--info > .icon { color: var(--color-info-fg); }
  .toast-region > .alert--success > .icon { color: var(--color-success-fg); }
  .toast-region > .alert--warning > .icon { color: var(--color-warning-fg); }
  .toast-region > .alert--danger > .icon { color: var(--color-danger-fg); }
  @media (min-width: 64rem) {
    body:has(#copilot-panel[data-state="open"]) .toast-region {
      inset-inline-end: calc(var(--size-copilot-panel) + var(--layout-gutter));
    }
  }

  /* --- Confirm dialog (components.md section 18) -------------------- */
  .confirm-dialog {
    inline-size: min(32rem, calc(100vw - var(--layout-gutter-sm) * 2));
    max-block-size: calc(100vh - var(--space-8));
    padding: var(--space-6);
    background-color: var(--color-surface-1);
    border: 0;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-3);
    color: var(--color-text-primary);
    transform-origin: 50% 50%;
  }
  .confirm-dialog::backdrop {
    background-color: var(--color-surface-overlay);
  }
  .confirm-dialog > h2 {
    margin-block-end: var(--space-2);
  }
  .confirm-dialog > p {
    max-inline-size: var(--size-measure);
    color: var(--color-text-secondary);
  }
  .confirm-dialog > label {
    display: block;
    margin-block-start: var(--space-4);
    margin-block-end: var(--space-1);
  }
  .confirm-dialog > .form-hint {
    margin-block-start: var(--space-1);
  }
  .confirm-dialog > menu {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-block-start: var(--space-6);
  }

  /* --- Disclosure (components.md section 19) ------------------------ */
  details[data-behavior~="disclosure"] > summary,
  .disclosure > summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--size-touch-min);
    color: var(--color-link);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    list-style-position: inside;
  }
  details[data-behavior~="disclosure"] > summary:hover {
    color: var(--color-link-hover);
    text-decoration-line: underline;
  }
  details[data-behavior~="disclosure"][open] > summary {
    margin-block-end: var(--space-3);
  }

  /* Menu (<details class="menu">, motion-spec.md M-22) */
  .menu {
    position: relative;
  }
  .menu > summary {
    list-style: none;
    cursor: pointer;
  }
  .menu > summary::-webkit-details-marker {
    display: none;
  }
  .menu-popup {
    position: absolute;
    inset-block-start: calc(100% + var(--space-1));
    inset-inline-end: 0;
    z-index: var(--z-dropdown);
    display: grid;
    min-inline-size: 12rem;
    padding: var(--space-1);
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-2);
    transform-origin: top;
  }
  .menu-popup :is(a, button) {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--size-control-sm);
    padding: var(--space-1) var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background-color: transparent;
    color: var(--color-text-primary);
    font-size: var(--text-base);
    text-align: start;
    text-decoration: none;
  }
  .menu-popup :is(a, button):hover {
    background-color: var(--color-surface-hover);
  }

  /* --- Tabs (components.md section 20) ------------------------------ */
  .tabs {
    display: flex;
    gap: var(--space-1);
    overflow-x: auto;
    border-block-end: var(--border-width-hairline) solid var(--color-border-subtle);
    scrollbar-width: thin;
  }
  .tabs a {
    flex: none;
    display: inline-flex;
    align-items: center;
    min-block-size: var(--size-control-md);
    padding: 0 var(--space-3);
    margin-block-end: calc(var(--border-width-hairline) * -1);
    border-block-end: var(--border-width-strong) solid transparent;
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
  }
  .tabs a:hover {
    color: var(--color-text-primary);
    border-block-end-color: var(--color-border-strong);
  }
  .tabs a[aria-current="page"] {
    color: var(--color-text-primary);
    border-block-end-color: var(--color-accent);
    font-weight: var(--font-weight-semibold);
  }

  /* --- SoD control, bank account, audit timeline (sections 23-25) --- */
  .sod-reason {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    background-color: var(--color-warning-bg);
    border: var(--border-width-hairline) solid var(--color-warning-border);
    border-radius: var(--radius-sm);
    color: var(--color-warning-fg);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .sod-reason .icon {
    inline-size: var(--size-icon-sm);
    block-size: var(--size-icon-sm);
    margin-block-start: var(--space-0-5);
  }
  .sod-reason--override {
    background-color: var(--color-info-bg);
    border-color: var(--color-info-border);
    color: var(--color-info-fg);
  }
  .bank-account-field {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-4);
  }
  /* P4 D2-08/D4-16: the masked number and its Reveal button (or the revealed number
     and its note) stack with a gap instead of touching. */
  .bank-account {
    display: grid;
    gap: var(--space-2);
    justify-items: start;
  }
  .bank-account-masked {
    font-family: var(--font-family-mono);
    font-variant-numeric: slashed-zero tabular-nums;
    letter-spacing: var(--tracking-wide);
  }
  .bank-account-beneficiary,
  .bank-account-indicator {
    color: var(--color-text-secondary);
    font-size: var(--text-base);
  }
  .audit-timeline {
    display: grid;
  }
  .audit-timeline-item {
    display: grid;
    gap: var(--space-0-5);
    padding-block: var(--space-3);
    border-block-end: var(--border-width-hairline) solid var(--color-border-subtle);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .audit-timeline-item:last-child {
    border-block-end: 0;
  }
  .audit-timeline-item > time {
    color: var(--color-text-muted);
    font-size: var(--text-xs);
  }
  .audit-timeline-detail,
  .audit-timeline-reason {
    color: var(--color-text-secondary);
  }

  /* --- Login (R07, R09, R10, AC-UI-30): flat canvas, wordmark, form, 0 raster images --- */
  /* .login-page centres .login-card directly; .login-brand and .login-main are optional. */
  .login-page {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: center;
    justify-items: center;
    gap: var(--space-6);
    min-block-size: 100vh;
    padding: var(--space-8) var(--layout-gutter-sm);
    background-color: var(--color-bg-canvas);
  }
  .login-page:has(> .login-brand) {
    align-content: start;
    padding-block-start: 0;
  }
  .login-main {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: var(--space-4);
    inline-size: 100%;
  }
  /* Optional claret brand band/panel (design-direction.md section 15). Flat fill, no imagery. */
  .login-brand {
    display: grid;
    align-content: center;
    gap: var(--space-2);
    justify-self: stretch;
    margin-inline: calc(var(--layout-gutter-sm) * -1);
    padding: var(--space-6) var(--layout-gutter-sm);
    background-color: var(--color-brand);
    color: var(--color-brand-fg);
  }
  .login-brand .login-wordmark {
    color: var(--color-brand-fg);
  }
  .login-brand-line {
    max-inline-size: 28ch;
    color: var(--color-brand-fg);
    font-size: var(--text-md);
    line-height: var(--leading-normal);
  }
  .login-card {
    display: grid;
    gap: var(--space-6);
    inline-size: min(100%, 26rem);
    padding: var(--space-8) var(--space-6);
    background-color: var(--color-surface-1);
    border: var(--border-width-hairline) solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-1);
  }
  .login-card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
  }
  .login-wordmark {
    display: block;
    font-family: var(--font-family-display);
    font-size: var(--text-xl);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--tracking-tight);
    color: var(--color-brand);
    text-decoration: none;
  }
  .login-headline {
    font-family: var(--font-family-display);
    font-size: var(--text-3xl);
    line-height: var(--leading-tight);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--tracking-tight);
    font-variant-numeric: lining-nums proportional-nums;
    color: var(--color-text-primary);
  }
  .login-intro {
    color: var(--color-text-secondary);
    font-size: var(--text-md);
    line-height: var(--leading-normal);
    max-inline-size: var(--size-measure);
  }
  .login-form {
    display: grid;
    gap: var(--space-4);
  }
  .login-form .button {
    inline-size: 100%;
    min-block-size: var(--size-control-lg);
  }
  .login-footer {
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
    text-align: center;
  }
  @media (max-width: 39.9375rem) {
    .login-headline { font-size: var(--text-2xl); }
    .login-card { padding: var(--space-6) var(--space-5); }
  }
  @media (min-width: 64rem) {
    .login-page:has(> .login-brand) {
      grid-template-columns: minmax(18rem, 28rem) minmax(0, 1fr);
      align-content: stretch;
      gap: 0;
      padding: 0;
    }
    .login-page:has(> .login-brand) > .login-main {
      padding: var(--space-8) var(--layout-gutter);
    }
    .login-brand {
      align-content: end;
      margin-inline: 0;
      padding: var(--space-12) var(--space-10);
    }
    .login-brand .login-wordmark {
      font-size: var(--text-3xl);
    }
  }

  /* === Motion (docs/design/motion.md) ================================
   * transform and opacity only; durations and easings are tokens only.
   * The resting state of every element is fully visible (motion-spec.md 1.2). */

  /* M-02 non-figure fragment settle */
  [data-motion="settle"] { transition: opacity var(--motion-duration-fast) var(--motion-ease-standard); }
  [data-motion="settle"].htmx-added { opacity: 0; }

  /* M-04 request indicator (replaces htmx's injected styles; includeIndicatorStyles:false, R46) */
  .htmx-indicator { opacity: 0; transition: opacity var(--motion-duration-fast) var(--motion-ease-standard); }
  .htmx-request .htmx-indicator,
  .htmx-request.htmx-indicator { opacity: 1; transition-delay: var(--motion-duration-fast); }

  /* M-05 skeleton rows, M-18 copilot thinking bar */
  .skeleton-block,
  .copilot-thinking-bar { animation: mot-pulse calc(var(--motion-duration-slow) * 3) var(--motion-ease-standard) infinite alternate; }
  @keyframes mot-pulse { from { opacity: 1; } to { opacity: 0.55; } }

  /* M-11 AI suggestion arrives, M-12 outcome replaces it */
  .ai-card { transition: opacity var(--motion-duration-base) var(--motion-ease-enter), transform var(--motion-duration-base) var(--motion-ease-enter); }
  .ai-card.htmx-added:not(.ai-card--decided) { opacity: 0; transform: translateY(0.5rem); }
  .ai-card--decided { transition-duration: var(--motion-duration-fast); transition-timing-function: var(--motion-ease-standard); }
  .ai-card--decided.htmx-added { opacity: 0; transform: none; }

  /* M-13 flag badge appears in place */
  .flag-badge { transition: opacity var(--motion-duration-base) var(--motion-ease-enter); }
  .flag-badge.htmx-added { opacity: 0; }
  .flag-badge--decided { transition-duration: var(--motion-duration-fast); transition-timing-function: var(--motion-ease-standard); }
  .flag-badge--decided.htmx-added { opacity: 0; }

  /* M-14 toast enter, M-15 toast exit (no auto-dismiss, ever) */
  .toast-region > .alert { transition: opacity var(--motion-duration-base) var(--motion-ease-enter), transform var(--motion-duration-base) var(--motion-ease-enter); }
  .toast-region > .alert.htmx-added { opacity: 0; transform: translateY(0.5rem); }
  .toast-region > .alert[data-state="closing"] {
    opacity: 0; transform: translateY(0.25rem);
    transition-duration: var(--motion-duration-fast);
    transition-timing-function: var(--motion-ease-exit);
  }

  /* M-16 / M-17 copilot panel (docked at lg+) and sheet (below lg) */
  .copilot-panel[data-state="open"]    { animation: mot-rise-in var(--motion-duration-slow) var(--motion-ease-enter); }
  .copilot-panel[data-state="closing"] { animation: mot-rise-out var(--motion-duration-fast) var(--motion-ease-exit) forwards; }
  @media (min-width: 64rem) {
    .copilot-panel[data-state="open"]    { animation-name: mot-dock-in; }
    .copilot-panel[data-state="closing"] { animation-name: mot-dock-out; }
  }
  @keyframes mot-dock-in  { from { opacity: 0; transform: translateX(1.5rem); } }
  @keyframes mot-dock-out { to   { opacity: 0; transform: translateX(1.5rem); } }
  @keyframes mot-rise-in  { from { opacity: 0; transform: translateY(1.5rem); } }
  @keyframes mot-rise-out { to   { opacity: 0; transform: translateY(1.5rem); } }

  /* M-19 disclosure reveal (close is instant), M-22 menu */
  details[data-behavior~="disclosure"][open] > :not(summary) { animation: mot-reveal var(--motion-duration-fast) var(--motion-ease-enter); }
  .menu[open] > .menu-popup { animation: mot-reveal var(--motion-duration-base) var(--motion-ease-enter); }
  @keyframes mot-reveal { from { opacity: 0; transform: translateY(-0.25rem); } }

  /* M-20 off-canvas sidebar, below lg only */
  @media (max-width: 63.9375rem) {
    .sidebar-nav[data-state="open"]    { animation: mot-start-in var(--motion-duration-slow) var(--motion-ease-enter); }
    .sidebar-nav[data-state="closing"] { animation: mot-start-out var(--motion-duration-fast) var(--motion-ease-exit) forwards; }
  }
  @keyframes mot-start-in  { from { opacity: 0; transform: translateX(-1.5rem); } }
  @keyframes mot-start-out { to   { opacity: 0; transform: translateX(-1.5rem); } }

  /* M-21 confirm dialog and backdrop (close is instant) */
  .confirm-dialog[open] { animation: mot-dialog-in var(--motion-duration-base) var(--motion-ease-enter); }
  .confirm-dialog[open]::backdrop { animation: mot-fade-in var(--motion-duration-base) var(--motion-ease-standard); }
  @keyframes mot-dialog-in { from { opacity: 0; transform: scale(0.98); } }
  @keyframes mot-fade-in   { from { opacity: 0; } }
  /* A form dialog the server re-opens on load (validation errors, data-open-on-load)
     is part of first paint: nothing on first paint animates (motion.md F2). */
  .confirm-dialog[data-open-on-load="true"][open],
  .confirm-dialog[data-open-on-load="true"][open]::backdrop { animation: none; }

  /* M-27 pressed button: see .button (transform, --motion-duration-instant). */

  /* Reduced motion (motion-spec.md 8.1). tokens.css already collapses every
     --motion-duration-* to 0.01ms; this block handles what 0.01ms cannot
     (infinite loops would flash) and is a safety net for any later rule. */
  @media (prefers-reduced-motion: reduce) {
    .skeleton-block,
    .copilot-thinking-bar { animation: none; }
    *,
    *::before,
    *::after,
    *::backdrop {
      animation-duration: var(--motion-duration-instant) !important;
      animation-delay: 0s !important;
      transition-duration: var(--motion-duration-instant) !important;
      transition-delay: 0s !important;
      scroll-behavior: auto !important;
    }
  }
}

/* =====================================================================
 * UTILITIES
 * ===================================================================== */
@layer utilities {
  .visually-hidden {
    position: absolute !important;
    inline-size: 1px !important;
    block-size: 1px !important;
    padding: 0 !important;
    margin: calc(var(--border-width-hairline) * -1) !important;
    overflow: hidden !important;
    clip: rect(0 0 0 0) !important;
    clip-path: inset(50%) !important;
    white-space: nowrap !important;
    border: 0 !important;
  }
  .text-right { text-align: right; }
  .text-start { text-align: start; }
  .text-secondary { color: var(--color-text-secondary); }
  .text-muted { color: var(--color-text-muted); }
  .nowrap { white-space: nowrap; }
  .hyphens-none { hyphens: none; }
  .stack { display: grid; gap: var(--space-4); align-content: start; }
  .cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
}

/* components: ai-suggestion + flag-badge (P1-CLOSE A4) */
/* The flag badge wears the AI token family only (copilot-ux.md section 4): no warning
   amber on unconfirmed AI output. Its distinct weight is a strong dashed border, so a
   flag reads "needs a look" next to a hairline suggestion card. Decision layout for
   components/ai_suggestion.html (D-132 accept, D-133 edit-accept, D-134 reject). */
@layer components {
  .flag-badge {
    border-width: var(--border-width-strong);
    border-color: var(--color-ai-suggestion-border);
  }
  .flag-badge > .icon,
  .flag-badge-title {
    color: var(--color-ai-suggestion-fg);
  }
  .flag-badge--decided {
    border-width: var(--border-width-hairline);
    border-color: var(--color-border-subtle);
  }
  .flag-badge > .ai-card-decisions,
  .flag-badge > .ai-card-blocked-note {
    grid-column: 2;
  }
  .ai-card-decisions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-2) var(--space-3);
    min-inline-size: 0;
  }
  .ai-card-decisions .button,
  .ai-card-reject > summary {
    min-block-size: var(--size-control-lg);
  }
  .ai-card-edit-field {
    display: grid;
    gap: var(--space-1);
    min-inline-size: 0;
  }
  .ai-card-edit-field label {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--font-weight-medium);
  }
  .ai-card-edit-field select {
    min-block-size: var(--size-control-lg);
  }
  .ai-card-reject {
    flex-basis: 100%;
  }
  .ai-card-reject > form {
    display: grid;
    justify-items: start;
    gap: var(--space-2);
    max-inline-size: var(--size-measure);
  }
  .ai-card-blocked-note {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    color: var(--color-text-secondary);
    font-size: var(--text-base);
    line-height: var(--leading-snug);
  }
  .ai-card-blocked-note .icon {
    flex: none;
    inline-size: var(--size-icon-sm);
    block-size: var(--size-icon-sm);
    margin-block-start: var(--space-0-5);
  }
}

/* P3: multi-line summary text keeps the author's line breaks (textarea-sourced copy). */
@layer components {
  .summary-text {
    white-space: pre-wrap;
  }
}

/* --- P4 final polish (UI-3a, AC-UI-40) ---------------------------------------
   Shell and platform pages. Tokens only; every rule below names the finding it
   closes in docs/design/audits/P4-polish-audit.md. */
@layer components {
  /* H1: stored AI request / response JSON (audit/ai_interaction_detail.html). A <pre>
     keeps its longest line, which pushed the whole page wide at every width
     (WCAG 1.4.10). Lines wrap inside the panel; a long token breaks anywhere. */
  .code-block {
    max-inline-size: 100%;
    margin: 0;
    padding: var(--space-3) var(--space-4);
    overflow-x: auto;
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-sm);
    font-family: var(--font-family-mono);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    font-variant-numeric: slashed-zero;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }
  .table-scroll .code-block {
    padding: var(--space-2);
  }

  /* M: the 403 page header touched its panel (the wrapper hid .page-content's gap). */
  .error-page {
    display: grid;
    row-gap: var(--space-6);
    align-content: start;
  }

  /* L: a disclosure summary read as plain ruby text with no open/close cue. The
     component now carries a chevron after its label; it turns when open (a state,
     not an animation). */
  .disclosure > summary {
    list-style: none;
  }
  .disclosure > summary::-webkit-details-marker {
    display: none;
  }
  .disclosure > summary > .icon {
    inline-size: var(--size-icon-sm);
    block-size: var(--size-icon-sm);
  }
  .disclosure[open] > summary > .icon {
    transform: rotate(180deg);
  }

  /* L: a setting's effective state leads its env/runtime/effective caption as a pill. */
  .settings-state {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
  }
}

/* --- P4 final polish, design pass D1 (WS-UI-3, AC-UI-40) ---------------------
   Shell, overview, claims and claim approval. Tokens only; IDs are the D1 findings. */
@layer components {
  /* D1-03: on a phone the header row wraps, which put the primary action between the
     h1 and its description (the description read as the button's caption). Below
     40rem the row dissolves into the header grid and the action follows the text. */
  @media (max-width: 39.9375rem) {
    .page-header > .page-header-row:has(> .page-header-actions) {
      display: contents;
    }
    .page-header > .page-header-row > .page-header-actions {
      order: 1;
      margin-block-start: var(--space-1);
    }
  }

  /* D1-04: inside a .status-stack the flex gap already spaces the pills; the
     adjacency margin from records.css indented a wrapped second pill. */
  .table-region td .status-stack > .status-pill + .status-pill {
    margin-inline-start: 0;
  }

  /* D1-08: the phone search lives at the top of the menu (topbar search is hidden
     below 40rem). Exactly one of the two search forms is displayed at any width. */
  .sidebar-search {
    display: none;
    padding-inline: var(--space-3);
  }
  .sidebar-search input {
    inline-size: 100%;
  }
  @media (max-width: 39.9375rem) {
    .sidebar-search {
      display: block;
    }
  }

  /* D1-09: the standalone claim approval page has only the decisions column; the
     bills 8/4 split left an empty 8-column hole beside it at desktop widths. */
  .bill-layout--single > .bill-decisions {
    grid-column: 1 / -1;
    grid-row: auto;
    inline-size: 100%;
    max-inline-size: 40rem;
  }

  /* D1-00h: the disclosure chevron picks up the summary's link colour. */
  .disclosure > summary > .icon {
    flex: none;
  }
}
