/* ============================================================================
   Expanding Ranks, components
   ----------------------------------------------------------------------------
   Load FOURTH and last. Depends on all three files above it.

   Rules this file is written under, each of which has a check in
   scripts/check-design-system.py:

     - No raw hex. Every colour comes from a role in 02-system-tokens.css.
     - No raw ramp token either. A component reaching for
       --color-oxford-navy-700 has made a decision that belongs in the token
       file.
     - No spacing value that is not a --space-* token.
     - No z-index integer. Every layer reads a --z-* token.
     - No duration or easing literal. Every one reads a --dur-* or --ease-*.
     - No media query breakpoint outside the five declared in section 4 of
       02-system-tokens.css.
     - No em dash and no en dash, including in these comments.

   Every interactive element in this file has, and is demonstrated in
   site/styleguide.html with: default, hover, focus-visible, active, disabled,
   and where it applies loading and error. A state that is not demonstrated is
   a state nobody has looked at. A disabled text input that renders identically
   to an enabled one is the specific bug that rule exists to prevent.
   ========================================================================== */

/* ============================================================================
   1. BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  line-height: var(--lead-base);
  -webkit-font-smoothing: antialiased;
}

/* Numbers never jitter when a value changes width. This product's output is
   counts, so a column that shifts from 9 to 10 reads as broken. DM Sans
   carries an optical size axis: leave font-optical-sizing alone. */
table, .er-tabular, .er-metric__value, .er-rank__value, .er-count {
  font-variant-numeric: tabular-nums lining-nums;
}

img, svg, video { max-width: 100%; height: auto; }

/* ============================================================================
   2. TYPE BUNDLES

   Size, leading, family, weight and tracking travel together. Setting
   font-size on its own is how a type system comes apart, so these are applied
   as one class and the check script fails a rule that sets a size without a
   leading.

   Headings never skip a level. A section with no heading of its own promotes
   its card titles rather than leaving an h1 to h3 gap.
   ========================================================================== */

.er-t-display,
.er-t-h1, .er-t-h2, .er-t-h3 {
  font-family: var(--font-display);
  color: var(--color-text);
  margin: 0;
}

.er-t-display { font-size: var(--text-4xl); line-height: var(--lead-4xl); letter-spacing: var(--track-tight);
                font-variation-settings: "wght" var(--wght-bold), "wdth" var(--wdth-display); }
.er-t-h1      { font-size: var(--text-3xl); line-height: var(--lead-3xl); letter-spacing: var(--track-tight);
                font-variation-settings: "wght" var(--wght-semibold), "wdth" var(--wdth-display); }
.er-t-h2      { font-size: var(--text-2xl); line-height: var(--lead-2xl); letter-spacing: var(--track-tight);
                font-variation-settings: "wght" var(--wght-semibold), "wdth" var(--wdth-display); }
.er-t-h3      { font-size: var(--text-xl);  line-height: var(--lead-xl);  letter-spacing: var(--track-normal);
                font-variation-settings: "wght" var(--wght-semibold), "wdth" var(--wdth-display); }

.er-t-lede    { font-family: var(--font-ui); font-size: var(--text-lg); line-height: var(--lead-lg);
                color: var(--color-text-muted); max-width: var(--measure); margin: 0; }
.er-t-body    { font-family: var(--font-ui); font-size: var(--text-base); line-height: var(--lead-base);
                max-width: var(--measure); margin: 0; }
.er-t-sm      { font-family: var(--font-ui); font-size: var(--text-sm); line-height: var(--lead-sm); margin: 0; }
.er-t-xs      { font-family: var(--font-ui); font-size: var(--text-xs); line-height: var(--lead-xs);
                color: var(--color-text-muted); margin: 0; }

/* Uppercase micro-label. An eyebrow is a span or a p, never a heading, or it
   becomes the first heading on the page ahead of the h1. */
.er-t-label {
  font-family: var(--font-ui);
  font-size: var(--text-3xs);
  line-height: var(--lead-3xs);
  font-weight: var(--wght-semibold);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0;
}

.er-t-mono { font-family: var(--font-mono); font-size: var(--text-sm); line-height: var(--lead-sm); }

a { color: var(--color-interactive); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--color-interactive-hover); }

/* ============================================================================
   3. FOCUS

   One focus treatment, everywhere, and it is never removed. --color-focus-ring
   measures 4.66:1 on the surface and 4.35:1 on the sunken surface, against the
   3:1 that WCAG 2.2 2.4.11 requires, and it rebinds to a cyan on the dark
   plate at 7.28:1.

   The offset is what makes it satisfy 2.4.11's requirement that the indicator
   not be obscured by the component's own fill.
   ========================================================================== */

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.er-skip-link {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: var(--z-skiplink);
  padding: var(--space-3) var(--space-4);
  background: var(--color-brand);
  color: var(--color-surface);
  border-radius: var(--radius);
  transform: translateY(calc(-100% - var(--space-8)));
  transition: transform var(--dur) var(--ease-out);
}
.er-skip-link:focus-visible { transform: translateY(0); color: var(--color-surface); }

/* ============================================================================
   4. BUTTON

   48px tall, which clears the 44px touch target with room for the focus ring.
   One filled primary per viewport.

   Press is asymmetric: 100ms down, 180ms up. The press is the feedback and a
   delay there reads as the control not having heard you. :active is never
   gated behind a hover query, because on a touch device it is the only press
   feedback a visitor gets.
   ========================================================================== */

.er-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--space-12);
  padding: 0 var(--space-6);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font-display);
  font-variation-settings: "wght" var(--wght-semibold), "wdth" var(--wdth-display);
  font-size: var(--text-base);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  position: relative;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              transform var(--dur) var(--ease-out);
}
.er-btn:active:not(:disabled):not([aria-disabled="true"]) {
  transform: scale(var(--press-scale));
  transition-duration: var(--dur-press);
}

.er-btn--primary { background: var(--color-brand); color: var(--color-surface); }
.er-btn--primary:hover:not(:disabled) { background: var(--color-brand-hover); color: var(--color-surface); }

.er-btn--secondary {
  background: var(--color-surface);
  color: var(--color-interactive);
  border-color: var(--color-border-control);
}
.er-btn--secondary:hover:not(:disabled) {
  background: var(--color-surface-sunken);
  color: var(--color-interactive-hover);
  border-color: var(--color-interactive);
}

.er-btn--quiet { background: transparent; color: var(--color-interactive); padding-inline: var(--space-3); }
.er-btn--quiet:hover:not(:disabled) { background: var(--color-hover-surface); color: var(--color-interactive-hover); }

/* The signal button. Atomic Tangerine as a FILL with dark type on it, which
   measures 6.21:1. It marks the one action that matters on a screen: call this
   candidate, fix this connector. Never more than one, and never as a generic
   confirm. A screen with two of these has none. */
.er-btn--signal { background: var(--color-signal); color: var(--color-on-signal); }
.er-btn--signal:hover:not(:disabled) { background: var(--color-signal-hover); color: var(--color-on-signal); }

.er-btn--sm { min-height: var(--space-10); padding: 0 var(--space-4); font-size: var(--text-sm); }
.er-btn--block { width: 100%; }

.er-btn:disabled,
.er-btn[aria-disabled="true"] {
  background: var(--color-surface-disabled);
  color: var(--color-text-disabled);
  border-color: transparent;
  cursor: not-allowed;
  transform: none;
}

/* Loading. The label stays, so the button does not change width and nobody
   loses what they were about to do. aria-busy carries the state; the spinner
   is decoration on top of it. This is one of the two functional loops the
   system permits. */
.er-btn[aria-busy="true"] { cursor: progress; color: transparent; }
.er-btn[aria-busy="true"]::after {
  content: "";
  position: absolute;
  width: var(--space-5);
  height: var(--space-5);
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  color: var(--color-surface);
  animation: er-spin var(--dur-spin) var(--ease-linear) infinite;
}
.er-btn--secondary[aria-busy="true"]::after,
.er-btn--quiet[aria-busy="true"]::after { color: var(--color-interactive); }
.er-btn--signal[aria-busy="true"]::after { color: var(--color-on-signal); }

@keyframes er-spin { to { transform: rotate(360deg); } }

/* Frozen at one frame the ring reads as a rendering fault rather than as busy,
   so under reduced motion it closes to a plain ring. */
@media (prefers-reduced-motion: reduce) {
  .er-btn[aria-busy="true"]::after {
    animation: none !important;
    border-top-color: currentColor;
    opacity: 0.45;
  }
}

/* Lift on hover is a transform, so it is gated to real pointers. Ungated, a
   tap fires :hover with no way to clear it and the last thing touched stays
   lifted, which looks like a bug rather than a hover. */
@media (hover: hover) and (pointer: fine) {
  .er-btn:hover:not(:disabled):not([aria-busy="true"]) { transform: translateY(-1px); }
}

/* ============================================================================
   5. FORM CONTROLS

   Every input has a real label tied with for and id. A placeholder is not a
   label: it disappears on focus, it fails 3.3.2, and it is the single most
   common accessibility defect in a product form.

   The boundary uses --color-border-control at 4.02:1, not --color-border at
   1.39:1. Putting the decorative border on a form control is the most common
   way an otherwise accessible design fails 1.4.11.
   ========================================================================== */

.er-field { display: flex; flex-direction: column; gap: var(--space-2); }
.er-field__label {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  line-height: var(--lead-sm);
  font-weight: var(--wght-medium);
  color: var(--color-text);
}
.er-field__hint { font-size: var(--text-xs); line-height: var(--lead-xs); color: var(--color-text-muted); }

/* Required is marked in words, not with a colour or a bare asterisk. */
.er-field__required { color: var(--color-text-muted); font-weight: var(--wght-regular); }

.er-input, .er-select, .er-textarea {
  width: 100%;
  min-height: var(--space-12);
  padding: 0 var(--space-4);
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: var(--text-base);   /* Never below 16px. iOS Safari zooms the
                                    viewport on focus under 16px and there is
                                    no way back out of that zoom */
  line-height: var(--lead-base);
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.er-textarea { min-height: var(--space-24); padding: var(--space-3) var(--space-4); resize: vertical; }

.er-input::placeholder, .er-textarea::placeholder { color: var(--color-text-muted); }
.er-input:hover:not(:disabled), .er-select:hover:not(:disabled), .er-textarea:hover:not(:disabled) {
  border-color: var(--color-text-muted);
}
.er-input:focus, .er-select:focus, .er-textarea:focus { border-color: var(--color-interactive); }

/* Disabled has to be visibly different from enabled. A disabled text input
   rendering byte identical to an enabled one is a real bug that a styleguide
   cannot reveal unless the state is demonstrated, so it is demonstrated. */
.er-input:disabled, .er-select:disabled, .er-textarea:disabled {
  background: var(--color-surface-disabled);
  border-color: var(--color-border);
  border-style: dashed;
  color: var(--color-text-disabled);
  cursor: not-allowed;
}

.er-input:read-only:not(:disabled) { background: var(--color-surface-sunken); }

/* Error. Tangerine plus an icon plus a message, never colour alone. The
   message sits below the field, is tied with aria-describedby, and carries
   role="alert" so it is announced when it appears. */
.er-input[aria-invalid="true"],
.er-select[aria-invalid="true"],
.er-textarea[aria-invalid="true"] {
  border-color: var(--color-signal-mark);
  border-width: 2px;
}
.er-field__error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--text-sm);
  line-height: var(--lead-sm);
  color: var(--color-signal-text);
}
.er-field__error::before { content: "!"; font-weight: var(--wght-bold); flex: none; }

.er-select {
  appearance: none;
  padding-right: var(--space-10);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - var(--space-5)) calc(50% + 1px),
                       calc(100% - var(--space-4)) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* Checkbox and radio. Built from the motif: a checkbox fills like a dot does.
   44px is the hit area; the visible box is smaller and centred in it. */
.er-check { display: flex; align-items: flex-start; gap: var(--space-3); min-height: var(--space-10); cursor: pointer; }
.er-check input {
  appearance: none;
  width: var(--space-6);    /* 24px. WCAG 2.2 2.5.8 AA floor, and the box itself
                               is a target, so the 40px label around it does not
                               satisfy the rule on its behalf. Measured at 20x20
                               before this change */
  height: var(--space-6);
  margin: var(--space-1) 0 0;
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  flex: none;
  cursor: pointer;
  display: grid;
  place-content: center;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.er-check input[type="radio"] { border-radius: 50%; }
.er-check input::before {
  content: "";
  width: var(--dot-sm);
  height: var(--dot-sm);
  border-radius: 50%;
  background: var(--color-surface);
  transform: scale(0);
  transition: transform var(--dur-fast) var(--ease-out);
}
.er-check input:checked { background: var(--color-brand); border-color: var(--color-brand); }
.er-check input:checked::before { transform: scale(1); }
.er-check input:indeterminate { background: var(--color-brand); border-color: var(--color-brand); }
.er-check input:indeterminate::before { transform: scale(1); border-radius: 1px; width: var(--space-3); height: 2px; }
.er-check input:hover:not(:disabled) { border-color: var(--color-interactive); }
.er-check input:disabled { background: var(--color-surface-disabled); border-color: var(--color-border); cursor: not-allowed; }
.er-check input:disabled + span { color: var(--color-text-disabled); }
.er-check__text { font-size: var(--text-base); line-height: var(--lead-base); }

/* Switch. Used only where the change takes effect immediately and can be
   undone by flipping it back. Anything that needs a confirm is a button. */
.er-switch { display: inline-flex; align-items: center; gap: var(--space-3); min-height: var(--space-10); cursor: pointer; }
.er-switch input {
  appearance: none;
  width: var(--space-10);
  height: var(--space-6);
  border-radius: var(--radius-pill);
  background: var(--color-switch-off);
  border: 1px solid var(--color-border-control);
  position: relative;
  cursor: pointer;
  flex: none;
  transition: background var(--dur-fast) var(--ease);
}
.er-switch input::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: var(--space-4);
  height: var(--space-4);
  border-radius: 50%;
  background: var(--color-surface);
  transition: transform var(--dur) var(--ease-out);
}
.er-switch input:checked { background: var(--color-brand); border-color: var(--color-brand); }
.er-switch input:checked::after { transform: translateX(var(--space-4)); }
.er-switch input:disabled { opacity: 0.5; cursor: not-allowed; }

/* ============================================================================
   6. CARD
   ========================================================================== */

.er-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease);
}
.er-card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.er-card__title { font-family: var(--font-display); font-size: var(--text-xl); line-height: var(--lead-xl);
                  font-variation-settings: "wght" var(--wght-semibold), "wdth" var(--wdth-display); margin: 0; }
.er-card__foot { display: flex; align-items: center; gap: var(--space-3); margin-top: auto; padding-top: var(--space-2); }

.er-card--sunken { background: var(--color-surface-sunken); }

/* Only a card that is genuinely a link gets the interactive treatment. A card
   that merely contains a link does not, or the whole surface lies about being
   clickable. */
.er-card--link { cursor: pointer; text-decoration: none; color: inherit; }
.er-card--link:hover { border-color: var(--color-interactive); box-shadow: var(--shadow); }
@media (hover: hover) and (pointer: fine) {
  .er-card--link:hover { transform: translateY(-2px); }
}
.er-card--link { transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease),
                             transform var(--dur) var(--ease-out); }
.er-card--link:active { transform: scale(var(--press-scale)); transition-duration: var(--dur-press); }

/* A card that needs a human. The left edge is the signal, and the badge inside
   it says why in words. */
.er-card--signal { border-left: 3px solid var(--color-signal-mark); }

/* A metric. One display numeral, a label, and where it helps a rank meter. */
.er-metric { display: flex; flex-direction: column; gap: var(--space-1); }
.er-metric__value {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  line-height: var(--lead-3xl);
  font-variation-settings: "wght" var(--wght-bold), "wdth" var(--wdth-display);
  color: var(--color-brand);
}
.er-metric__label { font-size: var(--text-sm); line-height: var(--lead-sm); color: var(--color-text-muted); }

/* ============================================================================
   7. BADGE, TAG, COUNT

   A badge states a fact about a record. A tag is a label a person applied. A
   count is a number. They look different on purpose, because a recruiter needs
   to know at a glance whether something was decided by the system or typed by
   a colleague.
   ========================================================================== */

.er-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-surface-disabled);
  color: var(--color-text);
  font-size: var(--text-2xs);
  line-height: var(--lead-2xs);
  font-weight: var(--wght-semibold);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
}
.er-badge--info    { background: var(--color-info-surface); color: var(--color-text); }
.er-badge--confirm { background: var(--color-confirm-surface); color: var(--color-confirm-text); }
.er-badge--signal  { background: var(--color-signal-surface); color: var(--color-signal-text);
                     box-shadow: inset 0 0 0 1px var(--color-signal-border); }
.er-badge--quiet   { background: transparent; color: var(--color-text-muted);
                     box-shadow: inset 0 0 0 1px var(--color-border); }

/* The derived badge. An activity written by reading the phone system, the
   mailbox or the calendar rather than typed by a person. This is the product's
   most important single distinction, so it carries a dot and a word, and it is
   never the same shape as a badge a person can set. */
.er-badge--derived { background: var(--color-info-surface); color: var(--color-text); text-transform: none;
                     letter-spacing: var(--track-normal); font-weight: var(--wght-medium); }

.er-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-info-surface);
  color: var(--color-text);
  font-size: var(--text-xs);
  line-height: var(--lead-xs);
  white-space: nowrap;
}
.er-tag__remove {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  padding: 0;
  width: var(--space-6);    /* 24px, not 16px. Measured at 16x16 before this */
  height: var(--space-6);
  margin-right: calc(var(--space-1) * -1);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  border-radius: 50%;
}
.er-tag__remove:hover { color: var(--color-text); background: var(--color-hover-surface); }

.er-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--space-5);
  height: var(--space-5);
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-track);
  color: var(--color-text);
  font-size: var(--text-2xs);
  line-height: 1;
  font-weight: var(--wght-semibold);
}
.er-count--signal { background: var(--color-signal-mark); color: var(--color-surface); }

/* ============================================================================
   8. TABLE

   The most used surface in this product. A recruiter reads a list of people
   all day, so the row is the component and everything else serves it.

   Numbers are tabular and right aligned. The head is sticky. Zebra striping is
   deliberately absent: it competes with the selected and signal row states,
   which carry meaning, and a 1px divider does the same job for nothing.
   ========================================================================== */

.er-table-wrap { overflow-x: auto; border: 1px solid var(--color-border); border-radius: var(--radius); }

.er-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); line-height: var(--lead-sm); }

.er-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--color-surface-sunken);
  text-align: left;
  padding: var(--space-2) var(--space-4);  /* 8px, down from 12px, to absorb the
                                              24px minimum on the sort button
                                              below without growing the row */
  font-size: var(--text-2xs);
  line-height: var(--lead-2xs);
  font-weight: var(--wght-semibold);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}
.er-table tbody td { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--color-divider); vertical-align: middle; }
.er-table tbody tr:last-child td { border-bottom: 0; }
.er-table tbody tr { transition: background var(--dur-fast) var(--ease); }
.er-table tbody tr:hover { background: var(--color-hover-surface); }
.er-table tbody tr[aria-selected="true"] { background: var(--color-selected-surface); }

.er-table .er-num { text-align: right; font-variant-numeric: tabular-nums lining-nums; }

/* A row that needs a human. The left edge carries the signal and the row also
   carries a badge saying why, because a coloured edge alone is colour alone. */
.er-table tbody tr.is-signal td:first-child { box-shadow: inset 3px 0 0 var(--color-signal-mark); }

/* A sortable column head is a real button inside the th, so it is reachable,
   announceable and has its own focus ring. aria-sort lives on the th. */
.er-table__sort {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--space-6);   /* 24px. Measured at 71x16 before this */
}
.er-table__sort:hover { color: var(--color-text); }
.er-table thead th[aria-sort] .er-table__sort { color: var(--color-text); }

.er-table--compact tbody td { padding: var(--space-2) var(--space-4); }

@media (max-width: 768px) {
  /* The table does not shrink below its content. It scrolls, and the first
     column is frozen so a name stays visible while the columns move. */
  .er-table th:first-child, .er-table td:first-child {
    position: sticky;
    left: 0;
    z-index: var(--z-sticky-cell);
    background: var(--color-surface);
  }
  .er-table thead th:first-child { z-index: var(--z-sticky); background: var(--color-surface-sunken); }
}

/* ============================================================================
   9. NAVIGATION
   ========================================================================== */

.er-header {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-3) var(--gutter);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
.er-header__brand { display: flex; align-items: center; gap: var(--space-3); text-decoration: none; }

.er-nav { display: flex; align-items: center; gap: var(--space-1); }
.er-nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--space-10);
  padding: 0 var(--space-4);
  border-radius: var(--radius);
  font-family: var(--font-display);
  font-variation-settings: "wght" var(--wght-medium), "wdth" var(--wdth-nav);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.er-nav__link:hover { background: var(--color-hover-surface); color: var(--color-text); }

/* Current is carried by aria-current, not by a class, so the markup and the
   accessibility tree cannot disagree. The dot is the redundant, non-colour
   channel. */
.er-nav__link[aria-current="page"] { color: var(--color-brand); background: var(--color-surface-brand-tint); font-variation-settings: "wght" var(--wght-semibold), "wdth" var(--wdth-nav); }
.er-nav__link[aria-current="page"]::before {
  content: "";
  width: var(--dot-sm);
  height: var(--dot-sm);
  border-radius: 50%;
  background: var(--color-dot-filled);
  flex: none;
}

@media (max-width: 1024px) {
  .er-nav--collapse { display: none; }
}

/* A side rail of territories or pipelines. Counts sit on the right and are
   tabular, so a rail of three-digit and one-digit numbers still lines up. */
.er-rail { display: flex; flex-direction: column; gap: var(--space-1); }
.er-rail__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--space-10);
  padding: 0 var(--space-3);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--color-text);
  font-size: var(--text-sm);
  transition: background var(--dur-fast) var(--ease);
}
.er-rail__item:hover { background: var(--color-hover-surface); }
.er-rail__item[aria-current="true"] { background: var(--color-selected-surface); font-weight: var(--wght-semibold); }
.er-rail__count { margin-left: auto; }

/* ============================================================================
   10. TABS

   A real tablist. Arrow keys move between tabs, the panel is labelled by its
   tab, and only the selected tab is in the tab order.
   ========================================================================== */

.er-tabs {
  display: flex;
  gap: var(--space-1);
  border-bottom: 1px solid var(--color-border);
  /* Four tabs do not fit a 390px viewport. Measured: 442px of tablist against
     390px of screen. The tablist scrolls, the page does not. Without this the
     whole page scrolls sideways, which is the failure a reader blames on the
     content rather than on the tabs. */
  overflow-x: auto;
  scrollbar-width: thin;
  scroll-padding-inline: var(--space-4);
  /* A flex or grid item defaults to min-width: auto, which is its CONTENT
     width, so overflow-x on its own does nothing when the tablist is a flex
     child. The scroll container only works once it is allowed to be narrower
     than its contents. This pair is one fix and neither half works alone. */
  min-width: 0;
}
.er-tab { flex: none; }
.er-tab {
  appearance: none;
  border: 0;
  background: transparent;
  min-height: var(--space-12);
  padding: 0 var(--space-4);
  font-family: var(--font-display);
  font-variation-settings: "wght" var(--wght-medium), "wdth" var(--wdth-nav);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  cursor: pointer;
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  transition: color var(--dur-fast) var(--ease);
}
.er-tab:hover { color: var(--color-text); }
.er-tab[aria-selected="true"] { color: var(--color-brand); font-variation-settings: "wght" var(--wght-semibold), "wdth" var(--wdth-nav); }
.er-tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: var(--space-4);
  right: var(--space-4);
  bottom: -1px;
  height: 2px;
  background: var(--color-brand);
}
.er-tab:disabled { color: var(--color-text-disabled); cursor: not-allowed; }
.er-tabpanel { padding-top: var(--space-6); }

/* ============================================================================
   11. PAGINATION

   Pages are a rank: a row of units, one of them filled. Numerals stay, because
   a dot cannot say "page 7 of 12", and the dots would be a decoration if they
   did not also carry the position.
   ========================================================================== */

/* Wraps. Seven page buttons plus a status line is 429px against 390px of
   phone, measured. Pagination is the one control where wrapping is plainly
   correct: the numbers keep their order and nothing is hidden. */
.er-pagination { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.er-pagination__status { font-size: var(--text-sm); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
.er-page {
  appearance: none;
  min-width: var(--space-10);
  min-height: var(--space-10);
  padding: 0 var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: transparent;
  color: var(--color-interactive);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.er-page:hover:not(:disabled) { background: var(--color-hover-surface); }
.er-page[aria-current="page"] { background: var(--color-brand); color: var(--color-surface); font-weight: var(--wght-semibold); }
.er-page:disabled { color: var(--color-text-disabled); cursor: not-allowed; }

/* ============================================================================
   12. MODAL

   A real dialog element, so focus trapping, Escape and inertness come from the
   platform rather than from a script that has to be maintained. Focus returns
   to the trigger on close, and the dialog is labelled by its own heading.

   It enters by rising, never by scaling from zero and never from the side.
   ========================================================================== */

.er-modal {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-modal);
  max-width: min(var(--container-prose), calc(100vw - var(--space-8)));
  width: 100%;
}
.er-modal::backdrop { background: var(--color-scrim); }
.er-modal[open] { animation: er-rise var(--dur-slow) var(--ease-out); }

.er-modal__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4);
                  padding: var(--space-6) var(--space-6) var(--space-4); }
.er-modal__body { padding: 0 var(--space-6); max-width: var(--measure-tight); }
.er-modal__foot { display: flex; justify-content: flex-end; gap: var(--space-3);
                  padding: var(--space-6); border-top: 1px solid var(--color-divider); margin-top: var(--space-6); }
.er-modal__close {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  width: var(--space-10);
  height: var(--space-10);
  border-radius: var(--radius);
  cursor: pointer;
  flex: none;
  font-size: var(--text-lg);
  line-height: 1;
}
.er-modal__close:hover { background: var(--color-hover-surface); color: var(--color-text); }

@keyframes er-rise {
  from { opacity: 0; transform: translateY(var(--space-3)); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================================
   13. TOAST

   Announced, dismissible, and never the only record of what happened. A toast
   that carries information a person might need later is a bug: the same fact
   belongs on the record.

   The region is a live region and it is polite, not assertive. An assertive
   toast interrupts a screen reader mid sentence, which for a confirmation is
   rude and for an error is still usually wrong.
   ========================================================================== */

.er-toast-region {
  position: fixed;
  right: var(--gutter);
  bottom: var(--gutter);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: min(380px, calc(100vw - var(--space-8)));
  pointer-events: none;
}
.er-toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: var(--radius);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-border-control);
  box-shadow: var(--shadow-lg);
  font-size: var(--text-sm);
  line-height: var(--lead-sm);
  animation: er-rise var(--dur-slow) var(--ease-out);
}
.er-toast--confirm { border-left-color: var(--color-confirm-mark); }
.er-toast--signal  { border-left-color: var(--color-signal-mark); }
.er-toast__title { font-weight: var(--wght-semibold); margin: 0 0 var(--space-1); }
.er-toast__body { margin: 0; color: var(--color-text-muted); }
.er-toast__close {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  margin-left: auto;
  width: var(--space-6);
  height: var(--space-6);
  border-radius: var(--radius-sm);
  flex: none;
  line-height: 1;
}
.er-toast__close:hover { background: var(--color-hover-surface); color: var(--color-text); }

/* An inline message. Use this rather than a toast for anything tied to a
   specific region of the page, because a toast about a form field is a message
   the user has to hold in their head while they look back at the field. */
.er-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: var(--radius);
  background: var(--color-info-surface);
  color: var(--color-text);
  font-size: var(--text-sm);
  line-height: var(--lead-sm);
}
.er-note--confirm { background: var(--color-confirm-surface); }
.er-note--signal  { background: var(--color-signal-surface); box-shadow: inset 0 0 0 1px var(--color-signal-border); }
.er-note__mark { flex: none; margin-top: var(--space-1); }

/* ============================================================================
   14. LAYOUT HELPERS

   Deliberately few. A utility class that can set any property is an override
   for every rule at once, which is the per-call-site override generalised.
   These five set one property each and none of them can set a colour, a size
   or a duration.
   ========================================================================== */

.er-container { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.er-container--prose { max-width: var(--container-prose); }
.er-container--wide { max-width: var(--container-wide); }

.er-stack { display: flex; flex-direction: column; gap: var(--space-4); }
.er-stack--lg { gap: var(--space-8); }
.er-cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.er-grid { display: grid; gap: var(--grid-gap); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.er-section { padding-block: var(--space-16); }

@media (min-width: 768px) {
  .er-container, .er-container--prose, .er-container--wide { padding-inline: var(--gutter-lg); }
}

/* Visually hidden but available to a screen reader. The only permitted way to
   hide text that still has to be announced. */
.er-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Contact form. The site publishes no email address, so this form is the only
   contact route and it has to look like a first-class part of the page rather
   than an afterthought bolted under the prose. Tokens only, no raw colours:
   the first draft of this block used hex fallbacks and check-design-system.py
   caught five of them. */
.er-form { display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-5); }
.er-field { display: flex; flex-direction: column; gap: var(--space-2); }
.er-field__label { font-weight: 600; font-size: var(--text-sm); }
.er-field__hint { font-weight: 400; color: var(--color-text-muted); }
.er-field__control {
  font: inherit;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  width: 100%;
}
.er-field__control:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}
.er-form__status { min-height: 1.4em; margin: 0; font-size: var(--text-sm); }
.er-form__status--error { color: var(--color-text); }
.er-form__status--ok { color: var(--color-confirm-text); }
