:root {
  --teal: #009999;
  --teal-dark: #007a7a;
  --teal-light: #e6f7f7;
  --teal-tint: rgba(0, 154, 149, 0.35);  /* translucent teal: "committed but not spent" bar segments */
  --gray-dark: #4d4d4d;
  --gray-mid: #7a7a7a;
  --gray-light: #f5f5f5;
  --white: #ffffff;

  /* Neutral borders & surfaces — the recurring greys, named by role so a
     border or fill tweak is a single edit. Each value is preserved exactly
     from the literal it replaced (no visual change). */
  --border: #e8e8e8;          /* default hairline: nav, cards, panels, popovers */
  --border-input: #d0d0d0;    /* form controls: inputs, selects, textareas */
  --divider: #f0f0f0;         /* rules between list items / sections, in-table dividers */
  --surface-muted: #fafafa;   /* muted fill: table headers, hover rows, subtle cards */

  /* Text greys used by the newer review screens (invoices, travel costs,
     bank) — darker/lighter than the brand greys above, named by role. Each
     value is preserved exactly from the literal it replaced. */
  --text-strong: #111827;     /* near-black emphasis: amounts, key values */
  --text-faint: #9ca3af;      /* placeholder / empty-state text (secondary text = --gray-mid) */

  /* Height of the sticky top nav — sticky table headers offset by this so
     they pin just below the nav instead of sliding under it. */
  --nav-height: 64px;

  /* Semantic status colours — used app-wide so reds/greens/ambers stay
     consistent. Red is reserved for destructive/warning actions. */
  --danger: #c0392b;
  --danger-dark: #a93226;
  --danger-bg: #fdecea;
  --danger-border: #f5c0c0;
  --success: #15803d;
  --success-dark: #166534;
  --warning: #d97706;
  --warning-dark: #b45309;
  --success-bg: #ecfdf5;
  --warning-bg: #fef9e7;

  /* Status-badge palettes (badges.css .status-*) — also used for row accents,
     banners and transition buttons, so the whole flow reads in one colour
     language. Values preserved exactly from the literals they replaced.
     Optional slots per palette: -border (tinted button border) and -bg-hover
     (tinted button hover fill). Some slots share a value with another
     palette's — the alias keeps the role while holding the number of distinct
     colours down. */
  --info: #0066cc;            /* processing / ongoing / open */
  --info-bg: #e8f4ff;
  --info-bg-hover: #d4e9ff;
  --review: #b85c00;          /* in-review warm orange */
  --review-bg: #fff3e0;
  --review-border: #f7ddb8;
  --review-bg-hover: #ffe8c7;
  --approved: #007a52;        /* approved / paid / verified badge green */
  --approved-bg: #e6f7f2;
  --approved-bg-hover: #d4f0e6;
  --pending: #b07d00;         /* pending golden amber */
  --pending-bg: var(--warning-bg);
  --requested: #1d4ed8;       /* verification-requested deep blue (cashbook flow) */
  --requested-bg: var(--info-bg);
  --requested-border: #c6d8fb;
  --requested-bg-hover: var(--info-bg-hover);
  --invoiced: #5b21b6;        /* handed to the donor / third party (purple) */
  --invoiced-bg: #ede9fe;
  --invoiced-bg-hover: #ddd3fc;

  /* Success/warning palette extras (same slot naming as the badges above). */
  --success-border: #b5e3cb;
  --warning-tint: #fff7ed;

  /* Indigo accent — donor "assign" actions and the lump-sum badge. */
  --indigo: #4338ca;
  --indigo-dark: #3730a3;
  --indigo-bg: #eef2ff;
  --indigo-bg-hover: #e0e7ff;

  /* Deep amber — the "corrected"/off-balance brown-amber, darker than
     --warning-dark. */
  --amber-dark: #854d0e;
  --amber-bg: #fef9c3;

  /* Gold — "confirm before you proceed" notices (LERF confirm/export
     warnings, travel reconfirm banners). Distinct from the amber warnings. */
  --gold: #d4a017;            /* accent bars, icons, flags */
  --gold-bright: #f0c040;     /* notice borders */
  --gold-soft: #f3dca0;       /* pale notice borders */
  --gold-dark: #8a6a0c;       /* notice text */

  /* Budget overflow — deeper orange than the amber warnings, so an
     over-budget row reads as its own severity between amber and red. */
  --overflow: #ea580c;
  --overflow-dark: #c2410c;
  --overflow-bg: #fdeee4;

  /* Teal-tinted panel border (vendor-match panels, review banner). */
  --teal-border: #d9ebe7;

  /* EU emblem blue — the EU donor badge only; not a UI colour. */
  --eu-blue: #003399;
  --eu-blue-bg: #e6e9f7;

  /* Chart/segment fills (controlling spend bar). */
  --chart-gray: var(--text-faint);
  --chart-blue: #6fb3d2;
  --chart-gold: #e0c46c;

  --dropdown-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23999' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--gray-dark);
  background: var(--white);
}

/* Nav */
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0 2.5rem;
  /* A contract, not a look: six stylesheets use --nav-height as their sticky
     `top`, so the bar must never grow. The link row scrolls instead. */
  height: var(--nav-height);
  background: var(--white);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 100;
}

.nav-brand {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  text-decoration: none;
}

.nav-brand img {
  height: 36px;
  width: 36px;
}

.nav-brand-name {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--gray-dark);
  text-transform: uppercase;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 2rem;
  /* Role-dependent width, wider than a tablet. min-width lets the row shrink
     and scroll instead of forcing a floor the whole page stretches to. */
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.nav-links::-webkit-scrollbar {
  display: none;
}

/* Nothing in the bar may wrap to a second line — the bar is a fixed height, so
   a two-line item would spill out of it. */
.nav-links > * {
  white-space: nowrap;
  flex-shrink: 0;
}

/* Below this an admin's links + email + button stop fitting, and Sign out must
   stay reachable without scrolling. The email is the only non-control here. */
@media (max-width: 1000px) {
  .nav-user {
    display: none;
  }
}

.nav-links a {
  text-decoration: none;
  color: var(--gray-mid);
  font-size: 0.9rem;
  transition: color 0.15s;
}

.nav-links a:hover {
  color: var(--teal);
}

.btn {
  display: inline-block;
  padding: 0.55rem 1.25rem;
  border-radius: 4px;
  font-size: 0.9rem;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.15s, color 0.15s;
  cursor: pointer;
  /* Same width as .btn-outline's border, so filled and outline buttons match in height. */
  border: 1.5px solid transparent;
}

.btn-primary {
  background: var(--teal);
  color: var(--white);
}

.btn-primary:hover {
  background: var(--teal-dark);
  color: var(--white);
}

.btn-outline {
  background: transparent;
  color: var(--teal);
  border: 1.5px solid var(--teal);
}

.btn-outline:hover {
  background: var(--teal-light);
}

/* ── Danger button (destructive text actions) ──────────────────
   Standard red CTA, e.g. "Confirm rejection". Reserve for destructive
   actions so red consistently signals danger. */
.btn-danger {
  background: var(--danger);
  color: var(--white);
}

.btn-danger:hover {
  background: var(--danger-dark);
  color: var(--white);
}

/* ── Inline SVG icons ──────────────────────────────────────────
   Rendered by IconsHelper#icon. Size to the surrounding text and inherit
   colour so they recolour with their button/link. */
.icon {
  width: 1.05em;
  height: 1.05em;
  display: inline-block;
  vertical-align: -0.15em;
  flex: none;
}

/* ── Icon-only action buttons (edit / view / delete) ───────────
   The standard square icon button used for row + header actions. Promoted
   here from bank.css so every feature gets the same edit/view/delete UI.
   button_to wraps the <button> in a <form>; keep that form inline so the
   buttons sit in a row. */
.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--gray-mid);
  width: 2rem;
  height: 2rem;
  border-radius: 4px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  text-decoration: none;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  /* <button> doesn't inherit font-size by default; without this the em-sized
     .icon renders smaller in a button than in an <a>, making grouped edit/
     delete icons look mismatched. Inherit so every icon is the same size. */
  font: inherit;
}

.btn-icon:hover {
  background: var(--teal-light);
  color: var(--teal);
  border-color: var(--teal);
}

/* Destructive variant — red on hover so delete/remove reads as dangerous. */
.btn-icon-danger:hover {
  background: var(--danger-bg);
  color: var(--danger);
  border-color: var(--danger-border);
}

/* button_to renders <form><button></form>; collapse the form so grouped
   icon buttons line up. */
form.button_to { display: inline-block; margin: 0; }

/* Wrapper to group row/header action buttons with consistent spacing.
   Vertically centres mixed icon + text buttons so they share a baseline. */
.icon-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

/* ── Pagination ────────────────────────────────────────────── */
.pagination-bar {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 1.5rem;
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--divider);
}

.pagination-count {
  font-size: 0.8rem;
  color: var(--gray-mid);
  white-space: nowrap;
}

.pagination-bar nav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.pagination-bar a,
.pagination-bar span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.5rem;
  border-radius: 4px;
  font-size: 0.85rem;
  text-decoration: none;
  transition: background 0.15s, color 0.15s;
}

.pagination-bar a {
  color: var(--gray-dark);
  border: 1px solid var(--border);
}

.pagination-bar a:hover { background: var(--teal-light); border-color: var(--teal); color: var(--teal); }

.pagination-bar span.current {
  background: var(--teal);
  color: var(--white);
  border: 1px solid var(--teal);
  font-weight: 600;
}

.pagination-bar span.disabled {
  color: var(--text-faint);
  border: 1px solid var(--border);
  cursor: default;
}

/* ── Form actions + errors ─────────────────────────────────── */
.form-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding-top: 0.5rem;
}

.form-actions input[type="submit"] {
  padding: 0.55rem 1.25rem;
  border-radius: 4px;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  border: none;
  background: var(--teal);
  color: var(--white);
  transition: background 0.15s;
  font-family: inherit;
}

.form-actions input[type="submit"]:hover { background: var(--teal-dark); }

.form-errors {
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  border-radius: 4px;
  padding: 0.875rem 1.25rem;
  margin-bottom: 1.5rem;
  font-size: 0.88rem;
  color: var(--danger);
  /* Keep scrollIntoView from parking the banner under the sticky nav. */
  scroll-margin-top: calc(var(--nav-height) + 1rem);
}

.form-errors ul {
  margin-top: 0.5rem;
  padding-left: 1.25rem;
}

/* Client-side "what's missing" summary (submit_guard_controller). Compact: a
   short heading over the missing field names as pill chips, not a wall of
   bullets. */
.submit-guard-errors {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  border-left: 3px solid var(--danger);
  border-radius: 6px;
  padding: 0.8rem 1rem;
  margin-bottom: 1.5rem;
}

.submit-guard-errors-title {
  margin: 0;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--danger);
}

.submit-guard-errors ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.submit-guard-errors li {
  font-size: 0.78rem;
  color: var(--danger);
  background: var(--white);
  border: 1px solid var(--danger-border);
  border-radius: 99px;
  padding: 0.15rem 0.65rem;
}

/* ── Form fields ───────────────────────────────────────────────
   Shared form primitives used across every feature's forms (invoices,
   cashbook, budget lines, ToRs, accounts…). Live in base so any feature
   file can override at equal specificity, and so feature files don't have
   to load in a particular order to pick them up. */
.form-field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

/* The HTML `hidden` attribute is overridden by any selector that sets
   `display` (e.g. .form-field uses display: flex). We rely on `hidden` to
   collapse manual-rate fields on cashbook new/edit until the toggle-field
   Stimulus controller flips them on, so force the attribute to win. */
[hidden] { display: none !important; }

/* Read by screen readers, not shown — e.g. the label of an icon-only column. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

.form-field label {
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--gray-dark);
}

.required-legend {
  margin: 0 0 1rem;
}

.form-field input,
.form-field textarea,
.form-field select {
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border-input);
  border-radius: 4px;
  font-size: 0.9rem;
  color: var(--gray-dark);
  font-family: inherit;
  /* Editable fields are white. Native <select> would otherwise render with a
     grey system background, which reads as "disabled" — keep all editable
     controls white so grey can mean one thing only: not editable (see :disabled
     below). */
  background: var(--white);
  transition: border-color 0.15s, box-shadow 0.15s;
  outline: none;
  width: 100%;
}

/* <select> drops the native chrome (incl. the system arrow) so it matches the
   white inputs, then draws the shared custom arrow. */
.form-field select {
  appearance: none;
  background-image: var(--dropdown-arrow);
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  padding-right: 2.25rem;
}

.form-field input:focus,
.form-field textarea:focus,
.form-field select:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(0, 153, 153, 0.12);
}

/* Disabled / locked fields (e.g. the budget line once a ToR line item is
   chosen): grey fill, muted text, no caret — the single, consistent signal
   that a field can't be edited. */
.form-field input:disabled,
.form-field textarea:disabled,
.form-field select:disabled {
  background-color: var(--gray-light);
  color: var(--gray-mid);
  cursor: not-allowed;
}

/* A read-only field VALUE shown where an input would otherwise sit (e.g. a
   carried-over quantity that must not be edited). Same box + grey fill as a
   disabled field — the app's one signal for "not editable" — with the value in
   normal text and an optional trailing note muted. It wraps rather than
   spills out of a narrow grid column: the note drops below the value, and a
   long unbroken value (an e-mail address) breaks. */
.form-readonly {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.5rem;
  overflow-wrap: anywhere;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border-input);
  border-radius: 4px;
  font-size: 0.9rem;
  color: var(--gray-dark);
  background-color: var(--gray-light);
}
.form-readonly-note { font-size: 0.78rem; color: var(--gray-mid); }

/* Native file inputs render an unstyled OS button ("Choose File" / "Browse…").
   Give every file picker the app's compact outline-button look so it matches our
   buttons instead of standing out as a system control. */
input[type="file"]::file-selector-button {
  margin-right: 0.75rem;
  padding: 0.35rem 0.9rem;
  border: 1.5px solid var(--teal);
  border-radius: 4px;
  background: var(--teal-light);
  color: var(--teal);
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
input[type="file"]::file-selector-button:hover {
  background: var(--teal);
  color: var(--white);
}

.form-field textarea { resize: vertical; }

.form-field--full  { grid-column: 1 / -1; }
.form-field--small { min-width: 0; }
.form-field--grow  { min-width: 0; }

/* ── Inline line-item rows ─────────────────────────────────────
   A flex row (fields grid + Remove button) that wraps instead of
   overflowing. Used by the invoices and ToR builders via the line-items
   Stimulus controller. Each feature sets its own grid-template-columns on
   .line-item-fields (invoices in invoices.css, ToRs via .line-item-fields--tor). */
.line-item-row {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--divider);
}

.line-item-row:last-child { border-bottom: none; }

.line-item-fields {
  display: grid;
  gap: 0.75rem;
  flex: 1;
  min-width: 0;
}

.btn-remove-line-item {
  background: none;
  border: none;
  color: var(--gray-mid);
  font-size: 0.8rem;
  cursor: pointer;
  padding: 0.35rem 0.5rem;
  border-radius: 4px;
  transition: color 0.15s, background 0.15s;
  white-space: nowrap;
  margin-top: 1.6rem;
}

.btn-remove-line-item:hover {
  color: var(--danger);
  background: var(--danger-bg);
}

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}

/* ── Mobile (≤ 600px) ──────────────────────────────────────── */
@media (max-width: 600px) {
  /* Nav — set the token, not the height: everything that sticks below the nav
     offsets itself by --nav-height, so a shorter bar has to move those too. */
  :root {
    --nav-height: 56px;
  }

  .nav {
    padding: 0 1rem;
  }

  .nav-brand-name { display: none; }

  /* .nav-user is already hidden from 1000px down. */

  .nav-links { gap: 0.75rem; }

  /* Page containers */
  .invoice-show,
  .project-show {
    padding: 1.5rem 1rem;
  }

  .dashboard { padding: 1.5rem 1rem; }

  /* Invoice header — stack vertically */
  .invoice-header {
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
  }

  .invoice-header h1 { font-size: 1.25rem; }

  .invoice-header-right {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  /* Project header — stack vertically */
  .project-header {
    flex-direction: column;
    gap: 0.5rem;
    margin-bottom: 1rem;
  }

  .project-header h1 { font-size: 1.25rem; }

  /* Expert/general header actions */
  .header-actions {
    flex-wrap: wrap;
    align-items: center;
  }

  /* Detail cells — 2-column grid on mobile */
  .invoice-details-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }

  .detail-cell {
    border-right: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
  }

  .detail-cell:nth-child(even) { border-right: none; }

  /* Review banner */
  .review-banner-actions {
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  /* Assign form — stack on very small screens */
  .assign-form {
    flex-wrap: wrap;
  }

  .assign-form select {
    width: 100%;
  }

  /* Form sections — tighter padding */
  .form-section { padding: 1rem; }
  .dashboard-section { padding: 1rem; }
}

/* ── Disabled buttons ──────────────────────────────────────── */
.btn:disabled,
.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* Looks disabled, but stays clickable — clicking explains what's missing
   (submit_guard_controller). Keep pointer-events so the click is received. */
.btn--blocked {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── Attachments popover ───────────────────────────────────── */
.col-attachments {
  width: 4rem;
  text-align: center;
  white-space: nowrap;
}

.attachments-trigger {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  color: var(--gray-mid);
  cursor: pointer;
  font-family: inherit;
  transition: color 0.15s;
}

.attachments-trigger:hover {
  color: var(--teal);
}

.attachments-popover {
  position: fixed;
  margin: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem 1.25rem;
  background: var(--white);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
  min-width: 220px;
  max-width: 380px;
}

.attachments-popover::backdrop {
  background: transparent;
}

.attachments-popover-title {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-mid);
  margin-bottom: 0.6rem;
}

.attachments-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.attachment-link {
  display: block;
  font-size: 0.88rem;
  color: var(--teal);
  text-decoration: none;
  word-break: break-word;
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--divider);
}

.attachment-link:last-child { border-bottom: none; }
.attachment-link:hover { text-decoration: underline; }

.no-attachments {
  color: var(--text-faint);
  font-size: 0.85rem;
}

/* ── Flexible Remuneration overflow (shared: cashbook show, invoice review) ── */
.overflow-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  justify-content: space-between;
  margin: 1rem 0;
  padding: 0.85rem 1rem;
  border: 1px solid var(--danger);
  border-radius: 6px;
  background: var(--danger-bg);
}
.overflow-banner-text { flex: 1 1 18rem; font-size: 0.9rem; }
.overflow-banner-actions { display: flex; gap: 0.5rem; align-items: center; }

.overflow-review {
  margin: 1rem 0;
  padding: 1rem;
  border: 1px solid var(--danger);
  border-radius: 6px;
  background: var(--danger-bg);
}
.overflow-review-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0;
  border-top: 1px solid var(--danger-border);
}
.overflow-review-info { display: flex; flex-direction: column; }

.overflow-modal {
  border: none;
  border-radius: 8px;
  padding: 1.5rem;
  max-width: 32rem;
  width: 90%;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}
.overflow-modal::backdrop { background: rgba(0, 0, 0, 0.4); }
.overflow-modal .form-field { margin: 0.75rem 0; }
.overflow-modal .form-field label { display: block; margin-bottom: 0.25rem; font-weight: 600; font-size: 0.85rem; }
.overflow-modal textarea { width: 100%; }
.overflow-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 1rem;
}

.overflow-chip {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  white-space: nowrap;
  color: var(--danger);
  background: var(--danger-bg);
}
.overflow-chip--flex {
  color: var(--teal);
  background: var(--teal-light);
}

/* Inline help marker (INT-84): a small ⓘ that reveals explanatory copy in an
   in-page CSS tooltip on hover/focus. Shared by field labels and table column
   headers across the app — see the `info_tip` helper, which puts the copy in
   `data-tip`. The bubble is rendered in-page (not the native `title` tooltip)
   so it appears instantly, can be styled, is keyboard-reachable, and shows in
   screenshots. Kept lightweight (weight: normal) so it doesn't bolden table
   headers. */
.info-tip {
  position: relative;
  display: inline-block;
  vertical-align: baseline;
  line-height: 1;
  cursor: help;
  /* Reset the native <button> chrome so the marker reads as a plain inline ⓘ. */
  margin: 0 0 0 0.3rem;
  padding: 0;
  border: 0;
  background: none;
  appearance: none;
  -webkit-appearance: none;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: normal;
  color: var(--gray-mid);
  white-space: nowrap;
  user-select: none;
}
.info-tip:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 1px;
  border-radius: 2px;
}

/* The bubble: opens below the marker (avoids being clipped by the top edge of
   scrollable table wrappers) and carries the copy via content: attr(data-tip).
   Hidden with display: none, not visibility — a merely invisible bubble keeps
   its box, so a marker near an edge widens the page into a sideways scroll. */
.info-tip::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 260px;
  white-space: normal;
  text-align: left;
  font-size: 0.78rem;
  font-weight: normal;
  line-height: 1.35;
  color: var(--white);
  background: var(--gray-dark);
  padding: 0.5rem 0.65rem;
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
  z-index: 1000;
  display: none;
  pointer-events: none;
}
/* Little arrow pointing up at the marker. */
.info-tip::before {
  content: "";
  position: absolute;
  top: calc(100% + 3px);
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-bottom-color: var(--gray-dark);
  z-index: 1000;
  display: none;
  pointer-events: none;
}
.info-tip:hover::after,
.info-tip:hover::before,
.info-tip:focus::after,
.info-tip:focus::before,
.info-tip:focus-visible::after,
.info-tip:focus-visible::before {
  display: block;
}

/* Open the bubble leftward (right edge anchored to the marker) for markers in
   the right-edge columns of a scrollable table, so the .data-table-wrap overflow
   doesn't clip it (INT-84 #2). The arrow stays under the marker. */
.info-tip--left::after {
  left: auto;
  right: 0;
  transform: none;
}
.info-tip--left::before {
  left: auto;
  right: 4px;
  transform: none;
}

/* Mirror of --left: open the bubble rightward (left edge anchored to the marker)
   for markers near the left edge of a scrollable container, so a centered bubble
   doesn't overflow the container's left side. The arrow stays under the marker. */
.info-tip--right::after {
  left: 0;
  right: auto;
  transform: none;
}
.info-tip--right::before {
  left: 4px;
  right: auto;
  transform: none;
}

