/* ==========================================================================
   overrides.css — the Silk Matrix override layer
   --------------------------------------------------------------------------
   WHAT THIS IS
   Maps the EXISTING selectors in the twelve original stylesheets onto the
   custom properties declared in tokens.css. This is where every visual change
   in the UI enhancement lives.

   LOAD ORDER — THIS FILE LOADS LAST, AFTER EVERY OTHER STYLESHEET,
   INCLUDING AFTER ANY {% block extra_css %} CONTENT.
   It wins by source order, which is what lets it re-point the originals
   without editing them. If it loads any earlier, a page-specific stylesheet
   overrides it and the redesign silently does nothing on that page.

   Note: the originals carry 184 !important declarations (88 in
   home_styles.css). Source order does not beat !important, so those specific
   rules need !important here too. Match specificity to the rule being
   overridden; do not reach for !important by default.

   WHO LOADS IT
   The same five templates and the same 43 preview pages as tokens.css.

   THE TWELVE ORIGINAL STYLESHEETS MUST NOT BE EDITED.
   Do not rename a class to make an override easier. The class names listed in
   CLAUDE.md are load-bearing — JavaScript and Django template logic select on
   them. Restyle the selector; leave its name alone.

   REMEMBER: DEBUG = False is hardcoded. Nothing here is visible in the real
   app until `collectstatic` runs.
   ========================================================================== */

/* ==========================================================================
   1. THE SHARED INSET                         HANDOFF.md 1.3 · BRIEF 3.11
   --------------------------------------------------------------------------
   One rule replaces six competing per-page values. What it neutralises:

     108px !important  .main-content            home_styles.css:4
     108px !important  .main-content            matching_styles.css:6
     108px !important  .main-content            name_selection_styles.css:8
      90px             .page-center-boundary    customer_styles.css:26
      64px (4em)       .main-content            tagging_styles.css:2
      nothing          review_styles.css, error_styles.css, relationship_list

   ...plus four more insets found while verifying the six, all of which fight
   the same job and all of which are neutralised here:

      32px (2rem)      .main-content            mapping_styles.css:5 (shorthand)
      32px !important  .main-content @<=1023px  home_styles.css:407  <- the
                       769-1023px overlap: 32px reserved against a 72px rail
      24px !important  .main-content @<=768px   matching_styles.css:632,
                                                name_selection_styles.css:583
       0px             .page-center-boundary @<=1024px  base_styles.css:301
       8%              .top-bar                 base_styles.css:367  <- the
                       top bar is a SIBLING of .main-content, not a child, so
                       it carries its own inset. 8% is 64px at 800px wide,
                       i.e. under the 72px rail. Same shared value fixes it.

   .main-content is the one element every page has: base.html puts it on all
   12 of its pages, and home.html, mapping.html and upload_status.html each
   declare their own. .page-center-boundary sits INSIDE .main-content on the
   customer / relationship pages, so it must drop to 0 or the inset doubles.

   !important is required on .main-content and only there: three originals set
   this property with !important and source order does not beat !important.
   Every other rule in this section wins on source order alone.
   ========================================================================== */

.main-content {
  /* !important: required. home_styles.css:4, matching_styles.css:6 and
     name_selection_styles.css:8 all set padding-left with !important, as do
     their <=1023px / <=768px media rules. */
  padding-left: var(--sm-inset) !important;
  transition: padding-left 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.top-bar {
  /* No !important: base_styles.css:367 is a plain declaration. */
  padding-left: var(--sm-inset);
  transition: padding-left 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.page-center-boundary {
  /* Nested inside .main-content, which now carries the inset. No !important:
     customer_styles.css:26 and base_styles.css:301 are both plain. */
  padding-left: 0;
}

/* ==========================================================================
   2. PUSH AT >= 1024px                                        BRIEF 3.11
   --------------------------------------------------------------------------
   The sidebar is position: fixed, so it cannot push anything by itself. The
   expanded state lives as .open on .left-bar, which is a preceding SIBLING of
   both .top-bar and .main-content in every one of the five full-document
   templates, so the general sibling combinator reads the sidebar's state
   without a template or JS change.

   72px rail -> 240px expanded is 168px of extra width, so the expanded inset
   is --sm-inset + 168px: 276px at >= 1024px, which is the figure HANDOFF.md
   1.3 gives. Pushing is scoped to >= 1024px; below that the sidebar overlays
   and the page must not reflow.

   The expanded state is not persisted (no localStorage, no sessionStorage, no
   cookie), so it resets on navigation. The resting layout is unchanged.
   ========================================================================== */

@media (min-width: 1024px) {
  .left-bar.open ~ .main-content {
    /* !important: same reason as the base rule - it is overriding the same
       three !important declarations. */
    padding-left: calc(var(--sm-inset) + 168px) !important;
  }

  .left-bar.open ~ .top-bar {
    padding-left: calc(var(--sm-inset) + 168px);
  }
}

/* ==========================================================================
   3. OVERLAY + SCRIM BELOW 1024px               BRIEF 3.11 · HANDOFF 3.17
   --------------------------------------------------------------------------
   #overlay already exists and is already wired: toggleSilkSidebar() adds
   .active to it alongside .open on the sidebar, and clicking it closes the
   sidebar. Nothing here needs JS.

   base_styles.css declares #overlay TWICE with different colours and blurs
   (rgba(15,23,42,.15) + blur(3px) at :286, then rgba(0,0,0,.2) + blur(2px) at
   :342, which is the one that currently wins). Both are replaced by the one
   scrim token. HANDOFF.md 3.17: one scrim, --sm-scrim, NO blur.

   z-index: the scrim must sit above page content and below the sidebar. The
   sidebar carries an inline z-index: 99999 !important in left_bar.html, so
   1900 clears every in-page layer (.bottom-progress-container is 1000) while
   staying under it.
   ========================================================================== */

#overlay {
  background: var(--sm-scrim);
  /* HANDOFF.md 3.17 and 6.10: no blur. */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  z-index: 1900;
}

/* At >= 1024px the sidebar pushes instead of overlaying, so there is nothing
   to scrim. The toggle still adds .active; this makes it inert rather than
   asking the template to stop doing it. */
@media (min-width: 1024px) {
  #overlay,
  #overlay.active {
    opacity: 0;
    pointer-events: none;
  }
}

/* ==========================================================================
   4. THE CENTRED MODAL MUST CLEAR THE RAIL TOO
   --------------------------------------------------------------------------
   Found while verifying task 4, not on the original list of six.

   .modal-overlay (base_styles.css:550) is a full-viewport flex container that
   centres its dialog on the VIEWPORT, and it carries z-index: 9999 while the
   sidebar carries an inline z-index: 99999. So the rail paints over the
   dialog's left edge whenever the dialog is wide enough to reach it: measured
   at 900px the file-preview dialog's title starts at x=69 against a 72px rail,
   and at 800px at x=64.

   Giving the overlay the shared inset as padding re-centres the dialog in the
   area the sidebar leaves, which is what "centred" should have meant. The
   scrim itself still covers the full viewport.
   ========================================================================== */

.modal-overlay {
  padding-left: var(--sm-inset);
}

@media (min-width: 1024px) {
  .left-bar.open ~ .modal-overlay {
    padding-left: calc(var(--sm-inset) + 168px);
  }
}

/* ==========================================================================
   5. THE BUTTON SYSTEM                        HANDOFF.md 3.2, 3.3 · 2.1, 2.2
   --------------------------------------------------------------------------
   WHAT WAS WRONG

   Nine separately-defined primary buttons, verified against the twelve
   originals, with no shared class between them:

     .bottom-buttons .next / .next-address / .next-email / .next-phone
                                    name_selection_styles.css:310  12px  16px
     .btn-next                      mapping_styles.css:114         10px  14px
     .card-footer .skip-btn         matching_styles.css:142        12px  16px
     .process-forward-btn           home_styles.css:324            10px  13.5px
     .calendar-done-btn             customer_styles.css:735        12px  14px
     .home-btn                      error_styles.css:75            12px  14px
     .refresh-btn                   base_styles.css:519             8px  14px
     #filePreviewModal .btn-primary home_styles.css:587             6px  .875rem
                                    ALL NINE PROPERTIES !important
     .login-btn                     login_styles.css:138           50px  16px
                                    linear-gradient(193deg,#60c5c3,#54ccaf,#14c584)

   Six radii (6 8 10 12 12 50px), five font sizes (13.5 14 14 15 16px), two
   blues (#018ac6 and #0284c7, plus #008cdd on Review) and one green gradient.

   Three more solid actions that are primary in everything but name were found
   while verifying the nine and are unified with them: .btn-add
   (base_styles.css:586), .jump-page-btn (customer:864 and
   relationship_list:189, as the small variant) and .view-alt-btn
   (review_styles.css:988, a blue gradient). Two further green gradients that
   HANDOFF.md 4.3 sends to blue: .preview-file-btn (home:338) and
   .view-relationship (review:316).

   THE CANONICAL FORM — HANDOFF.md 3.2
     38px tall · --sm-radius-control · 14px/500 · 150ms transition.
     Primary     fill --sm-brand-strong, white text. Hover --sm-brand-deep.
     Secondary   border --sm-brand, text --sm-brand-strong, white fill.
                 Hover --sm-brand-wash.
     Tertiary    border --sm-line, text --sm-ink-muted. Hover --sm-surface-sunk.
     Destructive fill --sm-danger-solid. Hover --sm-danger-pressed.
     Disabled    border --sm-line, text --sm-ink-faint, NOT opacity on a fill.
     Small       30px tall, 0 12px, 13px.

   WHY --sm-brand-strong AND NOT --sm-brand
     HANDOFF.md 2.2 / 6.1: white 14px on #018ac6 is 3.8:1 and fails AA. Every
     one of the nine used #018ac6 or a retired blue as a fill under white text.
     #016fa0 is 5.6:1. This is the single most-repeated defect in the set.

   NO .btn CLASS IS INTRODUCED
     Templates are not edited on this branch and CLAUDE.md 6 forbids relying on
     a class the template does not emit. Every rule below therefore repeats the
     EXACT selector from the original, so it ties on specificity and wins on
     source order. Where the original is !important, so is this.

   HEIGHT ARITHMETIC
     HANDOFF.md 3.2 gives both "38px tall" and "10px 18px". 10 + 20 (the body
     leading) + 10 + 2 (border) is 42px, not 38. The stated control height is
     the harder constraint — HANDOFF.md 1.3 repeats "Control height 38px" in
     the density table — so the padding here is 8px 18px, which lands on 38px
     exactly with the 14/20 body role and a 1px border.
   ========================================================================== */

/* --------------------------------------------------------------------------
   5.1 The shared chassis
   Geometry, type and transition for every button-shaped control. Width and
   height are deliberately absent: .process-forward-btn, .login-btn,
   .view-alt-btn, .view-relationship, .alt-type-btn and .calendar-done-btn all
   size themselves from their container and must keep doing so. Only
   min-height is set, which is what neutralises Review's height: 33px.
   -------------------------------------------------------------------------- */

.bottom-buttons a,
.bottom-buttons button,
.bottom-buttons a.back,
.bottom-buttons a.next,
.bottom-buttons button.next,
.bottom-buttons .next,
.bottom-buttons .next-address,
.bottom-buttons .next-email,
.bottom-buttons .next-phone,
.card-footer .back,
.card-footer .skip-btn,
.btn-back,
.btn-next,
.process-forward-btn,
.preview-file-btn,
.calendar-done-btn,
.calendar-clear-btn,
.home-btn,
.refresh-btn,
.btn-add,
.login-btn,
.back-navigation-btn,
.netsuite-external-btn,
.add-field-btn,
.toggle-btn,
.alt-type-btn,
.view-alt-btn,
.view-relationship,
.process-btn,
.netsuite-btn,
.dashboard-signout-btn,
.right-card-box .attributes-sections button,
.buttons-section button,
.buttons-section .choose,
.buttons-section .choose-btn,
.buttons-section .reject,
.buttons-section .back,
.relationship-buttons button,
.relationship-buttons .choose,
.relationship-buttons .reject,
.relationship-buttons .remove-relationship,
.options-wrapper button {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sm-space-2);
  min-height: 38px;
  padding: 8px 18px;
  border: 1px solid transparent;
  border-radius: var(--sm-radius-control);
  font-family: inherit;
  font-size: var(--sm-type-body-size);
  font-weight: var(--sm-weight-medium);
  line-height: var(--sm-type-body-leading);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  /* HANDOFF.md 2.10: buttons sit flat; depth is the 1px border. This clears
     .home-btn's 0 4px 6px, .card-footer .back's 0 2px 6px, .login-btn's hover
     shadow and .process-forward-btn's 0 2px 4px. The focus ring in 5.7 is a
     later, more specific rule and is not affected. */
  box-shadow: none;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

/* #filePreviewModal carries !important on all nine of its .btn-primary
   properties (home_styles.css:587-597) and on all nine of .btn-secondary's
   (home_styles.css:570-579). Source order cannot beat !important, so the
   chassis is repeated here with it, on the same selector so specificity ties
   and the later file wins. This is the one place where the whole chassis
   needs !important rather than a single declaration. */
#filePreviewModal .btn-primary,
#filePreviewModal .btn-secondary {
  box-sizing: border-box !important;
  display: inline-flex !important;                      /* was inline-block !important */
  align-items: center !important;
  justify-content: center !important;
  gap: var(--sm-space-2) !important;
  min-height: 38px !important;
  padding: 8px 18px !important;
  border: 1px solid transparent !important;
  border-radius: var(--sm-radius-control) !important;   /* was 6px !important */
  font-family: inherit !important;
  font-size: var(--sm-type-body-size) !important;       /* was .875rem !important */
  font-weight: var(--sm-weight-medium) !important;      /* was 600 !important */
  line-height: var(--sm-type-body-leading) !important;
  text-decoration: none !important;
  cursor: pointer !important;
  box-shadow: none !important;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease !important;
}

/* --------------------------------------------------------------------------
   5.2 Secondary (outlined)
   Declared before primary so that where a selector could match both, primary
   wins on its own specificity rather than on ordering. .bottom-buttons a
   (0,1,1) is listed here; every "next" variant is (0,2,0) or (0,2,1) and
   outranks it.
   -------------------------------------------------------------------------- */

.bottom-buttons a,
.bottom-buttons a.back,
.card-footer .back,
.btn-back,
.back-navigation-btn,
.netsuite-external-btn,
.add-field-btn,
.toggle-btn,
.alt-type-btn,
.process-btn,
.netsuite-btn,
.right-card-box .attributes-sections button {
  background-color: var(--sm-surface);
  /* background-image, not the background shorthand, which would reset the
     background-color declared beside it. Kept symmetrical with 5.3, where it
     is what actually removes the gradients. */
  background-image: none;
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

.bottom-buttons a:hover,
.bottom-buttons a.back:hover,
.card-footer .back:hover,
.btn-back:hover,
.back-navigation-btn:hover,
.netsuite-external-btn:hover,
.add-field-btn:hover,
.toggle-btn:hover,
.alt-type-btn:hover,
.process-btn:hover,
.netsuite-btn:hover,
.right-card-box .attributes-sections button:hover {
  background-color: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

/* --------------------------------------------------------------------------
   5.3 Primary (solid)
   HANDOFF.md 2.1: the fill is --sm-brand-strong because the user clicks it.
   -------------------------------------------------------------------------- */

.bottom-buttons button,
.bottom-buttons a.next,
.bottom-buttons button.next,
.bottom-buttons .next,
.bottom-buttons .next-address,
.bottom-buttons .next-email,
.bottom-buttons .next-phone,
.card-footer .skip-btn,
.btn-next,
.process-forward-btn,
.preview-file-btn,
.calendar-done-btn,
.home-btn,
.refresh-btn,
.btn-add,
.login-btn,
.view-alt-btn,
.view-relationship,
.toggle-btn.active,
.alt-type-btn.active,
.buttons-section .choose,
.buttons-section .choose-btn,
.relationship-buttons .choose {
  /* background-image: none is what actually removes the five gradients —
     .login-btn's linear-gradient(193deg,#60c5c3,#54ccaf,#14c584),
     .preview-file-btn's and .view-relationship's
     linear-gradient(90deg,#0AA8A5,#15C685), .view-alt-btn's
     linear-gradient(90deg,#018ac6,#0069a0) and .toggle-btn.active's
     linear-gradient(90deg,#0095D9,#007CC7). Setting only background-color
     leaves the gradient painted on top of it. */
  background-color: var(--sm-brand-strong);
  background-image: none;
  border-color: var(--sm-brand-strong);
  color: var(--sm-surface);
}

.bottom-buttons button:hover,
.bottom-buttons a.next:hover,
.bottom-buttons button.next:hover,
.bottom-buttons .next:hover,
.bottom-buttons .next-address:hover,
.bottom-buttons .next-email:hover,
.bottom-buttons .next-phone:hover,
.card-footer .skip-btn:hover,
.btn-next:hover,
.process-forward-btn:hover,
.preview-file-btn:hover,
.calendar-done-btn:hover,
.home-btn:hover,
.refresh-btn:hover,
.btn-add:hover,
.login-btn:hover,
.view-alt-btn:hover,
.view-relationship:hover,
.toggle-btn.active:hover,
.alt-type-btn.active:hover,
.buttons-section .choose:hover,
.buttons-section .choose-btn:hover,
.relationship-buttons .choose:hover {
  background-color: var(--sm-brand-deep);
  border-color: var(--sm-brand-deep);
  color: var(--sm-surface);
  /* .home-btn:hover lifted by translateY(-1px) and .login-btn:hover by
     translateY(-2px). HANDOFF.md 3.13 is explicit that a decision state
     carries no transform; the same reasoning applies to a hover. */
  transform: none;
  box-shadow: none;
}

#filePreviewModal .btn-primary {
  background-color: var(--sm-brand-strong) !important;  /* was #0284c7 !important */
  background-image: none !important;
  border-color: var(--sm-brand-strong) !important;      /* was border: none !important */
  color: var(--sm-surface) !important;
}

#filePreviewModal .btn-primary:hover {
  background-color: var(--sm-brand-deep) !important;    /* was #0369a1 !important */
  border-color: var(--sm-brand-deep) !important;
  color: var(--sm-surface) !important;
}

/* The pressed state of an interactive surface is --sm-brand-deep
   (HANDOFF.md 2.1), which is already the hover, so :active only has to cancel
   the transforms the originals apply. */
.home-btn:active,
.login-btn:active,
.calendar-clear-btn:active,
.close-panel-btn:active {
  transform: none;
}

/* --------------------------------------------------------------------------
   5.4 Tertiary (neutral outlined)
   -------------------------------------------------------------------------- */

.calendar-clear-btn,
.dashboard-signout-btn {
  background-color: var(--sm-surface);
  background-image: none;
  border-color: var(--sm-line);
  color: var(--sm-ink-muted);
}

.calendar-clear-btn:hover {
  background-color: var(--sm-surface-sunk);
  border-color: var(--sm-line);
  color: var(--sm-ink);
}

/* Sign Out keeps its danger-coloured hover — it is the one destructive action
   on the dashboard — but the off-palette #fef2f2 / #fca5a5 / #ef4444 become
   the danger tokens. */
.dashboard-signout-btn:hover {
  background-color: var(--sm-danger-tint);
  border-color: var(--sm-danger-border);
}

.dashboard-signout-btn:hover svg,
.dashboard-signout-btn:hover .signout-btn-text {
  color: var(--sm-danger-solid);
  transform: none;
}

#filePreviewModal .btn-secondary {
  background-color: var(--sm-surface) !important;       /* was #f1f5f9 !important */
  background-image: none !important;
  border-color: var(--sm-line) !important;              /* was #cbd5e1 !important */
  color: var(--sm-ink-muted) !important;                /* was #334155 !important */
}

#filePreviewModal .btn-secondary:hover {
  background-color: var(--sm-surface-sunk) !important;  /* was #e2e8f0 !important */
  border-color: var(--sm-line) !important;
  color: var(--sm-ink) !important;
}

/* --------------------------------------------------------------------------
   5.5 Destructive                                   TASK 3 · DESIGN-BRIEF 3.6
   The reject button is red on every screen.

     Matching  .buttons-section .reject              solid #fe5959   :342
     Tagging   .relationship-buttons .reject
               .relationship-buttons .remove-relationship
                                                     OUTLINED #0caf9c :381

   #0caf9c is --sm-success-mark. HANDOFF.md 4.3: green means completion and
   nothing else. Both become the canonical solid destructive, so Add/Reject on
   Tagging now reads exactly as Choose/Reject does on Matching.

   .options-wrapper button (name_selection:424) is the solid #fe5959 remove
   control on the selection screens and joins them.
   .buttons-section .back (matching:352) is solid #fe5959 today; it is grouped
   here so it keeps its meaning rather than turning blue. No template renders
   it — see the report.
   -------------------------------------------------------------------------- */

.buttons-section .reject,
.buttons-section .back,
.relationship-buttons .reject,
.relationship-buttons .remove-relationship,
.options-wrapper button {
  background-color: var(--sm-danger-solid);
  background-image: none;
  border-color: var(--sm-danger-solid);
  color: var(--sm-surface);
}

.buttons-section .reject:hover,
.buttons-section .back:hover,
.relationship-buttons .reject:hover,
.relationship-buttons .remove-relationship:hover,
.options-wrapper button:hover {
  background-color: var(--sm-danger-pressed);
  border-color: var(--sm-danger-pressed);
  color: var(--sm-surface);
}

/* The Choose and Reject labels carry an inline style="font-size: 15px" in
   matching.html. Templates are not edited on this branch, so !important is
   the only way to bring them to the 14px body role. */
.buttons-section .choose-btn p,
.buttons-section .reject p,
.buttons-section .back p {
  font-size: var(--sm-type-body-size) !important;
  font-weight: var(--sm-weight-medium);
  margin: 0;
}

/* --------------------------------------------------------------------------
   5.6 Disabled                                                HANDOFF.md 3.2
   "Border --sm-line, text --sm-ink-faint, cursor: not-allowed. Not opacity on
   a saturated fill." Opacity over a blue fill gives a washed blue that still
   reads as available; a neutral outline does not.
   -------------------------------------------------------------------------- */

.bottom-buttons button:disabled,
.card-footer .skip-btn:disabled,
.btn-next:disabled,
.calendar-done-btn:disabled,
.refresh-btn:disabled,
.btn-add:disabled,
.login-btn:disabled,
.toggle-btn:disabled,
.alt-type-btn:disabled,
.buttons-section button:disabled,
.relationship-buttons button:disabled,
.options-wrapper button:disabled,
.jump-page-btn:disabled,
.page-nav-btn:disabled,
.page-number-btn:disabled,
.disabled-btn {
  background-color: var(--sm-surface);
  background-image: none;
  border-color: var(--sm-line);
  color: var(--sm-ink-faint);
  /* replaces opacity: 0.6 on .refresh-btn:disabled and opacity: 0.4 on
     .disabled-btn */
  opacity: 1;
  box-shadow: none;
  cursor: not-allowed;
}

/* .disabled-btn also carries pointer-events: none, which is kept — it is what
   stops a dead pagination arrow being clickable. */
.disabled-btn {
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   5.7 Focus                                              HANDOFF.md 1.5 · 3.7
   --sm-focus on every interactive control. Only the login page has a focus
   ring today. :focus-visible, so a mouse click does not leave a ring behind.
   -------------------------------------------------------------------------- */

.bottom-buttons a:focus-visible,
.bottom-buttons button:focus-visible,
.card-footer .back:focus-visible,
.card-footer .skip-btn:focus-visible,
.btn-back:focus-visible,
.btn-next:focus-visible,
.process-forward-btn:focus-visible,
.preview-file-btn:focus-visible,
.calendar-done-btn:focus-visible,
.calendar-clear-btn:focus-visible,
.home-btn:focus-visible,
.refresh-btn:focus-visible,
.btn-add:focus-visible,
.login-btn:focus-visible,
.back-navigation-btn:focus-visible,
.netsuite-external-btn:focus-visible,
.add-field-btn:focus-visible,
.toggle-btn:focus-visible,
.alt-type-btn:focus-visible,
.view-alt-btn:focus-visible,
.view-relationship:focus-visible,
.process-btn:focus-visible,
.netsuite-btn:focus-visible,
.dashboard-signout-btn:focus-visible,
.right-card-box .attributes-sections button:focus-visible,
.buttons-section button:focus-visible,
.relationship-buttons button:focus-visible,
.options-wrapper button:focus-visible,
.jump-page-btn:focus-visible,
.page-nav-btn:focus-visible,
.page-number-btn:focus-visible,
.quick-pill-btn:focus-visible,
.scroll-btn:focus-visible,
.scroll-btn1:focus-visible,
.scroll-btn2:focus-visible,
.navigation-buttons button:focus-visible,
.close-panel-btn:focus-visible,
.cancel-upload-btn:focus-visible,
.tab-nav-link:focus-visible,
.relation-tab-btn:focus-visible,
.dropdown-trigger:focus-visible,
.date-dropdown-btn:focus-visible,
.action-external-trigger:focus-visible,
.sortable-column-header:focus-visible,
/* The sidebar toggle is the one focusable control on every page that had
   neither a hover nor a focus rule. It belongs to the sidebar component
   (HANDOFF.md 3.18), so only the ring is added here. */
.open-left-bar:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
  border-color: var(--sm-brand);
}

/* Session 11. A keyboard sweep of all 43 states (Tab through every focus
   stop at 1440 and 390, transitions off) found the controls 5.7 did not
   list: the list pages' search field, whose only focus state was a 1.5px
   border change; the login fields, whose ring was the retired teal
   rgba(72,201,176,.2) (login_styles.css:74, (0,2,1), so repeated with a
   class to outrank it); and the sidebar, logo, metric-tile, dashboard and
   calendar links and inputs plus the preview modal's table scroller, which
   fell back to the browser's own outline. All take the one ring. */
.search-wrapper input:focus-visible,
.login-container input[type="email"]:focus,
.login-container input[type="password"]:focus,
.login-container input[type="text"]:focus,
.export-modal-overlay input:focus-visible,
.icon-link-wrapper:focus-visible,
.logo-link:focus-visible,
.metric-card:focus-visible,
a.gradient-colored:focus-visible,
.modal-container .table-wrapper:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
  border-color: var(--sm-brand);
}

/* #filePreviewModal's box-shadow-adjacent properties are all !important, so
   the ring has to match or it never paints. */
#filePreviewModal .btn-primary:focus-visible,
#filePreviewModal .btn-secondary:focus-visible {
  outline: none !important;
  box-shadow: var(--sm-focus) !important;
  border-color: var(--sm-brand) !important;
}

/* --------------------------------------------------------------------------
   5.8 Small variant                                            HANDOFF.md 3.2
   30px tall, 0 12px, 13px. The Go-to-page submit on both list pages
   (customer_styles.css:864 and relationship_list_styles.css:189 — the same
   button defined twice, identically, in two files).
   -------------------------------------------------------------------------- */

.jump-page-btn {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 30px;
  padding: 0 12px;
  border: 1px solid var(--sm-brand-strong);
  border-radius: var(--sm-radius-control);   /* was 6px */
  background-color: var(--sm-brand-strong);  /* was #018ac6 — white text at 3.8:1 */
  background-image: none;
  color: var(--sm-surface);
  font-family: inherit;
  font-size: var(--sm-type-small-size);      /* was 12px, below the 13px floor */
  font-weight: var(--sm-weight-medium);      /* was 600 */
  line-height: var(--sm-type-small-leading);
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.jump-page-btn:hover {
  background-color: var(--sm-brand-deep);
  border-color: var(--sm-brand-deep);
}

/* --------------------------------------------------------------------------
   5.9 Icon buttons and deck pagers                             HANDOFF.md 3.3
   32px square, outlined, --sm-radius-pill for pagers and --sm-radius-control
   for close buttons, 44px minimum hit area via padding.

   PRESERVE THE CLASS NAMES — .scroll-btn, .scroll-btn1, .scroll-btn2 and
   .navigation-buttons button are read by the deck scripts (CLAUDE.md 6,
   HANDOFF.md 3.3, 6.4). Restyled, never renamed.

   These were 28px and 22px solid --sm-brand circles carrying a chevron PNG.
   Outlining them on white also fixes the chevron: the assets are dark glyphs
   that were being shown against a saturated blue fill.
   -------------------------------------------------------------------------- */

.scroll-btn,
.scroll-btn1,
.scroll-btn2,
.navigation-buttons button {
  position: relative;
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-surface);
  background-image: none;
  color: var(--sm-brand-strong);
  box-shadow: none;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.scroll-btn:hover,
.scroll-btn1:hover,
.scroll-btn2:hover,
.navigation-buttons button:hover {
  background-color: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

/* 44px minimum hit area without growing the 32px visual box. The
   pseudo-element is a child of the button, so a click on it still targets the
   button and the deck scripts see the same event. */
.scroll-btn::after,
.scroll-btn1::after,
.scroll-btn2::after,
.navigation-buttons button::after {
  content: "";
  position: absolute;
  top: -6px;
  right: -6px;
  bottom: -6px;
  left: -6px;
}

/* The deck pagers sit 10px apart, which would leave two 44px hit areas
   overlapping by 2px. One step of the space scale clears it exactly.
   (16.2 later widens it to 16px; this 12px is the fallback it overrides.) */
.right-card-box .navigation-buttons {
  gap: var(--sm-space-3);
}

/* .scroll-btn2.disabled and its siblings set background-color and color with
   !important (name_selection_styles.css:260-261), so the disabled treatment
   needs it too. Every other declaration here wins on source order. */
.scroll-btn.disabled,
.scroll-btn1.disabled,
.scroll-btn2.disabled,
.navigation-buttons button.disabled {
  background-color: var(--sm-surface) !important;  /* was #ccc !important */
  color: var(--sm-ink-faint) !important;           /* was #777 !important */
  border-color: var(--sm-line);
  opacity: 1;                                      /* was 0.6 / 0.4 */
  cursor: not-allowed;
  pointer-events: none;
}

/* Close buttons take --sm-radius-control, not the pill. */
.close-panel-btn {
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-control);   /* was 50% */
  background: var(--sm-surface);             /* was #f3f4f6 */
  color: var(--sm-ink-muted);
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.close-panel-btn:hover {
  background: var(--sm-surface-sunk);
  border-color: var(--sm-line-strong);
  color: var(--sm-ink);
  transform: none;                           /* was scale(1.08) */
}

/* HANDOFF.md 2.12: the icon takes its colour from the parent. */
.close-panel-btn svg {
  color: currentColor;
  transition: none;
}

.close-panel-btn:hover svg {
  color: currentColor;
  transform: none;                           /* was rotate(90deg) */
}

/* The staged-file dismiss control on the dashboard. */
.cancel-upload-btn {
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-pill);      /* was 50% */
  background: var(--sm-surface);             /* was #e5e7eb */
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-subtitle-size);   /* was 1.1rem */
  line-height: 1;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.cancel-upload-btn:hover {
  background: var(--sm-danger-tint);
  border-color: var(--sm-danger-border);
  color: var(--sm-danger-solid);
}

/* --------------------------------------------------------------------------
   5.10 Pagination controls                                    HANDOFF.md 3.11
   34px, --sm-radius-control, active page --sm-brand-strong. The active page
   is #10ba90 !important today — a green that means "completion" everywhere
   else in this system (HANDOFF.md 4.3) sitting on a control the user clicks.
   -------------------------------------------------------------------------- */

.page-nav-btn,
.page-number-btn {
  border-color: var(--sm-line);
  border-radius: var(--sm-radius-control);
  background-color: var(--sm-surface);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-medium);
  box-shadow: none;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.page-nav-btn:hover,
.page-number-btn:hover {
  background-color: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

/* !important on all three: customer_styles.css:434-436 declares them that way. */
.active-page {
  background-color: var(--sm-brand-strong) !important;
  border-color: var(--sm-brand-strong) !important;
  color: var(--sm-surface) !important;
}

/* --------------------------------------------------------------------------
   5.11 The remaining load-bearing button names                   CLAUDE.md 6
   Restyled onto tokens, never renamed.
   -------------------------------------------------------------------------- */

/* .quick-pill-btn — 11px/700 is below the 13px reading floor (HANDOFF.md 2.9)
   and #f3f4f6 + #4b5563 are already the pending neutrals. */
.quick-pill-btn {
  border: 1px solid transparent;
  border-radius: var(--sm-radius-pill);      /* was 20px */
  background-color: var(--sm-surface-sunk);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);      /* was 11px */
  font-weight: var(--sm-weight-medium);      /* was 700 */
  line-height: var(--sm-type-small-leading);
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.quick-pill-btn:hover {
  background-color: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

/* !important: base_styles.css:714-715 declares background-color and color
   that way. --sm-brand-deep for the label, not --sm-brand: the selected pill
   is a tinted classification chip (HANDOFF.md 2.4), and #018ac6 on #e0f2fe is
   the weaker of the two pairs. The date-range presets (17) carry this class
   too, so these !important values also fix their active colours: change
   both together. */
.quick-pill-btn.pill-active {
  background-color: var(--sm-brand-tint) !important;
  color: var(--sm-brand-deep) !important;
  border-color: var(--sm-brand);
  box-shadow: none;
}

/* Session 11's wrap for the export calendar's pill row (#55) is gone with
   the row: item 22's date-range panel lays the three presets out as an
   equal 3-column grid (section 17). */

/* .dropdown-trigger and .date-dropdown-btn — HANDOFF.md 3.9: 38px,
   --sm-radius-control, 1px --sm-line-strong. */
.dropdown-trigger {
  box-sizing: border-box;
  min-height: 38px;
  padding: 8px 16px;
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-radius-control);   /* was 12px */
  background: var(--sm-surface);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-body-size);
  font-weight: var(--sm-weight-medium);
  box-shadow: none;
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.dropdown-trigger:hover {
  border-color: var(--sm-brand);
  color: var(--sm-ink);
}

.date-dropdown-btn {
  border: 1px solid var(--sm-line-strong);   /* was #cbd5e1 */
  border-radius: var(--sm-radius-control);
  background: var(--sm-surface);             /* was #f8fafc */
  color: var(--sm-ink-muted);                /* was #334155 */
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-medium);
}

.date-dropdown-btn:hover {
  border-color: var(--sm-brand);
  background: var(--sm-brand-wash);
  color: var(--sm-brand-strong);
}

/* .action-external-trigger is #10ba90 — green, which HANDOFF.md 4.3 reserves
   for completion. It is a link out to the source system, so it is a link:
   --sm-brand-strong. */
.action-external-trigger {
  color: var(--sm-brand-strong);
}

.action-external-trigger:hover {
  color: var(--sm-brand-deep);
}

/* .tab-nav-link and .relation-tab-btn are tabs, not buttons, and their 3px
   underline is already --sm-brand, which is correct (HANDOFF.md 1.1). Only
   the resting colour moves: .relation-tab-btn is #9ca3af, and HANDOFF.md 2.8
   is explicit that --sm-ink-faint is never text a user reads. */
.relation-tab-btn {
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-body-size);       /* was 15px */
  font-weight: var(--sm-weight-semibold);
  border-radius: var(--sm-radius-control);
}

.tab-nav-link {
  color: var(--sm-ink-muted);
  border-radius: var(--sm-radius-control);
}

.tab-nav-link:hover,
.tab-nav-link.active-tab,
.relation-tab-btn:hover,
.relation-tab-btn.active-tab {
  color: var(--sm-brand-strong);
}

.tab-nav-link.active-tab::after,
.relation-tab-btn.active-tab::after {
  background-color: var(--sm-brand);
  height: 3px;                               /* was 3.5px */
}

/* --------------------------------------------------------------------------
   5.12 The wizard footer                                              TASK 4
   .bottom-buttons is defined in four stylesheets, for four consecutive
   screens of one flow, and rendered four different ways:

     name_selection:267  Back outlined #018ac6   Next solid #018ac6
                         radius 12px, 16px, min-width 100px
     review:251          Back outlined #0caf9c   Done solid #008cdd
                         radius 12px, 15px, width: 94px; height: 33px
     tagging:194         Back outlined #018ac6   Next solid #018ac6
                         radius 12px, font size inherited
     matching:230        Back outlined #799faf   Next green gradient
                         (no template renders it — see the report)

   5.1–5.3 already give all four the same fill, border, radius, padding, size
   and weight. This section removes the three things that would still make
   Review differ: its fixed 94x33 box, Name Selection's min-width, and the
   row's own gap.

   HANDOFF.md 2.11 names this rule directly — "the width: 94px; height: 33px
   on Review's wizard buttons goes". Content-width is what makes the two
   screens match, not a shared fixed width.
   -------------------------------------------------------------------------- */

.bottom-buttons a,
.bottom-buttons button,
.bottom-buttons a.back,
.bottom-buttons a.next,
.bottom-buttons button.next,
.bottom-buttons .next,
.bottom-buttons .next-address,
.bottom-buttons .next-email,
.bottom-buttons .next-phone {
  width: auto;        /* was 94px on Review */
  height: auto;       /* was 33px on Review; min-height: 38px from 5.1 stands */
  min-width: 0;       /* was 100px on Name Selection */
  max-width: 100%;
}

/* The row's own spacing. Only the gap is unified: Tagging's .bottom-buttons
   is a right-aligned column with margin-top: 38%, which is a layout defect
   (HANDOFF.md 6.13) but not a button one. No selector distinguishes that
   container from the other three without a template edit, so it is logged in
   docs/DEFERRED-FIXES.md rather than changed here. */
.bottom-buttons {
  gap: var(--sm-space-4);
}

/* Form wrappers must not introduce a box of their own. Review already does
   this; Name Selection needs it, because its Next lives inside a <form> too. */
.bottom-buttons form {
  margin: 0;
  display: inline-flex;
}

/* --------------------------------------------------------------------------
   5.13 The Choose button inside a chosen card
   matching_styles.css:364 is (0,3,0) and outranks 5.3, so the Choose button
   on a selected card stayed Bootstrap #0056b3 and kept transform: scale(1.05),
   which made it 40px tall next to a 38px Reject. Chosen is a pressed
   interactive surface, so HANDOFF.md 2.1 gives it --sm-brand-deep; HANDOFF.md
   3.13 is explicit that the chosen state carries no transform and no scale.

   The card's own .selected-card treatment is a decision state, not a
   button: 13.1 (session 12, DEFERRED-FIXES #18). Only the button is
   touched here.
   -------------------------------------------------------------------------- */

.selected-card .buttons-section .choose-btn {
  background-color: var(--sm-brand-deep);
  background-image: none;
  border-color: var(--sm-brand-deep);
  color: var(--sm-surface);
  transform: none;
}

/* ==========================================================================
   6. THE DATA TABLES                          HANDOFF.md 3.6, 3.10, 3.11
                                               BRIEF 3.9, 8.1, 8.2
   --------------------------------------------------------------------------
   Four pages, five table shapes:

     .customers-table-container            customer list
     .details-table-panel table            customer detail, five tab panels
     .details-table-panel table            relationship detail, one panel
     .master-relationship-table-container  relationship list, grouped tbodies
     .relationship-group-card              ...one <tbody> per group

   MEASURED BEFORE THIS SECTION (43 preview pages x 6 widths, iframe viewport,
   documentElement.scrollWidth vs clientWidth):

     the customer-list table needs 1061px and its container is overflow:hidden,
     so at 1100 / 900 / 800 / 768 / 390 its rightmost columns were unreachable
     -- 21 page/width combinations across customer-list, -filtered, -empty,
     -calendar-popover and loading-overlay-active.

     relationship-detail needed 1159px at 900 and overflowed the PAGE by
     102-704px at 1100 and below, and the relationship list pushed its own
     content to x = -121px at 390, where centring makes left overflow
     unrecoverable (DEFERRED-FIXES #14).

   Only three rules in the whole codebase enable horizontal scrolling, and
   none of them is on a table.
   ========================================================================== */

/* --------------------------------------------------------------------------
   6.1 Two-layer overflow                       HANDOFF.md 3.10, trap 6.12
   overflow: hidden is what gives the container its radius; it is also what
   makes a column that does not fit unreachable. The handoff asks for both:
   hidden outer, overflow-x: auto inner.

   THERE IS NO INNER WRAPPER IN THE MARKUP and CSS cannot create one, so the
   two behaviours are put on the one element that exists:

       overflow-x: auto    the columns are reachable
       overflow-y: hidden  the box still clips, so the radius still cuts

   That is the same result a wrapper would give. Adding a real wrapper div is
   a template edit, and the only template edit this session authorises is the
   identifier (6.2). It is not needed for the behaviour.

   WHY overflow-y IS DECLARED EXPLICITLY: overflow-x: auto alone computes
   overflow-y to auto, not visible, and any absolutely-positioned descendant
   that overhangs would then add a vertical scrollbar rather than being
   clipped. Both branches clip; only one of them also puts a scrollbar on a
   table that does not need one.

   CONSEQUENCE FOR POPOVERS INSIDE A CELL: a scroll container clips them.
   Two live inside these tables -- the identifier (6.2) and customer detail's
   date dropdown. The identifier opens UPWARDS into the header band, so it is
   always inside the box. The date dropdown opens downwards and cannot; 6.1.1
   handles it.
   -------------------------------------------------------------------------- */

.customers-table-container {
  overflow-x: auto;
  overflow-y: hidden;
  border-radius: var(--sm-radius-card);   /* was 20px */
  border-color: var(--sm-line);
  box-shadow: var(--sm-e1);               /* resting surface, HANDOFF.md 2.10 */
}

.details-table-panel {
  overflow-x: auto;
  overflow-y: hidden;
}

/* 6.1.1 The one popover a scroll container would break.
   customer_detail.html renders .date-dropdown-menu absolutely below its
   button inside a Date cell; on the lower rows it would now be clipped.
   While one is open the panel stops clipping. Progressive enhancement: a
   browser without :has() keeps the scroller and clips the open menu, which
   is what every other in-cell popover does today. */
.details-table-panel:has(.date-dropdown-container.menu-open) {
  overflow: visible;
}

/* The relationship list has no scroll container at all: its table is 505-518px
   wide at 390 inside a 310px boundary, and .page-center-boundary centres it,
   which is what puts the page title at x = -121 (DEFERRED-FIXES #14). Giving
   the table its own scroller sends the whole overflow one way and makes it
   reachable. The 4px block padding is room for the group cards' shadow, which
   overflow-y: hidden would otherwise cut. */
.master-relationship-table-container {
  overflow-x: auto;
  overflow-y: hidden;
  padding-block: var(--sm-space-1);
}

/* The four filter controls plus the search field need ~680px in one row
   (BRIEF 8.2) and are the reason the customer list still overflowed at 390
   with the table contained. They wrap instead. */
.unified-filter-group {
  flex-wrap: wrap;
  gap: var(--sm-space-2);
}

/* --------------------------------------------------------------------------
   6.2 The identifier — the UUID out of column one   HANDOFF.md 3.6 · BRIEF 8.1
   --------------------------------------------------------------------------
   A 36-character UUID was the first column of the customer list, both row
   levels of the relationship list, the relationship-detail member table and
   the relationship tab of customer detail, plus the .card-id-tag on both
   detail headers. It is the widest cell in every table it appears in and it
   dictated the layout of all four pages.

   It now renders inside the row's actions affordance as a native <details>:
   a constant-width "ID" chip that opens a popover. No JavaScript.

   THE CHIP CARRIES NO PART OF THE UUID, deliberately. HANDOFF.md 3.6 gives
   the collapsed form as "first 8 chars + ellipsis", but the governing
   constraint on this task is that removing the identifier later must reflow
   nothing, and an 8-character mono chip is ~92px of column sized by the
   value. A two-letter chip is ~34px and is the same width whatever the
   identifier is -- or whether it exists at all. Deleting the four-line
   <details> from each template is then the whole change: no colspan, no sort
   index, no width, no other rule.

   THE POPOVER OPENS UPWARDS (bottom: 100% + 4px) for one reason: 6.1 makes
   every one of these containers clip vertically, and the header band above
   the first row is 40px, which is more than the 32px popover. Downwards, the
   last row's popover would be cut by the container.

   user-select: all makes one click select the whole UUID, which is what the
   column was for. HANDOFF.md 3.6 also asks for a COPY button; that needs
   clipboard JS, which this session is not authorised to add -- logged.
   -------------------------------------------------------------------------- */

.row-identifier {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  margin-left: var(--sm-space-2);
}

.row-identifier-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 20px, not the 22 it wants to be: the chip sits on the table row's line
     box, so anything taller than the body role's 20px leading raises every
     row it appears in. 6.3 is 44px rows and this is inside them. */
  height: 20px;
  padding: 0 var(--sm-space-2);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-control);
  background-color: var(--sm-surface);
  color: var(--sm-ink-quiet);
  /* micro: an eyebrow label, never a sentence. HANDOFF.md 2.9 allows 11px
     here and nowhere a steward reads prose. */
  font-size: var(--sm-type-micro-size);
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: var(--sm-type-micro-transform);
  cursor: pointer;
  user-select: none;
  list-style: none;                       /* drop the disclosure triangle */
  transition: background-color 150ms ease, border-color 150ms ease,
              color 150ms ease, box-shadow 150ms ease;
}

.row-identifier-toggle::-webkit-details-marker { display: none; }
.row-identifier-toggle::marker { content: ''; }

.row-identifier-toggle:hover {
  background-color: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

/* Every interactive control gets the ring. HANDOFF.md 1.5. */
.row-identifier-toggle:focus-visible {
  outline: none;
  border-color: var(--sm-brand);
  box-shadow: var(--sm-focus);
}

.row-identifier[open] > .row-identifier-toggle {
  background-color: var(--sm-brand-tint);
  border-color: var(--sm-brand);
  color: var(--sm-brand-deep);
}

/* The value, wherever it renders: mono, 13px, ink. HANDOFF.md 1.2. */
.row-identifier-value {
  font-family: var(--sm-type-mono-family);
  font-size: var(--sm-type-mono-size);
  line-height: var(--sm-type-mono-leading);
  font-weight: var(--sm-type-mono-weight);
  color: var(--sm-ink);
}

/* ...and inside a row, as a popover that takes no space in the row. */
.row-identifier > .row-identifier-value {
  position: absolute;
  bottom: calc(100% + var(--sm-space-1));
  right: 0;
  z-index: 60;
  display: block;
  white-space: nowrap;
  padding: 6px 10px;
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-control);
  box-shadow: var(--sm-e2);               /* it floats, so it gets a shadow */
  user-select: all;
  -webkit-user-select: all;
}

/* The expanded form on the two detail headers. Already position: absolute in
   customer_styles.css:467, so it never took part in the layout and does not
   now. 11px/700 grey on a cream border becomes the canonical identifier box. */
.card-id-tag {
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-quiet);
  background-color: var(--sm-surface);
  border-color: var(--sm-line);
  border-radius: var(--sm-radius-control);
  padding: var(--sm-space-1) var(--sm-space-3);
}

/* --------------------------------------------------------------------------
   6.3 Density — 44px rows                      HANDOFF.md 1.3, 3.10 · TASK 4
   14px/400 --sm-ink-body on 12px vertical / 20px horizontal padding:
   12 + 20 (the body role's own line height) + 12 = 44px.

   Today it is 14px text on 15/20, 16/20, 14/20 and 14/24 paddings, giving
   46-50px rows. 20px is a component size, not a space step -- tokens.css
   section 8 says so; only the vertical 12px is on the scale.
   -------------------------------------------------------------------------- */

.customers-table-container tbody td,
.details-table-panel table tbody td,
.relationship-group-card td {
  padding: var(--sm-space-3) 20px;
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  font-weight: var(--sm-type-body-weight);
  color: var(--sm-ink-body);
  border-bottom-color: var(--sm-line);
}

/* Column headers are micro on the brand tint. HANDOFF.md 3.10.
   Replaces #e6f2f7 (customer pages) and #3DAFED33 (relationship list). */
.customers-table-container thead th,
.details-table-panel table thead th,
.master-relationship-table-container table thead th {
  background-color: var(--sm-brand-tint);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-micro-size);
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: var(--sm-type-micro-transform);
  padding: var(--sm-space-3) 20px;
  border-bottom: 1px solid var(--sm-line);
}

/* Row hover, no zebra. HANDOFF.md 4.9. */
.customers-table-container tbody tr:hover,
.details-table-panel table tbody tr:hover {
  background-color: var(--sm-surface-sunk);
}

.customers-table-container tbody tr {
  border-bottom-color: var(--sm-line);
}

/* The Name column leads and is body-strong. HANDOFF.md 1.2, 3.10. */
.bold-text-cell {
  font-weight: var(--sm-type-body-strong-weight);
  color: var(--sm-ink);
}

/* An em-dash empty cell and a "no records" sentence are not the same thing.
   #9ca3af is legal for the first and fails at 2.5:1 for the second
   (HANDOFF.md 2.8), and both of these are sentences.
   Session 11: the rule that stood here (--sm-ink-quiet, weight 400) was
   superseded in full by 8.6, which restyles the same two selectors later and
   with !important on every property (--sm-ink-muted, weight 500). The two
   disagreed and 8.6 always won, so this one was dead and has been removed.
   The empty state has one owner: 8.6. */

/* --------------------------------------------------------------------------
   6.4 The relationship list's grouped tbodies
   Each group is a <tbody class="relationship-group-card">, queried by the
   sort engine as tbody.relationship-group-card and toggled by
   .active-group-tab. Neither name is touched here -- only the card look:
   a 14px radius and a 15px-blur shadow become the card radius and --sm-e1
   plus a 1px ring, because depth is carried by the border (HANDOFF.md 2.10).
   The ring is a shadow rather than a border because border-collapse is
   separate on this table and a <tbody> border would not paint.
   -------------------------------------------------------------------------- */

.relationship-group-card {
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e1), 0 0 0 1px var(--sm-line);
}

.relationship-group-card tr:first-child td:first-child { border-top-left-radius: var(--sm-radius-card); }
.relationship-group-card tr:first-child td:last-child  { border-top-right-radius: var(--sm-radius-card); }
.relationship-group-card tr:last-child td:first-child  { border-bottom-left-radius: var(--sm-radius-card); }
.relationship-group-card tr:last-child td:last-child   { border-bottom-right-radius: var(--sm-radius-card); }

/* The cells carry their own background, so the hover has to be on the cell. */
.relationship-group-card tr:hover td {
  background-color: var(--sm-surface-sunk);
}

/* Parent rows are the one place weight 700 is allowed outside the logotype
   (HANDOFF.md 1.2). 18px is not a role; body-strong's size keeps the row at
   44px and the weight still separates parent from child. */
.parent-title-bold {
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-weight-bold);
  color: var(--sm-ink);
}

/* The child indent was padding-left: 80px on a cell whose own padding is
   20px. 24px of indent still reads as nesting and gives the column back
   36px in a table that now has to fit. */
.child-name-muted {
  /* !important: this selector is (0,1,0) and .relationship-group-card td is
     (0,1,1), so it would otherwise lose the padding it is trying to set. */
  padding-left: calc(20px + var(--sm-space-5)) !important;
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-muted) !important;   /* :131 declares it !important */
}

.master-relationship-table-container table thead th:first-child {
  border-top-left-radius: var(--sm-radius-card);
}
.master-relationship-table-container table thead th:last-child {
  border-top-right-radius: var(--sm-radius-card);
}

/* --------------------------------------------------------------------------
   6.5 Sort affordance
   data-column-index is positional and the ID column left, so every index
   shifted by one; that is a template change and it is in the same commit
   (HANDOFF.md 6.5). Only the colours are here.
   -------------------------------------------------------------------------- */

/* #d8eaf1 !important on a header that is now --sm-brand-tint. There is no
   darker brand tint in the system, so the hover lifts rather than deepens:
   --sm-brand-wash is the palette's lighter brand fill. */
.sortable-column-header:hover {
  background-color: var(--sm-brand-wash) !important;
}

.sort-arrows-indicator {
  color: var(--sm-ink-faint);              /* an inert glyph, not read text */
}

/* #10ba90 -- green means completion and nothing else (HANDOFF.md 4.3).
   A sort direction is an indicator, and indicators are --sm-brand (2.2). */
.sort-arrows-indicator .active-arrow {
  color: var(--sm-brand) !important;       /* :810 declares it !important */
  font-weight: var(--sm-weight-semibold);
}

/* --------------------------------------------------------------------------
   6.6 The profile card's field grid       DEFERRED-FIXES #13 · HANDOFF.md 2.11
   The five customer-detail states overflowed by 89-110px at 800 and by 41px
   at 768, with span.field-value as the rightmost element. The cause is a
   fixed two-track grid (minmax(180px,1fr) + minmax(240px,1.2fr) + a 4rem
   gutter) whose cells are flex rows with a 130px fixed label and a value that
   cannot shrink, because a flex item's automatic minimum size is its longest
   word -- and the longest word here is a 58-character email address.

   minmax(0,...) lets the tracks shrink, min-width: 0 lets the value shrink,
   and below 900px the two columns become one and the label sits above its
   value rather than beside it. Nothing is sized to a string (HANDOFF.md 2.11).
   -------------------------------------------------------------------------- */

.meta-field-columns-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--sm-space-6);
  row-gap: var(--sm-space-3);
}

.meta-item-cell {
  align-items: baseline;
  min-width: 0;
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
}

.meta-item-cell .field-label {
  color: var(--sm-ink-muted);              /* was #9ca3af — a label, not a placeholder */
  font-weight: var(--sm-weight-regular);
}

.meta-item-cell .field-value {
  min-width: 0;
  overflow-wrap: anywhere;
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
}

@media (max-width: 900px) {
  .meta-field-columns-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .meta-item-cell {
    flex-direction: column;
    align-items: flex-start;
  }

  .meta-item-cell .field-label {
    width: auto;
  }
}

/* --------------------------------------------------------------------------
   6.7 The pagination bar                                     HANDOFF.md 3.11
   The controls themselves were tokenised in 5.10. What is left is the rest of
   the bar: the entries count, the ellipsis and the Go-to field, all still on
   the retired slate scale, plus the bar's own behaviour when the page is
   narrow.

   The window ORDER (first · … · current-1 · current · current+1 · … · last,
   HANDOFF.md 2.13) is not reachable from here -- see the report and
   DEFERRED-FIXES #20.
   -------------------------------------------------------------------------- */

.customer-list-footer-pagination-bar {
  flex-wrap: wrap;
  gap: var(--sm-space-3);
  margin-top: var(--sm-space-4);
  margin-bottom: var(--sm-space-4);
}

/* #9ca3af at 14px is body text at 2.5:1. HANDOFF.md 2.8. */
.footer-entries-info-text {
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-quiet);
}

/* "Ellipses are not buttons" — HANDOFF.md 2.13. It stays text. */
.pagination-ellipsis-break {
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-quiet);
}

.jump-to-page-wrapper {
  border-color: var(--sm-line);
  border-radius: var(--sm-radius-control);
  box-shadow: none;
  gap: var(--sm-space-2);
}

.jump-label {
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-muted);
}

.jump-page-input {
  border-color: var(--sm-line-strong);
  border-radius: var(--sm-radius-control);
  color: var(--sm-ink);
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-regular);
}

.jump-page-input:focus {
  border-color: var(--sm-brand);
  box-shadow: var(--sm-focus);
}

/* Disabled is a border and a faint label, never opacity on a fill.
   HANDOFF.md 3.2.
   This repeats 5.6 on purpose -- do not delete it as a duplicate (checked in
   session 11). The dead pager arrow is .page-nav-btn.disabled-btn, and 5.10's
   .page-nav-btn sits between 5.6 and here at the same (0,1,0), so without
   this rule 5.10's white fill and --sm-ink-muted label win and the disabled
   arrow reads as live. */
.disabled-btn {
  opacity: 1;
  border-color: var(--sm-line);
  background-color: var(--sm-surface);
  color: var(--sm-ink-faint);
}

/* --------------------------------------------------------------------------
   6.8 Which way the identifier popover opens
   The popover is anchored to the chip, and the chip sits in whichever cell
   the template put it in. In the four row-level tables that is the actions
   cell, which is the LAST column, so the popover has to grow leftwards or it
   leaves the table. On customer detail's relationship tab there is no actions
   column and the chip sits in the name cell, where the mirror is true:
   anchored right, it grew past the left edge of the viewport (measured at
   x = -104 at 390px). Anchor follows position.
   -------------------------------------------------------------------------- */

.row-identifier > .row-identifier-value {
  left: 0;
  right: auto;
}

td:last-child .row-identifier > .row-identifier-value {
  left: auto;
  right: 0;
}

/* --------------------------------------------------------------------------
   6.9 The furniture around the tables at narrow widths
   --------------------------------------------------------------------------
   Putting the tables in scroll containers took the four pages' overflow down
   to the point where the remaining intrinsic minimum is no longer the table:
   it is the single-row flex bars around it. Measured at 390px, with the
   tables already contained:

     .pagination-suite-container   413px   both list pages
     .relationship-tabs-bar        ~510px  relationship list (3 tab labels)
     .tabs-nav-list-wrapper        ~500px  customer detail (5 tab labels)
     .unified-filter-group         ~680px  customer list (4 filters + search)

   None of them can shrink, so .page-center-boundary centres a child wider
   than itself and splits the overflow both ways -- which is what puts the
   relationship list's own page title at x = -121 and makes the left half
   unreachable (DEFERRED-FIXES #14). A row that wraps has no intrinsic
   minimum beyond its widest single item.
   -------------------------------------------------------------------------- */

.pagination-suite-container {
  flex-wrap: wrap;
  gap: var(--sm-space-3);
}

.relationship-tabs-bar {
  flex-wrap: wrap;
  row-gap: var(--sm-space-2);
}

.tabs-header-action-row {
  flex-wrap: wrap;
  gap: var(--sm-space-3);
}

.tabs-nav-list-wrapper {
  flex-wrap: wrap;
  row-gap: var(--sm-space-2);
}

.relationship-page-header,
.header-actions-flex-wrapper {
  flex-wrap: wrap;
  gap: var(--sm-space-3);
}

/* Below 768 the four filter dropdowns take a row each. Not for the width --
   6.1 already wraps them -- but for their MENUS: .dropdown-menu-content is
   anchored right: 0 to its trigger, and .calendar-window-frame is a fixed
   290px, so a trigger sitting at x = 192 put the calendar's left edge at
   x = -98, off the screen with no way to scroll to it. Against a full-width
   trigger a right-anchored 290px panel always lands inside the viewport. */
@media (max-width: 768px) {
  .unified-filter-group .custom-dropdown {
    flex: 1 1 100%;
  }

  .unified-filter-group .dropdown-trigger {
    width: 100%;
    justify-content: space-between;
  }
}

/* --------------------------------------------------------------------------
   6.10 The profile card below 768
   The last 8px of overflow on the two detail pages at 390. The card is a flex
   row of a 110px avatar, a 2.5rem gap and an info block, and the info block's
   automatic minimum size is the longest word in the customer's display name.
   Measured: the block resolved to 160px inside a 97px slot.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .profile-summary-card {
    padding: var(--sm-space-4);
  }

  .card-profile-split-wrapper {
    gap: var(--sm-space-4);
  }

  .profile-info-grid-block {
    min-width: 0;
  }

  .customer-full-display-name {
    overflow-wrap: anywhere;
  }
}

/* --------------------------------------------------------------------------
   6.11 The actions cell holds two things now
   The identifier chip sits beside the existing action link. The column is
   content-width, so without this the chip wraps under the icon and the row
   grows from 44px to 70px — which would undo 6.3 on every row of every one
   of these tables. nowrap makes the column ask for the width of both.
   -------------------------------------------------------------------------- */

.customers-table-container tbody td:last-child:not([colspan]),
.details-table-panel table tbody td:last-child:not([colspan]),
.relationship-group-card td:last-child:not([colspan]) {
  white-space: nowrap;
  /* line-height: 0 removes the cell's strut. Without it the row is 50px even
     though the padding is right: the chip and the 18px action icon sit on a
     line box that also carries a 20px strut and its descender space, and the
     sum is 26px of content inside 24px of padding. The cell holds no text --
     only the icon and the chip, each of which sets its own line height -- so
     dropping the strut costs nothing and the line box becomes the taller of
     the two boxes. :not([colspan]) keeps the empty-state row out of it; that
     cell IS a last child and it is all text. */
  line-height: 0;
}

.customers-table-container tbody td:last-child > *,
.details-table-panel table tbody td:last-child > *,
.relationship-group-card td:last-child > * {
  vertical-align: middle;
}

/* --------------------------------------------------------------------------
   6.12 The badge's geometry, and only its geometry       HANDOFF.md 2.11, 3.4
   .badge-pill carries min-width: 85px, which HANDOFF.md 2.11 retires by name
   -- it is a cell sized to a string, in the Type column of two of these four
   tables. Its 5px/12px box is also 30px tall, which sets the row height for
   every row that has a badge in it.

   The canonical badge is 13px/600 on 4px 12px with no min-width: 26px tall,
   so a row carrying one is ~50px rather than 44px. That is as close as the
   table can get while the badge is still a separate component; its two tones
   (HANDOFF.md 2.4, 2.5) belong to the badge task and are not touched here.

   The inline style="min-width: 95px" on customer detail's relationship tab
   beats any selector and is logged rather than fought.
   -------------------------------------------------------------------------- */

.badge-pill {
  min-width: 0;
  padding: var(--sm-space-1) var(--sm-space-3);
  border-radius: var(--sm-radius-pill);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
}

/* --------------------------------------------------------------------------
   6.13 Two rules at the end of relationship_list_styles.css
   --------------------------------------------------------------------------
   THE CELL FILL. .parent-row td and .child-row td are #edf5f9 at :244 and
   :248 -- one of the three values HANDOFF.md 3.10 names when it replaces this
   table ("header #3dafed33, 12px separated rows, #edf5f9 cells"). 6.4 sets the
   group card's own background but not the cells', so the tint survived and
   every row read as tinted with white gutters between them. Tables are
   --sm-surface and the row state is carried by hover alone (HANDOFF.md 4.9).

   THE COLUMN SHIFT. :234 and :239 select td:nth-child(2), which was the
   Relationship Name column and is now Relationship Type, because the ID
   column left (HANDOFF.md 6.5 -- positional selectors, same commit). They
   were left-aligning the name and indenting the child name by 60px, and they
   are now doing both to a centred badge. The two cells they land on carry
   .center-column-items, which is the markup saying what it wants, so that is
   what is restored. The child-row indent lives on .child-name-muted (6.4).
   -------------------------------------------------------------------------- */

.parent-row td,
.child-row td {
  background-color: var(--sm-surface);
}

.parent-row td.center-column-items,
.child-row td.center-column-items {
  padding-left: 20px;
  text-align: center;
}

/* The hover has to beat the two rules above, which are (0,1,1) like 6.4's. */
.relationship-group-card tr:hover td.center-column-items,
.relationship-group-card tr:hover td {
  background-color: var(--sm-surface-sunk);
}

/* ==========================================================================
   7. THE ARBITRATION SCREENS                 HANDOFF.md 3.12, 2.3, 2.9, 4.8
   --------------------------------------------------------------------------
   Matching, Name / Address / Email / Phone selection, and the compiled record
   on Review (whose classes the two Relationship pages share, via
   tagging_styles.css). These screens exist for one job: seeing which value
   differs between candidate records.

   WHAT THIS SECTION CAN AND CANNOT DO
   CSS cannot read text content, so it cannot compare the value in one card
   with the value in another. There is no character-level diff here and none
   is faked. What is here:

     7.1  the four field families as local variables, so a card names its
          family once and every slot (tint, mark, ink, highlight) follows
     7.2  the selection card: white body, 3px mark edge, tint on the header
          band only (HANDOFF.md 4.10)
     7.3  rows on a fixed 38px rhythm, 13px labels, 14/600 values
          (HANDOFF.md 4.8), so the same field sits at the same offset in
          every card of a deck
     7.4  empty fields as a fixed-height muted row -- NOT removed, because
          the deck pager divides by the first card's height (HANDOFF.md 6.6)
     7.5  THE DIFF: the hook a future diff script needs, and the one diff
          signal the page's own JavaScript already computes
     7.6 - 7.10  matching, Review, the step header, the deck pagers, and
          the reservation rule

   THE HEIGHT-SYNC (HANDOFF.md 6.1, BRIEF 6.1). The decks' heights are
   written inline by script from the left column's measured height, and the
   pager steps by the distance between card 0 and card 1. Nothing here sets
   a deck height. Everything here keeps cards in one deck the same height by
   giving every row a fixed minimum and every card the same chrome.

   Local variables are prefixed --arb-, not --sm-: they are aliases scoped
   to one component, not design-system tokens, and they never hold a raw
   value -- only var(--sm-*).
   ========================================================================== */

/* --------------------------------------------------------------------------
   7.1 Field families                                     HANDOFF.md 1.1, 2.3
   Name is the default: on the Name screen the cards carry no family class
   (.matched-record, .artifact-record, .new-record). Address, email and phone
   cards all carry one, except the incoming address card, which is found by
   its body.
   RESERVED HUES: nothing outside these four families may read --arb-*.
   -------------------------------------------------------------------------- */

.top-cards .record,
:is(.attributes-sections, .potential-match) .name-section,
.name-wrapper,
#altName {
  --arb-tint: var(--sm-field-name-tint);
  --arb-shade: var(--sm-field-name-shade);   /* 22.3: the NetSuite band */
  --arb-mark: var(--sm-field-name-mark);
  --arb-ink:  var(--sm-field-name-ink);
  --arb-hl:   var(--sm-field-name-hl);
}

.top-cards .record.matched-record-address,
.top-cards .record.address-artifact-record,
.top-cards .new-record:has(> .address-attributes),
:is(.attributes-sections, .potential-match) .address-section,
:is(.attributes-sections, .potential-match) .address-container,
.address-wrapper,
#altAddress {
  --arb-tint: var(--sm-field-addr-tint);
  --arb-shade: var(--sm-field-addr-shade);   /* 22.3: the NetSuite band */
  --arb-mark: var(--sm-field-addr-mark);
  --arb-ink:  var(--sm-field-addr-ink);
  --arb-hl:   var(--sm-field-addr-hl);
}

.top-cards .record.email-matched-record,
.top-cards .record.email-artifact-record,
.top-cards .record.email-new-record,
:is(.attributes-sections, .potential-match) .email-section,
.email-wrapper,
#altEmail {
  --arb-tint: var(--sm-field-email-tint);
  --arb-shade: var(--sm-field-email-shade);   /* 22.3: the NetSuite band */
  --arb-mark: var(--sm-field-email-mark);
  --arb-ink:  var(--sm-field-email-ink);
  --arb-hl:   var(--sm-field-email-hl);
}

.top-cards .record.phone-matched-record,
.top-cards .record.phone-artifact-record,
.top-cards .record.phone-new-record,
:is(.attributes-sections, .potential-match) .phone-section,
.phone-wrapper,
#altPhone {
  --arb-tint: var(--sm-field-phone-tint);
  --arb-shade: var(--sm-field-phone-shade);   /* 22.3: the NetSuite band */
  --arb-mark: var(--sm-field-phone-mark);
  --arb-ink:  var(--sm-field-phone-ink);
  --arb-hl:   var(--sm-field-phone-hl);
}

/* --------------------------------------------------------------------------
   7.2 The selection card                                     HANDOFF.md 3.12
   Eight rules become one component. What each was:
     .matched-record          #f9e0a6   .artifact-record         #f6e9cc
     .matched-record-address  #bad7eb   .address-artifact-record #d7e5ef
     .email-matched-record    #d0dbfe   .email-artifact-record   #d3dcf3
     .phone-matched-record    #edd8fe   .phone-artifact-record   #f0e5f8
   -- a saturated fill under 12px text, and a paler second tier for artifacts.
   Both tiers go: HANDOFF.md 4.1 makes artifact, alternate and variant
   visually identical, so a paler artifact card was asserting a hierarchy the
   product has not established.

   .new-record carried the heaviest shadow in the product
   (rgba(14,30,37,.32) 0 2px 16px, HANDOFF.md 1.5). It is a card that sits
   flat on the page, so it gets --sm-e1 like every other card (2.10).

   The header band is full-bleed: the card has no padding of its own, the band
   carries the inset, and overflow: hidden clips the band to the radius.
   -------------------------------------------------------------------------- */

.top-cards .record,
.left-cards .new-record {
  box-sizing: border-box;
  padding: 0;
  overflow: hidden;
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-left: 3px solid var(--arb-mark);
  border-radius: var(--sm-radius-card);            /* was 20px */
  box-shadow: var(--sm-e1);
}

/* The band. The approved card sets this label as micro (11px). It stays at
   the small role's size (13px) with micro's weight, tracking and case:
   DEFERRED-FIXES #36 was decided in session 11 -- the 13px floor wins for
   anything a steward reads repeatedly, and the band ("NEW EVENT RECORD",
   "NETSUITE RECORD") is read on every card of every deck. Contrast 7.8's
   step number, which is glanced at and does take micro. */
.top-cards .record > p.heading,
.right-cards .record > p.heading {
  margin: 0;
  padding: var(--sm-space-3) var(--sm-space-4);
  background-color: var(--arb-tint);
  color: var(--arb-ink);
  text-align: left;
  font-size: var(--sm-type-small-size);            /* was 20px / 18px */
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-micro-weight);        /* was 530 */
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: var(--sm-type-micro-transform);
}

.top-cards .record > .attributes {
  padding: var(--sm-space-1) var(--sm-space-4) var(--sm-space-2);
}

/* The deck well behind the right-hand column. */
.right-cards {
  background-color: var(--sm-surface-sunk);        /* was #f3f2f2 */
  border-radius: var(--sm-radius-card);            /* was 20px */
}

/* --------------------------------------------------------------------------
   7.3 Rows                                         HANDOFF.md 2.9, 4.8, 1.3
   The 12px at .right-cards .line p and .address-attributes p is the exact
   defect HANDOFF.md 2.9 names. Labels go to small (13px, ink-muted), values
   to body-strong (14/20/600, ink).

   Every row gets the same 38px minimum -- the handoff's control height, and
   the height the approved card's rows land on (9 + 20 + 9). A fixed row
   minimum is what keeps a deck's cards equal-height when one card's field is
   empty and another's is not.

   The label column is a fixed share of the row on every card, so values
   start at the same x in every card of a deck and in the incoming card
   beside it. Dashed dividers become the one border colour.
   -------------------------------------------------------------------------- */

.top-cards .record .line {
  box-sizing: border-box;
  min-height: 38px;
  padding-block: var(--sm-space-1);
  gap: var(--sm-space-3);
  align-items: center;
  border-bottom: 1px solid var(--sm-line);         /* was 1px dashed, currentColor */
}

.top-cards .record .line:last-child {
  border-bottom-color: transparent;                /* keep the 1px so every row is the same box */
}

/* Labels. The generic case: a paragraph directly in a row. */
.top-cards .record .line > p {
  margin: 0;
  min-width: 0;
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);            /* was 12px */
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-small-weight);
  word-break: normal;                              /* was break-all */
  overflow-wrap: anywhere;
}

.top-cards .record .line > p:first-child {
  flex: 0 0 32%;
  width: auto;                                     /* was 33% / 40% */
}

/* Values. Name screen: p.field-value. Address screen: the second paragraph. */
.top-cards .record .line > p.field-value,
.top-cards .record .address-attributes .line > p:nth-child(2) {
  flex: 1 1 0;
  width: auto;                                     /* was 60% on address */
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);      /* was 12px / 14px */
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);  /* was 400 */
  font-variant-numeric: tabular-nums;              /* zip, suite and house numbers line up digit for digit */
}

.top-cards .record .address-attributes .line > p {
  padding-block: 0;                                /* was 0.2rem */
}

/* Email and phone rows have no label: the paragraph IS the value. The one
   exception is the Count / Date row, which has a label and a .count_date. */
.top-cards .record :is(.email-attributes, .email-attributes-staging,
                       .phone-attributes, .phone-attributes-staging)
  .line:not(:has(> .count_date)) > p {
  flex: 1 1 0;
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  font-variant-numeric: tabular-nums;
}

.top-cards .record :is(.email-attributes, .email-attributes-staging,
                       .phone-attributes, .phone-attributes-staging) .line {
  gap: var(--sm-space-3);                          /* was 8% */
  margin: 0;
}

/* Email values run to 65+ characters (BRIEF 5.3). Beside two selects they
   wrapped to three and four lines, at different counts in different cards,
   which is what made the email deck's cards unequal (140/122/122/117px
   before this change). The value gets the row's full width on a line of its
   own and the two selects drop beneath it, right-aligned. The ROW reserves
   two value lines plus the controls, so a one-line and a two-line address
   produce the same card, while the value itself stays the size of its text
   -- which is what lets a highlight hug the address instead of painting a
   two-line block behind a one-line value.

   The line break is a zero-height ::after with a 100% basis, ordered between
   the value and the controls. It is a flex item of the row; no markup. */
.top-cards .record :is(.email-attributes, .email-attributes-staging)
  .line:not(:has(> .count_date)) {
  flex-wrap: wrap;
  align-content: flex-start;
  justify-content: flex-end;
  row-gap: var(--sm-space-1);
  padding-block: var(--sm-space-2);
  /* 2 value lines + the gap + a 30px control + the row's padding */
  min-height: calc(2 * var(--sm-type-body-strong-leading) + var(--sm-space-1)
                   + 30px + 2 * var(--sm-space-2));
}

.top-cards .record :is(.email-attributes, .email-attributes-staging)
  .line:not(:has(> .count_date))::after {
  content: "";
  order: 1;
  flex: 0 0 100%;
  height: 0;
}

.top-cards .record :is(.email-attributes, .email-attributes-staging)
  .line:not(:has(> .count_date)) > p {
  order: 0;
  flex: 0 1 auto;
  max-width: 100%;
  margin: 0 auto 0 0;                              /* was margin-right: 15px */
}

.top-cards .record :is(.email-attributes, .email-attributes-staging)
  .line:not(:has(> .count_date)) > :is(select, .options-wrapper) {
  order: 2;
}

/* The Count / Date row: a label, an inert count chip, a date select. */
.top-cards .record .line > div,
.top-cards .record .line > .count_date {
  display: flex;
  align-items: center;
  gap: var(--sm-space-2);
  padding-left: 0;                                 /* was 40px */
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
}

.top-cards .record .line > div > p {
  margin: 0;
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
}

.top-cards .record .line > div span,
.top-cards .record .line > .count_date > span {
  display: inline-block;
  min-width: 20px;
  padding: 0 var(--sm-space-1);
  border-radius: var(--sm-radius-inline);          /* was 5px */
  background-color: var(--sm-surface-sunk);        /* was #fff on a tint */
  color: var(--sm-ink-body);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Selects inside a card: the small control, on white, with a real border.
   Replaces border: none on #ededed / #f3f2f2 / #fff, radius 12px, and
   .options-wrapper select's 1px solid #333. */
.top-cards .record select {
  box-sizing: border-box;
  height: 30px;
  min-width: 0;
  margin: 0;
  padding: 0 var(--sm-space-2);
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-radius-control);
  background-color: var(--sm-surface);
  color: var(--sm-ink-body);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-small-weight);
  text-align: left;
  cursor: pointer;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}

.top-cards .record select:hover {
  background-color: var(--sm-surface);             /* was #e9e8e8 */
  border-color: var(--sm-ink-quiet);
}

.top-cards .record select:focus-visible {
  outline: none;
  border-color: var(--sm-brand);
  box-shadow: var(--sm-focus);
}

/* Widths. The type select on the name screen, the two selects on an email or
   phone row: one fixed width, so the value column is the same width on every
   card. The date select sizes to its date. */
.top-cards .record .line > select {
  flex: 0 0 7.5rem;
  width: 7.5rem;
}

/* .options-wrapper is a COLUMN flexbox, so a flex-basis here would be a
   height. Width only. */
.top-cards .record .line .options-wrapper select {
  flex: none;
  width: 7.5rem;
}

.top-cards .record .line select.date-selection {
  flex: 0 0 auto;
  width: auto;
}

.top-cards .record .line .options-wrapper {
  flex: 0 0 auto;
  width: auto;
  min-width: 0;                                    /* was 24% / 25% */
  background: none;
}

.phone-selection {
  margin-right: 0;                                 /* was 30px, name_selection_styles.css */
}

/* Address: the column of four selects beside the six rows. */
.top-cards .record .address-attributes {
  gap: var(--sm-space-3);
  align-items: flex-start;
}

.top-cards .record .address-attributes .attributes-wrapper {
  flex: 1 1 0;
  min-width: 0;
  width: auto;                                     /* was 75% */
}

.top-cards .record .address-attributes .options-wrapper {
  flex: 0 0 9rem;
  width: auto;                                     /* was 24% */
  box-sizing: border-box;
  align-self: stretch;
  justify-content: flex-start;
  gap: var(--sm-space-2);
  margin-block: var(--sm-space-1);
  padding: var(--sm-space-2);
  background-color: var(--sm-surface-sunk);        /* was #e9e8e8 */
  border-radius: var(--sm-radius-control);         /* was 12px */
}

.top-cards .record .address-attributes .options-wrapper select {
  width: 100%;                                     /* was 65% / 80% */
}

/* Disabled: HANDOFF.md 3.2 -- border --sm-line, text --sm-ink-faint,
   not-allowed. Not opacity on a fill. */
.top-cards .record select:disabled,
.top-cards .record select.disabled-dropdown {
  opacity: 1;                                      /* was 0.5 */
  background-color: var(--sm-surface-sunk);        /* was #f0f0f0 */
  border-color: var(--sm-line);
  color: var(--sm-ink-faint);                      /* was #999 */
  cursor: not-allowed;
}

.disabled-line p,
.disabled-dropdown ~ p {
  color: var(--sm-ink-quiet);                      /* was #999 (2.8:1); quiet is the reading floor */
}

/* 7.3.1 Phone widths. BRIEF 8.5 does not require the comparison to work at
   390px, but the decks must not get worse there. The original <=768px rule
   (name_selection_styles.css) lets every row wrap, so at a phone width a row
   wrapped or not depending on how long its VALUE was -- "Jon" stayed on one
   line, "Johnathan" pushed its select down -- and cards in one deck came out
   different heights. Below 600px every row takes one fixed shape instead:
   the value line, then the controls on a line of their own. The shape no
   longer depends on the data, so the deck stays equal. */
@media (max-width: 600px) {
  /* Name: label + value, then the select under the value. */
  .top-cards .record .line:has(> p.field-value) {
    flex-wrap: wrap;
    row-gap: var(--sm-space-1);
  }

  .top-cards .record .line:has(> p.field-value) > p.field-value {
    flex: 1 1 calc(68% - var(--sm-space-3));
  }

  .top-cards .record .line:has(> p.field-value) > select {
    margin-left: calc(32% + var(--sm-space-3));
  }

  /* Phone: the email shape (7.3) -- value, then both selects. */
  .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
    .line:not(:has(> .count_date)) {
    flex-wrap: wrap;
    justify-content: flex-end;
    row-gap: var(--sm-space-1);
  }

  .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
    .line:not(:has(> .count_date)) > p {
    flex: 1 0 100%;
  }

  /* Email: three value lines at this width, not two. */
  .top-cards .record :is(.email-attributes, .email-attributes-staging)
    .line:not(:has(> .count_date)) {
    min-height: calc(3 * var(--sm-type-body-strong-leading) + var(--sm-space-1)
                     + 30px + 2 * var(--sm-space-2));
  }

  /* Address: the select column moves under the six rows, two per line. */
  .top-cards .record .address-attributes {
    flex-wrap: wrap;
  }

  .top-cards .record .address-attributes .attributes-wrapper,
  .top-cards .record .address-attributes .options-wrapper {
    flex: 1 0 100%;
  }

  .top-cards .record .address-attributes .options-wrapper {
    flex-direction: row;
    flex-wrap: wrap;
  }

  .top-cards .record .address-attributes .options-wrapper select {
    width: calc(50% - var(--sm-space-1));
  }
}

/* --------------------------------------------------------------------------
   7.4 Empty fields -- the fixed-height muted row        HANDOFF.md 3.12, 6.6
   The page's own script marks an empty field: initializePageUI() puts
   .disabled-dropdown on the select of every row whose value is "", "-",
   "none", "null" or "undefined". The same class also goes on an INCOMING
   row whose value duplicates one on file, so duplicates are excluded here by
   the hidden option the script selects for them.

   The row keeps its full 38px and its divider. Removing it would change the
   card's height and break the pager arithmetic. What collapses is the ink:
   the label drops to quiet, the dash to faint, and the disabled select --
   which offers nothing on an empty field -- stops drawing.

   Name screen only. Address, email and phone rows carry no empty marker the
   stylesheet can see (docs/DEFERRED-FIXES.md).
   -------------------------------------------------------------------------- */

.top-cards .record .line:has(> select.disabled-dropdown):not(:has(option[value="duplicate"]:checked)) > p {
  color: var(--sm-ink-quiet);
}

.top-cards .record .line:has(> select.disabled-dropdown):not(:has(option[value="duplicate"]:checked)) > p.field-value {
  color: var(--sm-ink-faint);                      /* em-dash empty cell: HANDOFF.md 2.8 permits faint */
  font-weight: var(--sm-type-body-weight);
}

.top-cards .record .line:has(> select.disabled-dropdown):not(:has(option[value="duplicate"]:checked)) > select {
  visibility: hidden;                              /* keeps its box: the row does not move */
}

/* --------------------------------------------------------------------------
   7.5 THE DIFF                                            HANDOFF.md 1.1, 3.12
   The highlight slot (--sm-field-*-hl) exists for one purpose: the <mark>
   behind differing characters. It is the most load-bearing colour in the
   system and nothing else may use it.

   7.5.1 THE HOOK -- styled now, emitted by nothing yet.
   A diff script (or the template) needs to do exactly two things, and the
   rendering is already here:

     <p class="field-value">Joh<mark>n</mark>athan</p>
         characters that differ from the incoming value, wrapped in <mark>

     <div class="line line-1 value-differs">
         the row whose value differs from the incoming card's value for the
         same field -- lets the eye land on the row before reading the word

   Both work inside any family in 7.1, on every screen, in the field's own
   hue. Neither changes a box size, so neither disturbs the height-sync: the
   mark has no vertical padding and the row treatment is paint only.
   -------------------------------------------------------------------------- */

:is(.top-cards .record, .attributes-sections, .left-container-relationship, .alt-section) mark {
  padding: 0 1px;
  border-radius: var(--sm-radius-inline);
  background-color: var(--arb-hl);
  color: var(--arb-ink);
  font-weight: inherit;
}

.top-cards .record .line.value-differs > p:first-child,
.attributes-sections .line.value-differs > p:first-child {
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
}

.top-cards .record .line.value-differs > p.field-value,
.top-cards .record .address-attributes .line.value-differs > p:nth-child(2),
.top-cards .record :is(.email-attributes, .email-attributes-staging,
                       .phone-attributes, .phone-attributes-staging)
  .line.value-differs:not(:has(> .count_date)) > p,
.attributes-sections .line.value-differs > p:nth-child(2) {
  /* A whole-value highlight in the field's hue. box-shadow rather than
     padding, so the text does not move and the row does not grow. The
     value keeps its column's start (content-sized + an auto right margin), so
     highlighted and plain values still begin at the same x. */
  flex: 0 1 auto;                                  /* content-sized: the value column's 0 basis would collapse it */
  width: auto;
  max-width: 100%;
  margin-right: auto;
  justify-self: start;
  background-color: var(--arb-hl);
  box-shadow: 0 0 0 3px var(--arb-hl);
  border-radius: var(--sm-radius-inline);
  color: var(--arb-ink);
}

/* --------------------------------------------------------------------------
   7.5.2 WHAT THE PAGE ALREADY KNOWS -- the incoming card's new values
   The selection scripts compare every INCOMING value with every value on
   file for the same field (trimmed, case-folded, exact), and lock the
   incoming row -- .disabled-dropdown on its select -- when it matches.
   So on the incoming card, a row with a value whose select is still live
   holds a value that NO card on the page has. That is a real comparison,
   computed by the page, and it is exactly the row the steward must decide.

     Name     applyStagingLogic()              per field
     Email    PART 2b, matchedEmailValues      per email
     Phone    the same block on phone          per phone
     Address  per WHOLE address (all five fields at once) -- it says the
              address is new, not which field is, so it is NOT marked:
              highlighting five rows would claim five differences.

   It does not say which candidate differs, or which characters. That needs
   7.5.1's hook and a script (docs/DEFERRED-FIXES.md).

   Both passes run on every load, including a restored session (neither is
   gated on sessionStorage), so the mark is stable across Back navigation.
   The selectors below are 7.5.1's .value-differs, spelled out as the state
   the script leaves behind; a script that adds .value-differs gets the same
   paint on every card.
   -------------------------------------------------------------------------- */

.top-cards .new-record .line:has(> p.field-value):has(> select:not(.disabled-dropdown)) > p:first-child {
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
}

.top-cards .new-record .line:has(> p.field-value):has(> select:not(.disabled-dropdown)) > p.field-value,
.top-cards .new-record .line:has(> .email-type-selector:not(.disabled-dropdown):not([data-email-value="-"]):not([data-email-value=""])) > p,
.top-cards .new-record .line:has(> .phone-type-selector:not(.disabled-dropdown):not([data-phone-value="-"]):not([data-phone-value=""])) > p {
  flex: 0 1 auto;                                  /* content-sized: the value column's 0 basis would collapse it */
  width: auto;
  max-width: 100%;
  margin-right: auto;
  background-color: var(--arb-hl);
  box-shadow: 0 0 0 3px var(--arb-hl);
  border-radius: var(--sm-radius-inline);
  color: var(--arb-ink);
}

/* --------------------------------------------------------------------------
   7.6 Matching -- the incoming card and the scored candidates    BRIEF 5.3
   Here the four families are blocks inside one card rather than cards of
   their own, and there is no header band to carry the tint, so the block
   itself carries it -- at the tint value, with the 3px mark on its left
   edge. Replaced, per card:
     incoming    #fae1a7  #bad7eb  #d0dbfe  #edd8fe
     candidate   #fcefd2  #d7e5ef  #d3dcf3  #f0e5f8
   #fae1a7 and #fcefd2 are two more name ambers that HANDOFF.md 1.1's
   "Replaced" list does not name. The two-tier split goes for the same
   reason as 7.2's.

   The candidates are compared ACROSS the deck, left to right, so what
   matters is that a field sits at the same height in every card. The label
   column was 40% on the incoming card, 50% on a candidate and 35% on email
   and phone; it is one fixed width everywhere, so values start at one x.
   Email -- the value that wrapped to four lines in a 168px column -- moved
   under its label at the block's full width; round 2 (item 12) put it back
   on the shared label | value row, with two value lines reserved.
   -------------------------------------------------------------------------- */

.main-content .top-cards .right-card-box {
  background-color: var(--sm-surface-sunk);        /* was #e6ecee */
  border-radius: var(--sm-radius-card);            /* was 30px x4 */
}

.top-cards .left-card-box .new-record-card,
.cards-container .potential-match-card {
  border-radius: var(--sm-radius-card);            /* was 25px */
  box-shadow: var(--sm-e1);                        /* was the .32 16px shadow */
}

.top-cards .left-card-box .new-record-card,
.cards-container .potential-match-card:not(.selected-card) {
  border: 1px solid var(--sm-line);                /* .selected-card's own border is 13.1 */
}

.new-record-card > p:first-of-type,
.potential-match-card > p:first-of-type {
  font-size: var(--sm-type-subtitle-size);
  line-height: var(--sm-type-subtitle-leading);
  font-weight: var(--sm-type-subtitle-weight);     /* was bold */
  color: var(--sm-ink);
}

.attributes-sections,
.potential-match {
  gap: var(--sm-space-3);                          /* was 20px / 15px */
}

/* The blocks. In a candidate the address block is .address-container
   wrapping .address-section; the container carries the chrome and the
   section inside it goes flat. */
:is(.attributes-sections, .potential-match) > :is(.name-section, .address-section,
                                                    .email-section, .phone-section),
:is(.attributes-sections, .potential-match) > .address-container {
  box-sizing: border-box;
  padding: var(--sm-space-2);                      /* was 10px / 3% */
  background-color: var(--arb-tint);
  border-left: 3px solid var(--arb-mark);
  border-radius: var(--sm-radius-control);
}

:is(.attributes-sections, .potential-match) > .address-container > .address-section {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}

/* Rows. .email-section and .phone-section are themselves a .line.
   The label column is narrow on purpose: a label that wraps ("Company /
   Name") wraps identically in every card, because every card has the same
   labels, so it cannot misalign the deck. A VALUE that wraps is data and
   wraps in one card only -- so the width goes to the value. At 6rem the
   candidate's value column is 186px, which holds "4412 Nacogdoches Road"
   (174px at 14/600) on one line; at the previous 40-50% it did not. */
:is(.attributes-sections, .potential-match) :is(.name-section, .address-section) .line,
:is(.attributes-sections, .potential-match) > :is(.email-section, .phone-section) {
  grid-template-columns: 6rem minmax(0, 1fr);      /* was 40% / 50% / 35% */
  column-gap: var(--sm-space-2);
  align-items: baseline;
  min-height: 28px;
  box-sizing: border-box;
  border-bottom: 1px solid var(--sm-line);         /* was dashed */
}

:is(.attributes-sections, .potential-match) :is(.name-section, .address-section) .line:last-child,
:is(.attributes-sections, .potential-match) > :is(.email-section, .phone-section) {
  border-bottom-color: transparent;
}

:is(.attributes-sections, .potential-match) p {
  padding-inline: 0;                               /* was 0.5em */
  padding-block: var(--sm-space-1);
  margin: 0;
  color: var(--sm-ink-muted);                      /* was #555 */
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-small-weight);        /* was 500 (the global p rule) */
  white-space: normal;                             /* matching_styles.css sets every p nowrap */
}

:is(.attributes-sections, .potential-match) .line p:nth-child(2),
:is(.attributes-sections, .potential-match) > :is(.email-section, .phone-section) > p:nth-child(2) {
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);      /* was 13px */
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  font-variant-numeric: tabular-nums;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: normal;
}

/* Round 2 (checklist item 12): email takes the same label | value row as
   name, address and phone. It had its own one-column shape here, the label
   above and the value below at the block's full width, which read as a
   different component. It keeps its two reserved value lines, now inside
   the value column: emails are the value most likely to wrap, and the
   reserve keeps the phone row level across the incoming card and every
   candidate when one email wraps and another does not. Delete the
   min-height to drop it; nothing else depends on it. A candidate with a
   second email or phone renders a second <p>; it goes under the first
   value, not under the label. */
:is(.attributes-sections, .potential-match) > .email-section > p:nth-child(2) {
  min-height: calc(2 * var(--sm-type-body-strong-leading));
  align-items: flex-start;       /* the global p rule centres: a one-line email sat mid-reserve */
}

:is(.attributes-sections, .potential-match) > :is(.email-section, .phone-section) > p:nth-child(n + 3) {
  grid-column: 2;
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   7.7 The compiled record -- Review, and Relationships   HANDOFF.md 3.12, 1.1
   .name-wrapper, .address-wrapper, .email-wrapper and .phone-wrapper are
   defined twice, in review_styles.css and in tagging_styles.css, for the
   same component on Review and on the two Relationship branches. Replaced:
     name     #f9e0a6                  address  #bad7eb, #dcebf6 (tags)
     email    #d0dbfe, #e7edfe (tags)  phone    #edd8fe, #e7edfe (tags)
   The phone tags were EMAIL's lavender (#e7edfe, review_styles.css and
   tagging_styles.css .phone-tags p) -- a field hue on the wrong field, which
   HANDOFF.md 2.3 forbids outright. They take phone's tint here.

   Blocks keep their tint (there is no header band on this component) and
   the label block carries the 3px mark, so each family reads the same way it
   does on the selection screens. Text: 13px labels and tags, 14/600 values.
   The Relationship pages set this record at 12px
   (tagging_styles.css .relationship-record-container p); that is lifted too,
   because it is the same component.
   -------------------------------------------------------------------------- */

.left-container-relationship,
.relationship-record-container .left-container-relationship {
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);            /* was 25px */
  box-shadow: var(--sm-e1);                        /* was the .32 16px shadow */
}

.name-wrapper > div,
.address-fields,
.address-values,
.address-wrapper .address-tags,
.email-wrapper > p,
.email-values,
.phone-wrapper > p,
.phone-values,
.email-tags p,
.email-tags input,
.phone-tags p,
.phone-tags input {
  background-color: var(--arb-tint);
}

.name-wrapper > .left-names,
.address-fields,
.email-wrapper > p:first-child,
.phone-wrapper > p:first-child {
  box-shadow: inset 3px 0 0 var(--arb-mark);
}

.name-wrapper > div,
.address-fields,
.address-values,
.address-wrapper .address-tags,
.email-values,
.phone-values,
.email-wrapper > p:first-child,
.phone-wrapper > p:first-child,
.email-tags p,
.phone-tags p {
  border-radius: var(--sm-radius-control);         /* was 8px -- same value, now the token */
}

.left-container-relationship :is(.name-wrapper, .address-wrapper, .email-wrapper, .phone-wrapper) p {
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);            /* was 12px on Relationships */
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-small-weight);
}

.left-container-relationship .right-names p,
.left-container-relationship .address-values p,
.left-container-relationship .email-values p,
.left-container-relationship .phone-values p {
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;                         /* a 65-character email must not set the grid's width */
  min-width: 0;
}

.left-container-relationship :is(.address-tags, .email-tags, .phone-tags) p {
  color: var(--sm-ink-body);
}

.name-wrapper p:not(:last-child),
.address-fields p:not(:last-child),
.address-values p:not(:last-child),
.email-values p:not(:last-child),
.phone-values p:not(:last-child),
.address-tags p:not(:last-child) {
  border-bottom: 1px solid var(--sm-line);         /* was 1px dashed, currentColor */
}

/* Grid children default to min-width: auto, so an unbreakable email set the
   minimum width of the whole three-column grid. On Review at 900px that put
   .email-tags 147px past the viewport (DEFERRED-FIXES #26). */
.email-wrapper > *,
.phone-wrapper > *,
.address-wrapper > *,
.name-wrapper > * {
  min-width: 0;
}

/* Review below 1024px. review_styles.css has no media query at all: the
   record sits in a 60% column behind a 20% left margin, beside a 20% button
   column, inside a card with a further 4rem margin -- at 390px that left the
   three-column grid about 20px per column, so once long values were allowed
   to wrap (above) they stacked one letter per line. BRIEF 3.2 frees the
   layout below 1024px; the page becomes one column, record first, actions
   under it. Scoped by .overall-page, which only Review renders.
   Session 10 moved this from <= 768 to <= 1023: at 900 the same geometry
   left the record 430px wide beside 250px of empty margin, "Nacogdoches"
   broke mid-word, and Back / Done sat against the viewport edge. */
@media (max-width: 1023px) {
  .overall-page .wrapper {
    flex-direction: column;
  }

  .overall-page .left-part,
  .overall-page .right-part {
    width: 100%;
    margin-left: 0;                                /* was 20% */
  }

  .overall-page .left-container-relationship {
    box-sizing: border-box;
    width: 100%;                                   /* was 90% */
    margin-left: 0;                                /* was 4rem */
    margin-top: var(--sm-space-5);                 /* was 4rem */
  }

  .overall-page .right-part {
    align-items: stretch;
    padding-block: var(--sm-space-5);
  }
}

/* The alternates panel on Review. Its cards list alternates by family
   (#altName / #altAddress / #altEmail / #altPhone): a card in a family gets
   that family's mark, and its label/value pairs drop the grey boxes
   (#f5f7fa, #e8edf2, radius 10px -- none in the palette) so values sit on
   white like every other arbitration card. */
.alt-address-card {
  border: 1px solid var(--sm-line);                /* was 2px solid #018ac6 */
  border-left: 3px solid var(--arb-mark, var(--sm-line));
  border-radius: var(--sm-radius-card);
  padding: var(--sm-space-2) var(--sm-space-4);    /* was 24px */
  box-shadow: var(--sm-e1);
}

.alt-address-row {
  gap: var(--sm-space-3);
  margin: 0;                                       /* was margin-bottom: 12px */
  min-height: 38px;
  box-sizing: border-box;
  border-bottom: 1px solid var(--sm-line);
}

.alt-address-row:last-child {
  border-bottom-color: transparent;
}

.alt-addr-label,
.alt-addr-value {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}

.alt-addr-label {
  color: var(--sm-ink-muted);                      /* was #555 */
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-small-weight);        /* was 600 */
}

.alt-addr-value {
  color: var(--sm-ink);                            /* was #333 */
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  overflow-wrap: anywhere;
}

/* #00c896 is a retired success green (HANDOFF.md 1.1), and green means
   completion and nothing else (4.3). This is a panel title. */
.panel-title {
  color: var(--sm-ink);
  font-size: var(--sm-type-title-size);            /* was 1.8rem */
  line-height: var(--sm-type-title-leading);
  font-weight: var(--sm-type-title-weight);        /* was 700 */
}

/* .alt-empty (was #aaa, 2.3:1) is styled by 8.6 with the other empty
   states. Session 11 removed the rule that stood here (--sm-ink-quiet at
   13px): 8.6 restyles the same selector later as --sm-ink-muted at 14px,
   so the two disagreed and this one never applied. */

/* --------------------------------------------------------------------------
   7.8 The step header on these screens              HANDOFF.md 3.14, 2.9
   Two jobs, both CSS: the 13px floor, and "wraps rather than compressing".

   TYPE. STEP n was 10px #959595 (2.9:1), the step name 12px, the status
   pill 10px. The name and the pill go to 13px. The step number is the
   handoff's micro eyebrow and takes micro whole -- 11px, uppercase, tracked
   -- per DEFERRED-FIXES #36 as decided in session 11: 11px is acceptable
   for a number a steward glances at, and HANDOFF.md 2.9 names step numbers
   as micro's use. Session 6 had set it at 13px to honour the floor.

   STATUS. The pill carries the state the template already writes as a class
   (.completed / .in-progress / none): success tint, brand tint, pending
   neutrals -- HANDOFF.md 2.7's three hues, no new one.

   WIDTH. The header is seven steps and six connectors in one nowrap flex row
   beside the title, with no media query. That row is what overflowed every
   wizard screen from 900px down: the rightmost element in DEFERRED-FIXES #13
   and #25 ("div.right-container", "div.review") is THIS header -- step 7,
   Review -- not an arbitration card. The bar now wraps, so the steps drop
   under the title when the two do not fit side by side, and the steps
   themselves wrap when one row will not hold them. Below 1024px -- the band
   where either can happen -- the connectors are removed, so no line is left
   dangling at a row end.

   NOT DONE: the compact "Step 3 of 7 · Address" pill below 768px. It needs
   markup the template does not render, and the state that would drive it is
   set in Python (HANDOFF.md 3.14). docs/DEFERRED-FIXES.md.
   -------------------------------------------------------------------------- */

/* A wrapping flex row breaks a line when an item's BASIS will not fit, and
   an auto basis is the item's max-content -- for the title, the whole title
   on one line. So the title column takes a small basis and grows into what
   is left: the steps stay beside it wherever they fit beside the title's
   narrowest form, which is every width >= 1024px (BRIEF 3.2 -- the layout
   above 1024 is preserved), and drop beneath it below that. */
.top-bar {
  flex-wrap: wrap;
  column-gap: var(--sm-space-5);
  row-gap: var(--sm-space-4);
}

.top-bar .left-container {
  flex: 1 1 10rem;
  min-width: 0;
}

.top-bar .right-container {
  flex: 0 1 auto;
  flex-wrap: wrap;
  row-gap: var(--sm-space-3);
  min-width: 0;
  max-width: 100%;
  margin-right: var(--sm-space-4);                 /* was 2rem */
}

.top-bar .right-container div {
  font-size: var(--sm-type-small-size);            /* was 10px */
  line-height: var(--sm-type-small-leading);
}

.top-bar .right-container div p:first-of-type {
  color: var(--sm-ink-quiet);                      /* was #959595 */
  font-size: var(--sm-type-micro-size);            /* 13px until session 11: #36 */
  line-height: var(--sm-type-small-leading);       /* kept at 18px so the step's height does not move */
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: var(--sm-type-micro-transform);
}

.top-bar .right-container div p:nth-of-type(2) {
  color: var(--sm-ink);
  font-size: var(--sm-type-small-size);            /* was 12px */
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
}

.top-bar .right-container div p:nth-of-type(3) {
  margin-top: var(--sm-space-1);
  padding: 0 var(--sm-space-2);                    /* was 0 5px */
  border: 1px solid var(--sm-line);                /* was #959595: pending */
  border-radius: var(--sm-radius-pill);            /* was 8px */
  background-color: var(--sm-surface-sunk);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);            /* was 10px */
  line-height: var(--sm-type-small-leading);
  white-space: nowrap;
}

.top-bar .right-container div p.completed {
  border-color: var(--sm-success-border);
  background-color: var(--sm-success-tint);
  color: var(--sm-success-ink);
}

.top-bar .right-container div p.in-progress {
  border-color: var(--sm-info-border);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
}

/* #24c095 is a retired success green, and it ran between PENDING steps as
   well as completed ones. A connector is structure, not state.
   width: auto matters. base_styles.css gives the connector width: 40px, and
   a fixed width counts at full size in the header's max-content even though
   flex lets it shrink -- six of them made the header 877px wide on Review
   instead of 709, and pushed it under the title at 1100px. The basis is the
   minimum; the connectors still grow to fill the row. */
.top-bar .right-container .connector {
  flex: 1 1 var(--sm-space-3);
  width: auto;                                     /* was 40px */
  min-width: var(--sm-space-3);
  background-color: var(--sm-line-strong);
}

@media (max-width: 1023px) {
  .top-bar .right-container {
    column-gap: var(--sm-space-4);
  }

  .top-bar .right-container .connector {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   7.9 Deck pager chevrons                                   HANDOFF.md 2.12
   Every deck pager -- .scroll-btn / .scroll-btn1 on the four selection
   screens and both Relationship branches, .navigation-buttons on Matching --
   carries an inline SVG with fill="white". It was drawn on a solid
   --sm-brand circle; since 5.9 made the pager an outlined white button, the
   chevron has been white on white, and the pagers on seven screens rendered
   as empty circles. A presentation attribute loses to any CSS declaration,
   so the icon takes the button's colour, as every icon should. 5.9's
   disabled colour (--sm-ink-faint) then reaches the chevron too.

   Round 2 (item 5): the Bootstrap chevrons are gone. Every pager now holds
   the sprite's stroked chevron (section 15), which is currentColor by
   construction, so the `fill: currentColor` that lived here is deleted: a
   fill is exactly what turns a stroked Lucide icon into a blob.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   7.10 The reservation rule                                 HANDOFF.md 2.3, 2.6
   .matched-record select[value="alias"] (name_selection_styles.css) paints an
   amber #fff3cd / #ffc107 warning -- a second amber meaning, which 2.6 rules
   out because amber is Name's. It matches nothing in any of the 43 states
   (a <select> has no value attribute), so this is insurance, not a visible
   change: if the markup ever emits the attribute, the select stays neutral.
   -------------------------------------------------------------------------- */

.matched-record select[value="alias"] {
  background-color: var(--sm-surface);
  border-color: var(--sm-line-strong);
}

/* ==========================================================================
   8. FEEDBACK AND STATE            HANDOFF.md 3.13, 3.15-3.17 · BRIEF 8.8, 8.9
   --------------------------------------------------------------------------
   Everything the product says back to the steward: the reject exit, the
   success and failure popups, the loading overlay, errors, the mapping
   toast, the empty states, and the Add Field menu.

   One vocabulary, three shapes:
     NOTICE   8px radius, status tint, 1px status border, status icon,
              --sm-ink-body text                          (HANDOFF.md 3.15)
     TOAST    a notice that floats: same form plus --sm-e3
     OVERLAY  --sm-scrim, no blur, and a --sm-surface panel at 16px radius
              with --sm-e3                                (HANDOFF.md 3.17)
   plus the EMPTY STATE: 1px dashed --sm-line, 16px radius, 40px vertical
   padding (3.16).

   Icons: the markup has no icon for any of these, so they are drawn by a
   pseudo-element with a CSS mask and `background-color: currentColor`, which
   is the currentColor rule (HANDOFF.md 2.12) in the only form CSS can reach.
   The SVGs are stroke-only masks, so the stroke colour inside them is
   irrelevant; the rendered colour always comes from `color`. These four are
   NOT design-system tokens and do not belong in tokens.css.

   Round 2 (item 5): a pseudo-element cannot hold a <use>, so these are the
   only icon drawings outside the sprite (templates/partials/silk-icons.svg).
   They are TYPE-AND-ICONS-HANDOFF.md's drop-ins, generated from the same
   Lucide v0.469.0 paths at the sprite's stroke of 2. What changed:
     --sm-icon-x      was stroke 2.5, heavier than every other icon
     --sm-icon-empty  was stroke 1.75, lighter than every other icon
     --sm-icon-alert  the same drawing, re-generated. It is circle-alert (a
                      circle with "!"), NOT the sprite's i-info (a circle
                      with "i"). CSS-only: no sprite symbol, on purpose.
     --sm-icon-check  new -- 13.1's chosen-card badge, which was the only
                      FILLED icon in the product; now the outline
                      circle-check, the sprite's i-success.
   The stroke is `black` on purpose: a mask reads alpha only. Do not "fix" it
   to a token -- the colour comes from background-color.
   ========================================================================== */

/* Lucide v0.469.0 — keep in step with silk-icons.svg */
:root {
  --sm-icon-alert: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' x2='12' y1='8' y2='12'/%3E%3Cline x1='12' x2='12.01' y1='16' y2='16'/%3E%3C/svg%3E");
  --sm-icon-x: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18'/%3E%3Cpath d='m6 6 12 12'/%3E%3C/svg%3E");
  --sm-icon-empty: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='22 12 16 12 14 15 10 15 8 12 2 12'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
  --sm-icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m9 12 2 2 4-4'/%3E%3C/svg%3E");
}

/* --------------------------------------------------------------------------
   8.1 Reject -- the exit                 HANDOFF.md 3.13 · BRIEF 3.12, 8.8
   The handler in matching.html adds .removed under a "Visual Animation"
   comment, then calls card.remove() 300ms later. .removed had no rule in
   any stylesheet, so the card simply vanished. This is its rule, and it is
   built to finish inside the 300ms the script already waits:

     0-100ms    the card dims to 0.55, its border turns --sm-danger-solid,
                and its text is struck through in --sm-danger-solid. This is
                the "you rejected THIS one" moment.
     100-300ms  the card collapses: its neighbours slide left over it while
                it fades out. At 300ms it occupies no space, so
                card.remove() causes no jump.

   The collapse animates a NEGATIVE margin-right rather than shrinking the
   width. Shrinking would rewrap every value inside the card for 200ms and
   change its height mid-deck; a negative margin keeps the card's geometry
   whole and lets the later siblings (which paint on top, being later in the
   DOM) close over it. The shift is the card's full footprint: 340px
   flex-basis + 10px margin-left + 10px gap (matching_styles.css:182, :200),
   and 290px + 20px at <= 768px (:636). If either geometry changes, change
   --sm-reject-shift with it.

   pointer-events: none also closes a real double-submit: a second click on
   Reject during the 300ms re-runs the handler, and if it is the last card
   both timeouts see zero remaining cards and call sendMatchDecision twice.

   What this does NOT do is offer undo -- that needs the script to hold the
   node instead of removing it, and markup for the dark --sm-ink undo toast
   (3.13), which does not exist yet, so it has no rule here. .removed is
   the hook either way: if the script later holds the card rather than
   removing it, this rule still runs and leaves a zero-width, zero-opacity
   card for Undo to restore by removing the class. docs/DEFERRED-FIXES.md #38.
   -------------------------------------------------------------------------- */

.cards-container .potential-match-card {
  --sm-reject-shift: -360px;
}

@media (max-width: 768px) {
  .cards-container .potential-match-card {
    --sm-reject-shift: -310px;
  }
}

.cards-container .potential-match-card.removed {
  pointer-events: none;
  opacity: 0.55;
  border-color: var(--sm-danger-solid) !important; /* beats :not(.selected-card) at 7.2 */
  text-decoration: line-through;
  text-decoration-color: var(--sm-danger-solid);
  text-decoration-thickness: 2px;
  transition: opacity 100ms ease-out, border-color 100ms ease-out;
  animation: sm-reject-collapse 200ms cubic-bezier(0.4, 0, 0.2, 1) 100ms forwards;
}

/* opacity < 1 puts the rejected card in the positioned paint layer, above
   in-flow siblings, so without this it draws OVER the card sliding across
   it -- its struck text showed through the next card's bands. Positioning
   the later cards puts them in the same layer, where DOM order wins. No card
   has an absolutely positioned descendant for this to re-anchor (checked
   across the matching states). */
.cards-container .potential-match-card.removed ~ .potential-match-card {
  position: relative;
}

@keyframes sm-reject-collapse {
  from { opacity: 0.55; margin-right: 0; }
  to   { opacity: 0;    margin-right: var(--sm-reject-shift); }
}

/* Reduced motion keeps the state change (dim, danger border, strike) and
   drops the slide. The card still leaves at 300ms, because the script
   removes it. */
@media (prefers-reduced-motion: reduce) {
  .cards-container .potential-match-card.removed {
    transition: none;
    animation: none;
  }
}

/* --------------------------------------------------------------------------
   8.2 One scrim                                   HANDOFF.md 3.17, 6.10
   Same page, same moment, two different backdrops: success was a white 53%
   wash with blur(4px), failure a black 55% tint with no blur. The loading
   overlay was a white 70-75% wash with blur(4px), from four stylesheets.
   All three become the one scrim, with no blur.

   z-index. The sidebar carries an inline z-index: 99999 in left_bar.html.
   Success sat at 999 (under the sidebar, so the rail stayed live and bright
   beside a modal), failure at 99999 (a tie, won on DOM order), the loading
   overlay at 1000000 !important from base_styles.css:607. Now: both popups
   at 100000, over the sidebar and under the loading overlay, which is the
   only thing that may cover a popup (a popup's own button can start a load).
   Because every scrim now covers the rail, the panels centre on the viewport
   and need none of section 4's inset padding.
   -------------------------------------------------------------------------- */

.success-popup-overlay,
.failure-popup-overlay {
  background: var(--sm-scrim);
  background-color: var(--sm-scrim);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  z-index: 100000;
  padding: var(--sm-space-4);
  box-sizing: border-box;
}

/* Success fades (visibility + opacity, review_styles.css:668); failure
   switched display: none -> flex with no transition at all. Both now enter
   the same way over 200ms. Failure keeps its display toggle -- an animation
   restarts whenever display comes back, so no JS is involved. */
.success-popup-overlay {
  transition: opacity 200ms ease-out, visibility 200ms ease-out;
}

.failure-popup-overlay.active {
  animation: sm-scrim-in 200ms ease-out;
}

.failure-popup-overlay.active .failure-popup {
  animation: sm-panel-in 200ms ease-out;
}

@keyframes sm-scrim-in {
  from { opacity: 0; }
}

@keyframes sm-panel-in {
  from { transform: translateY(16px); }
}

/* body.no-scroll is toggled by the popup script on Review, but its only rule
   is in tagging_styles.css:484, which Review does not load -- so the page
   behind Review's popups still scrolled. It is on CLAUDE.md's toggle list;
   this gives it a rule on every page. */
body.no-scroll {
  overflow: hidden;
}

/* The last two of HANDOFF.md 3.17's five scrims, added in session 11
   (DEFERRED-FIXES #54). .export-modal-overlay (base_styles.css:665) was
   rgba(15,23,42,.25) with blur(5px), both !important, so both need it here;
   6.10 is explicit that the blur goes and must not come back. .modal-overlay
   (base_styles.css:550, the dashboard's file-preview dialog) was a plain
   rgba(0,0,0,.4). Only the backdrop changes: each keeps its own z-index,
   display toggle and centring, and .modal-overlay keeps section 4's inset. */
.export-modal-overlay {
  background-color: var(--sm-scrim) !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

.modal-overlay {
  background: var(--sm-scrim);
}

/* Two more backdrops the same sweep found, neither in HANDOFF.md 3.17's list
   of five and neither covered until session 11. The dashboard's file-preview
   dialog is .modal-overlay too, but home_styles.css:478 restyles it by id
   as rgba(15,23,42,.75) + blur(4px), all !important. Review's alternatives
   panel sits on .relationship-overlay, rgba(90,90,90,.4) + blur(4px)
   (review_styles.css:419). Same scrim, no blur. */
#filePreviewModal.modal-overlay {
  background-color: var(--sm-scrim) !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

.relationship-overlay {
  background-color: var(--sm-scrim);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* --------------------------------------------------------------------------
   8.3 The popup panel -- success and failure       HANDOFF.md 3.17, 4.3
   DEFERRED-FIXES #17. The surface was linear-gradient(135deg, #18c196,
   #07b2a6) with white text: two retired greens, and a saturated green
   carrying white text, which 4.3 rules out (#0caf9c on white is 2.8:1).
   Session 4 had already put canonical outlined buttons on it.

   .success-popup is declared twice, review_styles.css:686 and
   tagging_styles.css:417, and the two differ (the checkmark's 5%/10%
   padding nudge exists only in review, .process-btn is a 14px-radius
   rectangle in one and a 50px pill in the other). Every property that
   differs, or that either copy sets, is set here, so the two resolve to one
   panel whichever file loaded. Only Review renders the popup today;
   Relationships loads tagging's copy with no markup to use it.

   The failure panel was a different component: #ffdddd fill, a 6px #d50000
   left edge, 10px radius, fixed 380px. It becomes the same panel. The
   status is carried by the icon disc and nothing else -- success-tint with
   --sm-success-ink, danger-tint with --sm-danger-solid -- so success and
   failure are the same object reading two different outcomes.

   Modal geometry is HANDOFF.md 3.17: --sm-surface, 16px, --sm-e3. Padding is
   32px (--sm-space-6) rather than 3.17's 24px because the panel is centred
   text around an icon disc, not a form; 24px left the disc crowding the
   top edge. Say so if that is wrong: it is one token.
   -------------------------------------------------------------------------- */

.success-popup,
.failure-popup {
  position: relative;
  box-sizing: border-box;
  width: min(400px, 100%);
  max-width: 400px;
  padding: var(--sm-space-6);
  background: var(--sm-surface);
  background-image: none;
  border: 1px solid var(--sm-line);
  border-left: 1px solid var(--sm-line);           /* failure's 6px #d50000 edge */
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e3);
  color: var(--sm-ink-body);
  text-align: center;
}

.success-popup {
  transform: translateY(16px);
  transition: transform 200ms ease-out;
}

.success-popup-overlay.active .success-popup {
  transform: translateY(0);
}

/* The icon disc. Success has markup for it; failure does not, so failure's
   is drawn: ::before is the disc, ::after the mark, both at the top of the
   panel where success's .popup-checkmark sits. */
.popup-checkmark,
.failure-popup::before {
  box-sizing: border-box;
  width: 56px;
  height: 56px;
  margin: 0 auto var(--sm-space-4);
  border-radius: var(--sm-radius-pill);
}

.popup-checkmark {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--sm-success-tint);         /* was white on the gradient */
  color: var(--sm-success-ink);
}

/* The glyph is the sprite's check (i-step-done) at .sm-icon--24, the size
   failure's ::after mark already used, so the two popups now match. It was a
   Bootstrap check with fill="white" at 28px; the fill and the size went with
   it. The 5%/10% padding was a nudge for a 60px glyph in an 80px disc and is
   still cancelled here, since review_styles.css:721 sets it on the svg. */
.popup-checkmark svg {
  padding: 0;
}

.failure-popup::before {
  content: "";
  display: block;
  background-color: var(--sm-danger-tint);
  box-shadow: inset 0 0 0 1px var(--sm-danger-border);
}

.failure-popup::after {
  content: "";
  position: absolute;
  top: calc(var(--sm-space-6) + 16px);             /* padding + (56 - 24) / 2 */
  left: 50%;
  width: 24px;
  height: 24px;
  margin-left: -12px;
  color: var(--sm-danger-solid);
  background-color: currentColor;
  -webkit-mask: var(--sm-icon-x) center / contain no-repeat;
  mask: var(--sm-icon-x) center / contain no-repeat;
}

.success-popup h2,
.failure-popup h2 {
  margin: 0 0 var(--sm-space-2);
  color: var(--sm-ink);                             /* failure's was #d50000 */
  font-size: var(--sm-type-title-size);            /* was 1.5rem / UA 1.5em */
  line-height: var(--sm-type-title-leading);
  font-weight: var(--sm-type-title-weight);
}

.success-popup p,
.failure-popup p {
  display: block;
  margin: 0 0 var(--sm-space-5);
  color: var(--sm-ink-body);                        /* was #f7f7f7 on the gradient */
  font-size: var(--sm-type-body-size);             /* was 0.9rem */
  line-height: var(--sm-type-body-leading);
  font-weight: var(--sm-type-body-weight);
}

/* .popup-buttons had no rule anywhere: two inline anchors, the second
   pushed down by margin-top: 7% and widened by min-width: 77%
   (review_styles.css:756), both percentages of the panel. One column,
   content-width buttons, one gap. HANDOFF.md 2.11, 6.13. */
.popup-buttons {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sm-space-3);
}

.popup-buttons .netsuite-btn {
  margin-top: 0;
  min-width: 0;
}

@media (prefers-reduced-motion: reduce) {
  .success-popup-overlay,
  .success-popup {
    transition: none;
  }
  .success-popup {
    transform: none;
  }
  .failure-popup-overlay.active,
  .failure-popup-overlay.active .failure-popup {
    animation: none;
  }
}

/* --------------------------------------------------------------------------
   8.4 The loading overlay                 HANDOFF.md 3.17 · BRIEF 8.9
   .buffer-overlay is declared four times: base_styles.css:598,
   matching_styles.css:546, review_styles.css:806, tagging_styles.css:551.
   They disagree on the wash (white 75% in base, 70% in the other three) and
   the z-index (1000000 !important in base, 2000 in the others -- base wins
   because !important beats source order, which is the only reason the
   overlay still covered the sidebar on the three wizard pages). Every
   property any copy declares is declared here once, so all four resolve to
   this rule whichever page stylesheet loaded last.

   thinking-orb.js reads, and so this rule must preserve:
     .buffer-overlay, .buffer-spinner, .active, data-buffer-* -- names only;
     opacity / visibility as the hidden state -- isRendered() walks up from
     the spinner and treats opacity 0 or visibility hidden as "not showing",
     which is what stops the orb animating behind a closed overlay. Do not
     switch the hidden state to display: none or a transform.
     The first <p> in the overlay -- the script writes data-buffer-text into
     it. Its content is the script's; only its styling is ours.

   The design. The one scrim (8.2), and on it a --sm-surface panel holding
   the orb and the message. The panel has no markup of its own, so it is the
   <p>'s ::before, extended upward past the orb; it sits under the orb
   because a negative z-index paints below in-flow content in the overlay's
   stacking context. The orb stays dark-on-light, which is what it is drawn
   for (--orb-ink is read through getComputedStyle, so a var() resolves).

   Round 3, item 7 (SHELL-HANDOFF.md 2): shell/loader.css replaces the
   values below; the selectors and the technique are unchanged. The panel
   no longer shrink-wraps the message -- the <p> is a fixed 256px measure,
   so the panel is 320px on every page and a long message wraps (grows
   taller, never wider). Orb 64 -> 96px, padding 24 -> 32px (28px under the
   text, 20px between orb and text), message body 14/500 -> subtitle
   16/24/600. And the overlay now appears only after 150ms: an .active that
   is added and removed inside 150ms is never seen, so sidebar navigation
   and fast fetches no longer flash it. Hidden is still opacity 0 +
   visibility hidden -- only the transition timing changed.
   -------------------------------------------------------------------------- */

.buffer-overlay {
  position: fixed;
  inset: 0;
  width: auto;
  height: auto;
  background-color: var(--sm-scrim);               /* was white 70% / 75% */
  backdrop-filter: none;                           /* was blur(4px) x4 */
  -webkit-backdrop-filter: none;
  z-index: 1000000 !important;                     /* base_styles.css:607 */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  /* Hiding: fade out over 150ms, then hide. */
  transition: opacity 150ms ease, visibility 0s linear 150ms;
}

.buffer-overlay.active {
  opacity: 1;
  visibility: visible;
  /* Showing: nothing for 150ms, then a 200ms fade. The orb's 250ms
     visibility poll starts it once the opacity is above 0. */
  transition: opacity 200ms ease 150ms, visibility 0s linear 150ms;
}

.buffer-spinner {
  width: 96px;                                     /* was 64px */
  height: 96px;
  flex: 0 0 auto;
  --orb-ink: var(--sm-brand);                      /* was black */
}

.buffer-spinner:not([data-orb-ready]) {
  border-color: var(--sm-line);                    /* was #e0e0e0 */
  border-top-color: var(--sm-brand);               /* was #007CC7 */
}

/* A fixed 256px measure -- never a min-width -- so the panel is 320px
   whatever the page's message. max-width is only for a phone. */
.buffer-overlay p {
  position: relative;
  display: block;
  box-sizing: border-box;
  width: 256px;
  min-width: 0;                                    /* was 208px */
  max-width: calc(100vw - 112px);                  /* 320px panel, 24px from each edge */
  margin: 20px 0 0;                                /* was 16px */
  /* relationship.html puts its overlay INSIDE .relationship-record-container,
     so tagging_styles.css:294's `.relationship-record-container p` (and the
     global paragraph padding) reached this <p>: 8.4px / 8px of padding that
     made the panel 17px taller and moved the orb 8px on that page only. */
  padding: 0;
  color: var(--sm-ink);                            /* was #333 */
  font-size: var(--sm-type-subtitle-size);         /* was body 14 (and 1.2rem before that) */
  line-height: var(--sm-type-subtitle-leading);
  font-weight: var(--sm-type-subtitle-weight);     /* was 500 */
  text-align: center;
  text-wrap: pretty;
}

/* The panel: 32px each side of the text, 28px below it, and upward over the
   20px gap, the 96px orb and 32px above the orb. */
.buffer-overlay p::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: calc(-1 * (32px + 96px + 20px));
  right: calc(-1 * var(--sm-space-6));
  bottom: -28px;
  left: calc(-1 * var(--sm-space-6));
  background-color: var(--sm-surface);
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e3);
}

/* Round 7, item D: the journey top-up. base.html's <head> script sets
   html[data-sm-topup] when the last page's journey stage was cut short;
   the base overlay is then up from the first frame, with no delay and no
   fade, until thinking-orb.js opens it as .active in the same task and
   removes the attribute. Everything else keeps the 150ms delay. */
html[data-sm-topup] body > .buffer-overlay {
  opacity: 1;
  visibility: visible;
  transition: none;
}

/* Reduced motion: no fade, but the 150ms delay stays. */
@media (prefers-reduced-motion: reduce) {
  .buffer-overlay { transition: visibility 0s linear 0s; }
  .buffer-overlay.active { transition: visibility 0s linear 150ms, opacity 0s linear 150ms; }
}

/* --------------------------------------------------------------------------
   8.5 Notices and the toast -- errors                     HANDOFF.md 3.15
   Three error surfaces the override layer can reach:

   .upload-error -- the Django messages framework, used on exactly one page:
     home.html prints {% for message in messages %} into .messages inside
     the dropzone. It had no rule at all, so "Invalid file format" rendered
     as plain black body text inside the dashed box, indistinguishable from
     the dropzone's own copy.
   .error-message -- the login error. The right idea, off-palette: #e74c3c
     text on #fbeae5 (3.4:1), 5px radius, no icon.
   .error-toast -- mapping's #errorToast. Solid #e74c3c with white text
     (3.8:1, the 2.2 failure in red), and white-space: nowrap, so a long
     "Please map required fields: ..." ran off a 390px screen.

   All three become the danger notice; the toast is the same notice with
   --sm-e3, because it floats. The text is --sm-ink-body per 3.15; the
   danger is carried by the tint, the border and the icon.

   Everything else is not reachable: 21 raw alert() calls across 9 templates,
   and the toast's 6-second auto-dismiss, are script. docs/DEFERRED-FIXES.md.
   -------------------------------------------------------------------------- */

.messages .upload-error,
.login-container .error-message,
.error-message,
.error-toast {
  position: relative;
  box-sizing: border-box;
  padding: var(--sm-space-3) var(--sm-space-4) var(--sm-space-3) 44px;
  background-color: var(--sm-danger-tint);
  border: 1px solid var(--sm-danger-border);
  border-radius: var(--sm-radius-control);
  color: var(--sm-ink-body);
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  font-weight: var(--sm-type-body-weight);
  text-align: left;
}

.messages .upload-error::before,
.error-message::before,
.error-toast::before {
  content: "";
  position: absolute;
  top: var(--sm-space-3);
  left: var(--sm-space-4);
  width: 20px;
  height: 20px;
  color: var(--sm-danger-solid);
  background-color: currentColor;
  -webkit-mask: var(--sm-icon-alert) center / contain no-repeat;
  mask: var(--sm-icon-alert) center / contain no-repeat;
}

/* The message block sits in a centred flex column inside the dropzone; it
   takes the column's width so the notice reads as a notice, not a pill. */
.upload-box-error-message .messages {
  align-self: stretch;
}

.upload-box-error-message .messages:empty {
  display: none;
}

.messages .upload-error + .upload-error {
  margin-top: var(--sm-space-2);
}

.login-container .error-message {
  margin: 0 0 var(--sm-space-4);                   /* was 15px */
}

/* The toast. It hid at top: -80px, which is only off-screen while the toast
   is one line: once it wraps (it must, below ~600px) its lower edge showed
   under the top of the viewport while "hidden". It now hides by transform
   (its own height, plus 8px) and visibility, and shows 24px down. The
   .active class and the 6s timer are the script's and are unchanged. */
.error-toast {
  position: fixed;                                 /* the notice group above sets relative */
  top: 0;
  left: 50%;
  width: max-content;
  max-width: min(560px, calc(100vw - 2 * var(--sm-space-4)));
  white-space: normal;                             /* was nowrap */
  font-weight: var(--sm-weight-medium);
  box-shadow: var(--sm-e3);
  transform: translate(-50%, calc(-100% - var(--sm-space-2)));
  visibility: hidden;
  transition: transform 200ms ease-out, visibility 0s linear 200ms;
}

.error-toast.active {
  top: 0;
  transform: translate(-50%, var(--sm-space-5));
  visibility: visible;
  transition: transform 200ms ease-out, visibility 0s;
}

@media (prefers-reduced-motion: reduce) {
  .error-toast,
  .error-toast.active {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   8.6 Empty states                        HANDOFF.md 3.16 · BRIEF 8.9
   Four implementations that shared nothing:
     a table cell     .table-empty-notice-block    customer list; customer
                                                   detail's relationship tab
     a table row      .no-filter-records-row td    customer detail, 4 tables,
                                                   shown by the tab filter
     an <h1>          .no-data-message             dashboard, 20px/600 #9ca3af
     a plain <div>    .alt-empty                   Review's alternates panel
   plus two cells styled entirely by inline style attributes, on the
   relationship list and relationship detail, #9ca3af at 15-16px.

   Session 23: this is no longer the one treatment. .table-empty-notice-block
   and the two inline-styled cells no longer exist (sessions 21, 22), and the
   lists, the detail pages' empty tabs and Review's panel moved to the 48px
   sunk disc (20.8, 21.8, 19.2). What still uses this section is the
   dashboard's .line.no-data and customer detail's .no-filter-records-row td;
   see DEFERRED-FIXES #42.

   All become one treatment: the inbox icon in --sm-ink-quiet, the sentence
   in --sm-ink-muted (#9ca3af fails for a sentence, 2.8), body size, inside
   a 1px dashed --sm-line frame at 16px radius with 40px vertical padding.

   In a table the frame cannot be the cell's own border -- under
   border-collapse a cell's border merges into the grid and ignores
   border-radius -- so it is the cell's ::after, inset 16px, and the icon is
   its ::before. The cell's padding is 16px of inset plus the 40px.

   The two inline-styled cells are matched on the one thing both carry and
   nothing else in any template does: a colspan and an inline #9ca3af. That
   selector is a stopgap, logged with the template fix. Relationship
   detail's inline padding is `4rem !important`, and an inline !important
   beats any stylesheet, so that cell keeps 64px of padding and its frame
   sits 16px in from a larger box. Its icon, colour, size and frame match.

   The copy does not yet do 3.16's other half -- name the filter that
   emptied it and offer the way out. That is template copy. docs/DEFERRED-FIXES.md.
   -------------------------------------------------------------------------- */

.table-empty-notice-block,
.no-filter-records-row td,
td[colspan][style*="#9ca3af"] {
  position: relative;
  padding: calc(var(--sm-space-4) + 40px) var(--sm-space-7) !important;
  background-color: var(--sm-surface) !important;  /* no row-hover fill on an empty row */
  color: var(--sm-ink-muted) !important;
  font-size: var(--sm-type-body-size) !important;
  line-height: var(--sm-type-body-leading) !important;
  font-weight: var(--sm-weight-medium) !important;
  text-align: center !important;
}

.table-empty-notice-block::after,
.no-filter-records-row td::after,
td[colspan][style*="#9ca3af"]::after {
  content: "";
  position: absolute;
  inset: var(--sm-space-4);
  border: 1px dashed var(--sm-line);
  border-radius: var(--sm-radius-card);
  pointer-events: none;
}

.table-empty-notice-block::before,
.no-filter-records-row td::before,
td[colspan][style*="#9ca3af"]::before,
.line.no-data::before,
.alt-empty::before {
  content: "";
  display: block;
  width: 24px;
  height: 24px;
  margin: 0 auto var(--sm-space-2);
  color: var(--sm-ink-quiet);
  background-color: currentColor;
  -webkit-mask: var(--sm-icon-empty) center / contain no-repeat;
  mask: var(--sm-icon-empty) center / contain no-repeat;
}

/* The dashboard. .line.no-data inherits the list-item card (a 1.5px border,
   a hover lift) and home_styles.css:355 strips it with !important. The line
   becomes the frame instead, and loses the hover that made an empty state
   look clickable. */
.lines-wrapper .line.no-data,
.line.no-data {
  flex-direction: column;
  justify-content: center !important;
  border: 1px dashed var(--sm-line) !important;
  border-radius: var(--sm-radius-card);
  box-shadow: none !important;
  padding: 40px var(--sm-space-5) !important;      /* was 4rem block */
  background-color: var(--sm-surface);
  transform: none;
}

.lines-wrapper .line.no-data:hover {
  border-color: var(--sm-line) !important;
  transform: none;
}

.no-data-message {
  margin: 0;
  color: var(--sm-ink-muted);                      /* was #9ca3af */
  font-size: var(--sm-type-body-size);             /* was 20px, an <h1> */
  line-height: var(--sm-type-body-leading);
  font-weight: var(--sm-weight-medium);
  text-align: center;
}

/* Review's alternates: the frame, and the same ink and size as the others.
   (7.7 once set its own colour and size; removed in session 11, see 7.7.) */
.alt-empty {
  padding: 40px var(--sm-space-5);
  border: 1px dashed var(--sm-line);
  border-radius: var(--sm-radius-card);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  font-weight: var(--sm-weight-medium);
  text-align: center;
}

/* --------------------------------------------------------------------------
   8.7 The Add Field menu                                 mapping · TASK 7
   .add-field-menu opened upward (bottom: calc(100% + 6px),
   mapping_styles.css:318) and covered the Zip Code and Country rows -- the
   very rows a steward is reading while choosing which field to add. It now
   opens downward, over the dashed divider and the Back / Next footer, which
   are not being read at that moment. The card is not a scroll container and
   clips nothing (no overflow on .mapping-container), so the menu is free
   to extend past the footer; at the page bottom the document grows.

   Tokenised while here: the menu is a raised layer (--sm-e2), items are
   the control radius, hover is --sm-brand-wash, the disabled item is
   --sm-ink-faint, which 2.8 permits for disabled labels.
   -------------------------------------------------------------------------- */

.add-field-row {
  border-top-color: var(--sm-line);                /* was #d8e3ec */
}

.add-field-menu {
  top: calc(100% + var(--sm-space-1));
  bottom: auto;
  min-width: 180px;
  padding: var(--sm-space-1);
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);                /* was #e1e8ed */
  border-radius: var(--sm-radius-control);
  box-shadow: var(--sm-e2);                        /* was 0 6px 18px .12 */
  z-index: 30;
}

.add-field-item {
  border-radius: var(--sm-radius-control);
  padding: var(--sm-space-2) var(--sm-space-3);
  color: var(--sm-ink);                            /* was #223 */
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
}

.add-field-item:hover {
  background-color: var(--sm-brand-wash);          /* was #eaf6fc */
}

.add-field-item.disabled-item {
  color: var(--sm-ink-faint);                      /* was #b7c0c8 */
}


/* ==========================================================================
   9. BRAND MARKS AND THE LOGIN ECLIPSE     LOGO-HANDOFF.md 2 · ECLIPSE-HANDOFF.md 2
   --------------------------------------------------------------------------
   The sidebar mark is sized in 15.2a: 40px in both states, the lockup
   replacing it when the rail is open (SHELL-HANDOFF 1). The img carries
   width/height="40" so the SVG's intrinsic 512px never paints.

   The eclipse is now a <canvas>, and login_styles.css:11 sizes only
   `.logo-container img`. Without this rule the canvas mounts at its 576px
   attribute size at every viewport and overflows the 50% column below 1440.
   With it, silk-eclipse.js reads the 80% width at mount (clientWidth) and
   sizes the backing store to match -- 576px at 1440, as budgeted.

   The script then pins style.width/height inline in px. max-width lets it
   shrink if the window narrows after load; height needs !important to beat
   that inline px so the ring stays round. Downscaling the bitmap is fine;
   it is never upscaled (handoff 6.9).
   -------------------------------------------------------------------------- */

.logo-container canvas.logo-eclipse {
  display: block;
  width: 80%;
  max-width: 80%;
  height: auto !important;   /* beats the inline px silk-eclipse.js sets */
  aspect-ratio: 1 / 1;
}

/* Round 3, item 1 / 78 (SHELL-HANDOFF.md 4). The login header is the
   lockup -- the A1 mark plus the client's Cinzel Bold wordmark, outlined to
   paths, so no font loads -- at 64px, replacing SM Logo Header.png. Sized
   by height only (4.5). That PNG carried ~39px of transparent margin on its
   left, so 40px of padding puts the new mark where the old one appeared.
   .header is used by login.html alone. App round item 1: the lockup is now
   7c's sm-lockup-login.svg, 316 x 64 (partials/logo.html); still 64px tall. */
.header {
  padding: var(--sm-space-6) 0 0 40px;             /* was 2em top */
}

.header img {
  width: auto;                                     /* was 24rem */
  height: 64px;
}

/* ==========================================================================
   10. THE SIX BROWSE PAGES AT PHONE WIDTH    BRIEF 3.2, 3.3, 8.4 · HANDOFF 3.10
   --------------------------------------------------------------------------
   Login, dashboard, customer list, customer detail, relationship list and
   relationship detail must work down to 390px (BRIEF 3.3).

   EVERY RULE IN THIS SECTION SITS INSIDE A max-width QUERY OF 768px.
   BRIEF 3.2: at 1024px and up the layout is preserved and every control stays
   where it is; below 1024 the layout is free to change. Nothing here may be
   moved out of its media query without re-running the >= 1024 computed-style
   comparison against HEAD.

   MEASURED AT HEAD BEFORE THIS SECTION (390px, all states of the six pages):
   zero horizontal overflow and nothing at negative x. DEFERRED-FIXES #14 was
   already closed on these pages by 6.1 and 6.9 (see #26). What was left was
   READABILITY, not reachability:

     the four tables scroll: the customer list is 914px wide in a 324px box,
       relationship detail 898px, customer detail's email tab 757px
     the tab strip wraps to three rows and its underline (bottom: -1.35rem,
       aimed at a one-row header) lands on top of the next row's labels
     the expanded identifier on both detail headers is position: absolute and
       sits on top of the record's own name
     the dashboard's source names break mid-word ("Servi / ceTit / an") beside
       two fixed buttons, and the page title sits under the sidebar toggle
     a 40px left / 24px right gutter from .main-content-inner's padding on top
       of the 16px inset, which spends 64px of a 390px screen on margins
   ========================================================================== */

/* --------------------------------------------------------------------------
   10.1 One 16px gutter each side                              HANDOFF.md 1.3
   The inset is 16px at <= 768 and already on .main-content's left. The four
   browse templates wrap their content in .main-content-inner, whose own
   1.5rem inline padding (customer_styles.css:709) stacks on top of it. Its
   left padding goes; its right padding becomes the inset, because
   .main-content carries no right padding of its own on these pages.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .main-content-inner {
    padding-left: 0;
    padding-right: var(--sm-inset);
  }

  /* The white panels inside it: 24px and 20px/32px paddings become one 16px
     step, so the table gets the width back. */
  .relationship-content-panel {
    padding: var(--sm-space-4);
  }

  .tabs-header-action-row {
    padding: var(--sm-space-3) var(--sm-space-4);
  }
}

/* --------------------------------------------------------------------------
   10.2 The tables become one block per row                 HANDOFF.md 3.10
   BRIEF 8.4: "at phone width a six-column table has no choice but to become
   something else." Each <td> now carries data-label="<its column header>"
   (the four table templates, this commit), and below 768 each row becomes a
   stacked block: the lead cell is the block's title, the actions cell sits
   beside it, and every other cell is a label / value line.

   WHAT DOES NOT CHANGE: the table box. The customer list keeps its card, the
   detail tables keep their panel, each relationship group keeps its
   tbody.relationship-group-card ring. Rows are divided by the one 1px line,
   as they are at desktop (HANDOFF.md 2.10, 4.9).

   WHY THIS IS SAFE
   - The markup is untouched apart from the attribute. Every <tbody> is still
     a <tbody>, so tbody.relationship-group-card (sorted by the list's engine,
     toggled by .active-group-tab) still exists and still groups its rows.
   - Cells keep their DOM order, so data-column-index still points at the
     same cell. The sort engine reads cells by index; display: block does not
     renumber them.
   - Row filtering on customer detail writes style.display = "" / "none", so
     "" hands the row back to this stylesheet and "none" still hides it.
   - The empty-state rows carry colspan and no data-label; they stay one
     full-width sentence.

   Selectors match 6.3's specificity or repeat 6.11's verbatim so that source
   order decides, rather than stacking !important.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  /* The containers stop scrolling: nothing is wider than they are now. The
     identifier popover and the date menu overhang the box, so it stops
     clipping too. */
  .customers-table-container,
  .details-table-panel,
  .master-relationship-table-container {
    overflow: visible;
  }

  .customers-table-container table,
  .customers-table-container tbody,
  .details-table-panel table,
  .details-table-panel tbody,
  .master-relationship-table-container table,
  .master-relationship-table-container tbody {
    display: block;
    width: 100%;
    min-width: 0;
  }

  /* Column headers are replaced by the per-cell labels. The one header that
     is also a control -- the Name sort on both list pages -- stays, as a
     full-width sort bar above the rows. */
  .customers-table-container thead,
  .details-table-panel table thead,
  .master-relationship-table-container table thead {
    display: block;
  }

  .customers-table-container thead tr,
  .details-table-panel table thead tr,
  .master-relationship-table-container table thead tr {
    display: flex;
  }

  .customers-table-container thead th,
  .details-table-panel table thead th,
  .master-relationship-table-container table thead th {
    display: none;
  }

  .customers-table-container thead th.sortable-column-header,
  .master-relationship-table-container table thead th.sortable-column-header {
    display: block;
    flex: 1 1 auto;
    /* !important: relationship_list.html sets style="width: 40%" on this th. */
    width: auto !important;
  }

  .master-relationship-table-container table thead th.sortable-column-header {
    border-radius: var(--sm-radius-card);
  }

  /* The row. Two columns: the title and the actions share the first line;
     every other cell spans both. */
  .customers-table-container tbody tr,
  .details-table-panel table tbody tr,
  .master-relationship-table-container .relationship-group-card tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: var(--sm-space-3);
    row-gap: var(--sm-space-1);
    padding: var(--sm-space-3) var(--sm-space-4);
    border-bottom: 1px solid var(--sm-line);
  }

  .customers-table-container tbody tr:last-child,
  .details-table-panel table tbody tr:last-child,
  .master-relationship-table-container .relationship-group-card tr:last-child {
    border-bottom: 0;
  }

  /* Every cell: a label / value line. The label column is fixed so values
     align down the block. Values wrap rather than truncate, because no cell
     carries the title attribute that would recover a truncated value
     (DEFERRED-FIXES #28). */
  .customers-table-container tbody td,
  .details-table-panel table tbody td,
  .master-relationship-table-container .relationship-group-card td {
    display: grid;
    grid-template-columns: 7.5rem minmax(0, 1fr);
    column-gap: var(--sm-space-3);
    align-items: baseline;
    grid-column: 1 / -1;
    padding: var(--sm-space-1) 0;
    border: 0;
    border-radius: 0;
    text-align: left;
    background-color: transparent;
    overflow-wrap: anywhere;
  }

  /* The label is the column header it replaces, so it takes the header's
     role: micro, a column eyebrow (HANDOFF.md 1.2). */
  .customers-table-container tbody td::before,
  .details-table-panel table tbody td::before,
  .master-relationship-table-container .relationship-group-card td::before {
    content: attr(data-label);
    color: var(--sm-ink-muted);
    font-size: var(--sm-type-micro-size);
    line-height: var(--sm-type-micro-leading);
    font-weight: var(--sm-type-micro-weight);
    letter-spacing: var(--sm-type-micro-tracking);
    text-transform: var(--sm-type-micro-transform);
    overflow-wrap: normal;
  }

  /* The lead cell is the block's title: no label, body-strong, first line. */
  .customers-table-container tbody td:first-child,
  .details-table-panel table tbody td:first-child,
  .master-relationship-table-container .relationship-group-card td:first-child {
    display: block;
    grid-column: 1;
    grid-row: 1;
    padding-bottom: var(--sm-space-1);
    color: var(--sm-ink);
    font-weight: var(--sm-type-body-strong-weight);
  }

  /* The actions cell (edit link + ID chip) sits beside the title. :has() is
     the only way to find it without a class; a browser without :has() gives
     it an ordinary labelled line, which still works. */
  .customers-table-container tbody td:has(> .action-external-trigger),
  .details-table-panel table tbody td:has(> .action-external-trigger),
  .master-relationship-table-container .relationship-group-card td:has(> .action-external-trigger) {
    display: flex;
    align-items: center;
    gap: var(--sm-space-2);
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
    padding: 0;
  }

  .customers-table-container tbody td:first-child::before,
  .details-table-panel table tbody td:first-child::before,
  .master-relationship-table-container .relationship-group-card td:first-child::before,
  .customers-table-container tbody td:has(> .action-external-trigger)::before,
  .details-table-panel table tbody td:has(> .action-external-trigger)::before,
  .master-relationship-table-container .relationship-group-card td:has(> .action-external-trigger)::before,
  .customers-table-container tbody td[colspan]::before,
  .details-table-panel table tbody td[colspan]::before,
  .master-relationship-table-container td[colspan]::before {
    content: none;
  }

  /* ...except the empty state's own ::before, which is 8.6's inbox icon, not
     a label. The td[colspan] lines above are (0,2,3) and outranked 8.6, so
     from session 9 until session 11 every table empty state lost its icon at
     <= 768 and kept only the frame. Restored with one more class or
     attribute than the rule that removed it. */
  .customers-table-container tbody td[colspan].table-empty-notice-block::before,
  .details-table-panel table tbody td[colspan].table-empty-notice-block::before,
  .details-table-panel table tbody .no-filter-records-row td[colspan]::before,
  .details-table-panel table tbody td[colspan][style*="#9ca3af"]::before,
  .master-relationship-table-container tbody td[colspan][style*="#9ca3af"]::before {
    content: "";
  }

  /* Empty-state rows: one full-width sentence, as at desktop. */
  .customers-table-container tbody tr:has(> td[colspan]),
  .details-table-panel table tbody tr:has(> td[colspan]),
  .master-relationship-table-container tbody:not(.relationship-group-card) tr {
    display: block;
    padding: 0;
  }

  .customers-table-container tbody td[colspan],
  .details-table-panel table tbody td[colspan],
  .master-relationship-table-container td[colspan] {
    display: block;
    padding: var(--sm-space-6) var(--sm-space-4);
    text-align: center;
  }

  /* 6.11 put nowrap and line-height: 0 on every last cell for the desktop
     row height. Here the last cell is sometimes text (Count, Email Address),
     and a zero line box would stack the label on top of the value. Same
     selectors as 6.11, later in the file, so they win on source order. */
  .customers-table-container tbody td:last-child:not([colspan]),
  .details-table-panel table tbody td:last-child:not([colspan]),
  .relationship-group-card td:last-child:not([colspan]) {
    white-space: normal;
    line-height: var(--sm-type-body-leading);
  }

  /* Things that are not text keep their own width in the value column: a
     grid item stretches by default, which drew every badge as a full-width
     bar and every date button as a full-width control. */
  .customers-table-container tbody td > .badge-pill,
  .details-table-panel table tbody td > .badge-pill,
  .master-relationship-table-container .relationship-group-card td > .badge-pill,
  .details-table-panel table tbody td > .date-dropdown-container {
    justify-self: start;
  }

  /* ---- the relationship list's groups ---------------------------------
     Each tbody.relationship-group-card is already a card (6.4). The table's
     separated borders (border-spacing) do not apply to block boxes, so the
     gap between groups is a margin. 6.4 rounds the four corner cells and 6.13
     paints every cell white with the hover on the cell; as blocks, the card
     clips and the row carries the fill and the hover instead. */
  /* A group is shown by .active-group-tab, which is display: table-row-group
     !important (relationship_list_styles.css:88); every other group is
     display: none. Only the SHOWN state becomes a block, so the show/hide
     mechanism is untouched. !important matches the rule it replaces. */
  .master-relationship-table-container .relationship-group-card.active-group-tab {
    display: block !important;
  }

  .master-relationship-table-container .relationship-group-card {
    margin-top: var(--sm-space-3);
    overflow: hidden;
    background-color: var(--sm-surface);
  }

  .master-relationship-table-container .relationship-group-card tr:hover {
    background-color: var(--sm-surface-sunk);
  }

  .master-relationship-table-container .relationship-group-card tr td,
  .master-relationship-table-container .relationship-group-card tr:hover td,
  .master-relationship-table-container .relationship-group-card tr td.center-column-items {
    background-color: transparent;
    padding-left: 0;
    text-align: left;
  }

  /* Members nest under the group's own row: 6.4's 44px cell indent becomes
     a row indent, so a member's labels still align with each other. */
  .master-relationship-table-container .relationship-group-card tr.child-row {
    padding-left: var(--sm-space-6);
  }

  .master-relationship-table-container .relationship-group-card td.child-name-muted {
    /* !important: 6.4 sets this padding-left with !important. */
    padding-left: 0 !important;
  }

  /* The customer list's card clips its last row's hover to the radius. */
  .customers-table-container tbody tr:last-child {
    border-radius: 0 0 var(--sm-radius-card) var(--sm-radius-card);
  }
}

/* --------------------------------------------------------------------------
   10.3 The customer-detail tab strip below 768
   Five tabs need ~500px and wrap. customer_styles.css:587 puts the underline
   at bottom: -1.35rem, which reaches the header row's border only when the
   strip is one line; on a wrapped strip it draws through the next row's
   labels. Each tab carries its underline on its own bottom edge instead, and
   the gaps drop from 32 / 36px to one 24px step so fewer rows are needed.
   The relationship list's three tabs get the same gap.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .tabs-nav-list-wrapper {
    column-gap: var(--sm-space-5);
  }

  .tab-nav-link.active-tab::after {
    bottom: 0;
  }

  .relationship-tabs-bar {
    column-gap: var(--sm-space-5);
  }
}

/* --------------------------------------------------------------------------
   10.4 The detail headers below 768                             extends 6.10
   The expanded identifier (.card-id-tag) is position: absolute at the card's
   top right (customer_styles.css:467). At 390 the card is 326px wide and the
   36-character UUID wraps to two lines across the record's own name. Here it
   takes part in the layout, above the name. The avatar stacks above the
   fields and drops from 110px to 64px, giving the four fields the card's
   whole width instead of ~170px of it.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .card-id-tag {
    position: static;
    display: block;
    width: fit-content;
    max-width: 100%;
    margin-bottom: var(--sm-space-4);
    overflow-wrap: anywhere;
  }

  .card-profile-split-wrapper {
    flex-direction: column;
    align-items: flex-start;
  }

  .avatar-circle-badge {
    width: 64px;
    height: 64px;
  }

  .avatar-circle-badge span {
    font-size: var(--sm-type-title-size);
    line-height: var(--sm-type-title-leading);
  }

  .profile-info-grid-block {
    width: 100%;
  }
}

/* --------------------------------------------------------------------------
   10.5 The dashboard below 768                            home.html · BRIEF 5.2
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  /* The sidebar toggle is fixed at top: 20px, left: 72px below 768
     (base_styles.css:328) and is 32px tall. The dashboard's title started at
     36px and sat under it; 64px clears it by 12. The right padding was
     home_styles.css's 2rem at <= 1023; the inset makes the gutters equal.
     :has() scopes this to home.html's .main-content without a template
     class. The other five pages clear the toggle already (56px). */
  .main-content:has(> .dashboard-metrics-grid) {
    /* !important: home_styles.css:6 sets padding-top with !important. */
    padding-top: var(--sm-space-8) !important;
    padding-right: var(--sm-inset);
  }

  /* The review panel: the heading takes its own line and the MATCH /
     RELATED labels sit under it, right-aligned over the buttons they label. */
  .review-records-header {
    flex-wrap: wrap;
    row-gap: var(--sm-space-2);
    padding: var(--sm-space-4) var(--sm-space-4) var(--sm-space-3);
  }

  .review-records-header .top-level-heading {
    flex: 1 1 100%;
  }

  .match-related-header {
    margin-left: auto;
  }

  .lines-wrapper {
    padding-inline: var(--sm-space-4);
  }

  /* Each source: the name on its own line, then the two count buttons. The
     name broke mid-word because it was squeezed to ~50px beside two buttons
     that cannot shrink (flex-shrink: 0), and word-break: break-word let it. */
  .lines-wrapper .line {
    flex-wrap: wrap;
    row-gap: var(--sm-space-2);
    padding: var(--sm-space-3) var(--sm-space-4);
  }

  .lines-wrapper .line .record-name {
    flex: 1 1 100%;
    word-break: normal;
    overflow-wrap: anywhere;
  }

  .lines-wrapper .line .match-related {
    margin-left: auto;
  }

  .upload-records {
    padding: var(--sm-space-5) var(--sm-space-4);
  }
}

/* --------------------------------------------------------------------------
   10.6 Two boxes wider than a phone                        DEFERRED-FIXES #14
   The alternatives panel on Review is min-width: 450px (review_styles.css
   :442) and slides in from the right edge, so at 390 its left 60px sat off
   the screen -- the last of #14's two cases, x = -60. It takes the
   viewport's width when the viewport is narrower than 450px; from 451px up
   it is exactly what it was.

   (The export calendar frame's max-width moved into section 17: the
   date-range panel carries its own max-width: calc(100vw - 32px).)
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .relationship-panel {
    min-width: 0;
    width: 100%;
    max-width: 450px;
  }
}

/* ==========================================================================
   11. THE NINE TABLET-FLOOR PAGES              BRIEF 3.3, 8.3, 8.5 · HANDOFF 3.14
   --------------------------------------------------------------------------
   The seven wizard screens, Mapping and Upload Status get a 768px floor, not
   phone support (BRIEF 3.3). Below 768 they must degrade without breaking;
   the three-way comparison is not required to work at phone widths.

   EVERY RULE HERE SITS INSIDE A max-width QUERY OF 1023px OR LESS, EXCEPT
   11.1. BRIEF 3.2: at 1024 and up the layout is preserved. 11.1 is the one
   deliberate change at desktop widths: it closes DEFERRED-FIXES #4.

   MEASURED AT HEAD BEFORE THIS SECTION (18 states x 1440 / 1100 / 1024 /
   900 / 800 / 768 / 390, documentElement.scrollWidth - clientWidth): 14 of
   126 combinations overflowed, every one of them Upload Status. Nothing at
   negative x, nothing under the rail. The wizard's overflow was closed by
   7.8 (DEFERRED-FIXES #32). What was left in 769-1023 was USABILITY:

     Upload Status  the 28% margin, at every width
     Relationships  record, deck and footer in three columns: "Nacogdoches"
                    broke one letter per line and deck values were clipped
     Review         the record in a 430px column beside 250px of margin
     step header    at 1023 the title's 10rem basis let "Relationships" run
                    into step 1
     at 768         the sidebar toggle sat on every wizard title and on
                    Upload Status's; Address's four selects collapsed to
                    four value-less chevrons (601-768 only; 7.3.1 covers <=600)

   Mapping needed nothing: at 768 each side of its grid is ~320px, not the
   ~150px once reported, and nothing truncates.
   ========================================================================== */

/* --------------------------------------------------------------------------
   11.1 Upload Status -- the 28% margin           DEFERRED-FIXES #4 · HANDOFF 6.13
   .three-column-status-row (upload_styles.css:9) is width: 100% AND
   margin-left: 28%, so it always ended 28% past its parent: 318px at 1440.
   The parent, .header-content, is already at the inset, so the margin is the
   whole defect and var(--sm-inset) would double-inset it (#4's verdict).

   Centring replaces what the margin was doing. The first tile's left edge,
   HEAD -> now: 469 -> 432 at 1440, 377 -> 268 at 1100, 356 -> 231 at 1024.
   With margin-left: 0 alone they would jump to x=108, the page edge, 240px
   left of the four report rows under them. Centred, at 1440 the tile group's
   centre is within 2px of the rows' centre. At 1024-1100 it centres on the
   column, not on the rows, because the rows carry their own fixed 220px +
   20px offset (docs/DEFERRED-FIXES.md). It fits at every width >= 1024 with the
   sidebar pushed, which aligning to the rows' 65% box would not.
   Plain declarations: the original carries no !important and loads earlier.
   -------------------------------------------------------------------------- */

.three-column-status-row {
  margin-left: 0;                                  /* was 28% */
  justify-content: center;                         /* was flex-start */
}

/* --------------------------------------------------------------------------
   11.2 Upload Status below 1024
   The four report rows sit behind a fixed 220px + 20px left margin in a 65%
   column, in boxes with a fixed 65px height; at 768 their sentences wrapped
   and spilled out of the bottom of the box. Below 1024 the rows take the
   column's full width and grow with their text. The tiles wrap, with room
   for the white face that hangs below each tile.
   -------------------------------------------------------------------------- */

@media (max-width: 1023px) {
  .four-rectangular-row {
    width: auto;                                   /* was 65% */
    margin-left: 0;                                /* was 220px */
  }

  .rect-box {
    box-sizing: border-box;
    height: auto;                                  /* was 65px */
    min-height: 65px;
    margin-left: 0;                                /* was 20px, past a 100% width */
    padding-block: var(--sm-space-3);              /* was 15px top only */
    align-items: center;
  }

  .rect-box > p,
  .rect-content-wrapper {
    margin-block: 0;                               /* the wrapper's 20px bottom margin set the box's height */
  }

  .three-column-status-row {
    flex-wrap: wrap;
    column-gap: var(--sm-space-7);                 /* was 80px */
    row-gap: var(--sm-space-8);                    /* the face hangs ~43px below its tile */
  }
}

/* --------------------------------------------------------------------------
   11.3 Clear the sidebar toggle at <= 768
   Below 769 the toggle is position: fixed at top 20px / left 72px, 32px tall
   (base_styles.css:328). With the 16px inset, every wizard title (in
   .top-bar, 30px top padding) and Upload Status's (36px) started under it.
   10.5 fixed the same collision on the dashboard with 64px. .top-bar renders
   only on the wizard screens. .header-content is shared with the dashboard,
   which 10.5 already clears, so Upload Status is found by its tile row.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .top-bar {
    padding-top: var(--sm-space-8);                /* was 30px */
  }

  .header-content:has(> .three-column-status-row) {
    padding-top: var(--sm-space-6);                /* + .main-content's 36px = 68px */
  }
}

/* --------------------------------------------------------------------------
   11.4 The step header below 1024                  HANDOFF 3.14 · extends 7.8
   7.8 lets the steps sit beside the title wherever they fit beside the
   title's 10rem basis. Below 1024 the inset drops to 96px and they still
   fit -- but "Relationships" is wider than 10rem, so the word ran into step
   1. Below 1024 the title takes its own row and the seven steps take the
   next; at 768 all seven still fit on that row.
   -------------------------------------------------------------------------- */

@media (max-width: 1023px) {
  .top-bar .left-container {
    flex-basis: 100%;
  }
}

/* --------------------------------------------------------------------------
   11.5 Address's selects from 601 to 768                   extends 7.3, 7.3.1
   name_selection_styles.css:597 turns .options-wrapper into a ROW at <= 768,
   but 7.3 gives the address card's wrapper a 9rem column: four selects in
   144px, about 30px each, showing only their chevrons. 7.3.1 re-lays it at
   <= 600. Between the two, the column is restored -- the same shape as at
   769 and up.
   -------------------------------------------------------------------------- */

@media (min-width: 601px) and (max-width: 768px) {
  .top-cards .record .address-attributes .options-wrapper {
    flex-direction: column;
  }
}

/* --------------------------------------------------------------------------
   11.6 Review's gutters below 1024                                extends 7.7
   7.7 makes Review one column below 1024. Review's .main-content has no
   right padding of its own, so the record card and Back / Done ran to the
   viewport's right edge. They get a right gutter: one step at 769-1023,
   where the left inset is the rail's 96px, and the inset at <= 768.
   -------------------------------------------------------------------------- */

@media (max-width: 1023px) {
  .overall-page {
    padding-right: var(--sm-space-6);
  }
}

@media (max-width: 768px) {
  .overall-page {
    padding-right: var(--sm-inset);
  }
}

/* --------------------------------------------------------------------------
   11.7 Relationships below 1024              BRIEF 5.3 · HANDOFF 3.12, 6.1
   Both branches set the compiled record (60%), the suggestion deck (40%)
   and the Back / Next column side by side (tagging_styles.css:255, 265,
   273, 194). At 900 that left the record's middle column ~95px and the
   deck's value pills clipped. Below 1024 they stack: record, deck, footer.

   THE DECK'S HEIGHT-SYNC IS NOT AFFECTED. Unlike the four selection
   screens, relationship.html sizes .cards-viewport from its own TALLEST CARD
   (+100px) and pages by summing each card's real height; it never reads the
   left column. A wider deck makes each card shorter, and the script measures
   that on load. DEFERRED-FIXES #31's unequal cards are unchanged in kind.

   The viewport: tagging_styles.css:504 caps it at max-height: 455px, which
   beats the script's inline tallest + 100px. The cap is lifted AT EVERY
   WIDTH, not just here -- see the rule after this media query
   (DEFERRED-FIXES #62, session 12).

   The footer: tagging_styles.css:194 is a right-aligned column pushed down
   by margin-top: 38% -- the defect 5.12 could not isolate. It CAN be
   isolated as the record container's direct child; below 1024 it becomes a
   right-aligned row. Above 1024 it is untouched.
   -------------------------------------------------------------------------- */

@media (max-width: 1023px) {
  .main-content div.relationship-record-container {
    flex-direction: column;
  }

  .relationship-record-container .left-container-relationship,
  .relationship-record-container .right-container-relationship {
    box-sizing: border-box;
    width: auto;                                   /* was 60% / 40% */
  }

  .relationship-record-container > .bottom-buttons {
    flex-direction: row;
    justify-content: flex-end;
    margin-top: 0;                                 /* was 38% */
    padding-block: 0 var(--sm-space-6);
  }
}

/* The deck cap, at every width (DEFERRED-FIXES #62, session 12). At >= 1024
   the 455px cap was shorter than the cards themselves -- 509px for the first
   at 1440, and at 1100 three of the four (563 / 496 / 496px) -- so the deck
   never showed a whole card at desktop widths. With the cap gone the script's
   own inline height governs: tallest card + 100px, 573px at 1440 and 663px at
   1100. Every card is whole at its pager stop, with the top of the next one
   showing beneath it as the cue that there is more. The cost is height: at
   1440 the deck column now outgrows the compiled record and the page is
   81px taller (900 -> 981); at 1100 and 1024 the record is still the taller
   column and nothing moves. The pager sums each card's real height and
   never reads this value, so the unequal cards of #31 page exactly as
   before -- same heights, same 9px landing offset at every stop. */
.right-container-relationship .cards-viewport {
  max-height: none;                                /* was 455px, tagging_styles.css:504 */
}

/* ==========================================================================
   12. CLOSEOUT -- WHAT THE WHOLE-BRANCH SWEEP FOUND        HANDOFF 2.8, 2.9, 2.14
   --------------------------------------------------------------------------
   Session 11 measured all 43 states at 1440 / 1100 / 1024 / 1023 / 900 /
   800 / 769 / 768 / 390 for the first time since section 1. Layout held
   (no overflow, nothing at negative x, nothing under the rail). What did
   not hold is below. Fixes made elsewhere in the file are marked "Session
   11" where they sit: the scrims (8.2), the focus ring (5.7), the empty-state
   icon at <= 768 (10.2), the step number (7.8), the calendar pills (5.11),
   and two dead rules removed where a later section already owned the
   selector (6.3 and 7.7, both superseded by 8.6).
   ========================================================================== */

/* --------------------------------------------------------------------------
   12.1 One page background                              HANDOFF.md 2.14, 4.7
   "The single page background -- every page, no exceptions." Seven
   originals still painted their own: #fafafa (base_styles .top-bar, and
   .main-content in home, matching, name_selection, tagging), #fafbfc
   (customer_styles .page-center-boundary), #eaf4fb (mapping's .main-content,
   a blue page) and review's .wrapper. None is !important; same selectors,
   later file. #fafafa -> #f9fafb is invisible; Mapping's blue page is the
   one visible change.
   -------------------------------------------------------------------------- */

.main-content,
.top-bar,
.page-center-boundary,
.overall-page .wrapper,
.main-content div.relationship-record-container {
  background-color: var(--sm-surface-page);
}

/* --------------------------------------------------------------------------
   12.2 The last text under the 13px floor                      HANDOFF.md 2.9
   The sweep lists every text node under 13px. What stays under it, on
   purpose: micro (11px) eyebrows -- column headers, the ID chip, the step
   number (#36) -- and the 10px sort and dropdown glyphs, which are marks,
   not text. What is lifted here is everything a steward reads:
     calendar day numbers and weekday letters   12px, both calendars (now
                                                section 17's date-range panel)
     Mapping's field names at <= 768            12px (mapping_styles.css:172)
     the dropzone copy on the dashboard         12px, #9ca3af, and a green
                                                "Drag & Drop" (green is
                                                completion only, HANDOFF 4.3)
     the login disclaimer                       12px, #999 (2.8:1)
     the sidebar's account email                11px
     the Relationship suggestion cards          12px (DEFERRED-FIXES #35)
   The export calendar's "New customers only" label, once an inline 12.5px
   in left_bar.html, is body size in section 17 since item 22.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .mapping-field {
    font-size: var(--sm-type-small-size);          /* was 12px */
  }
}

.mini-upload-content {
  font-size: var(--sm-type-small-size);            /* was 12px */
  color: var(--sm-ink-quiet);                      /* was #9ca3af: a sentence */
}

.mini-upload-content span {
  color: var(--sm-brand-strong);                   /* was #10ba90 */
}

.disclaimer {
  font-size: var(--sm-type-small-size);            /* was 12px */
  line-height: var(--sm-type-small-leading);
  color: var(--sm-ink-quiet);                      /* was #999 */
}

.section-2 .initials-email {
  font-size: var(--sm-type-small-size);            /* was 11px; still ellipsised */
}

/* #35 was held back because the deck's pager was thought to assume equal
   cards (#31). It does not on this page: relationship.html sizes its
   viewport from the tallest card and pages by summing each card's real
   height (see 11.7), so a taller card costs nothing. Lifted to the small
   role. The value pills' retired green gradient stays -- that is the
   component pass (#35). */
.right-container-relationship .relationship p {
  font-size: var(--sm-type-small-size);            /* was 12px, tagging_styles.css:294 */
  line-height: var(--sm-type-small-leading);
}

/* --------------------------------------------------------------------------
   12.3 The date calendar, on every page                        HANDOFF.md 2.2
   Superseded by section 17 (item 22). Session 11 gave .calendar-days-grid,
   .day-dot, .active-day and .in-range a rule on every page, because the
   export calendar opens everywhere while customer_styles.css (:217-287)
   loads on three pages only. Those four names now occur only inside the
   date-range panel, and section 17 styles them there, scoped to
   .sm-daterange, including the !important neutralisers the customer_styles
   originals need. The header, month-arrow and pill-row rules went with
   their markup.
   -------------------------------------------------------------------------- */

/* ==========================================================================
   13. THE UNREACHED COMPONENTS         HANDOFF 3.4, 3.12, 3.13, 3.17, 3.19, 2.3
   --------------------------------------------------------------------------
   Session 12. The components no earlier session took on, closing
   DEFERRED-FIXES #18 (the chosen card), #34 (field hues outside their
   field), #35 (the Relationship suggestion cards) and most of #66 (the 61
   off-token colours). Colour and elevation only, except where a HANDOFF
   component names its type role outright (the metric tile). Upload Status
   is deliberately NOT here: its tiles and bars are a redesign with open
   design questions, not a colour swap, and half-converting it would be
   worse than leaving it whole (DEFERRED-FIXES #66). The same goes for the
   export calendar's frame, whose shadow is inline (#67).
   ========================================================================== */

/* --------------------------------------------------------------------------
   13.1 Matching's chosen card                  HANDOFF.md 3.13 · FIXES #18
   "Chosen: 2px --sm-brand border + rgba(1,138,198,0.18) ring + a check
   badge. No transform, no scale." The five-layer offset shadow had in fact
   been dead since session 7 -- 7.2's .cards-container .potential-match-card
   (0,2,0) gives every card --sm-e1 and outranks .selected-card (0,1,0) --
   so what was left was Bootstrap #007bff on the border, and no ring or check.

   The ring is 3px outside the card, and the deck scrolls with
   overflow-y: hidden and zero top padding (matching_styles.css:181), so the
   card's 1px top margin would clip it. Every card -- chosen or not, so
   nothing moves on click -- takes 4px on top and 3px less underneath: the
   deck's height is unchanged, and at 1440 the cards now sit level with the
   New Event Record (232px, both). Card heights are fixed by the deck (615px
   at 1440 with or without the 2px border), so the pager's pitch does not
   change.

   The badge is static, so it is --sm-brand-deep (HANDOFF.md 2.5). It is a
   mask, not an image, so every colour here is a token. Round 2: it was a
   solid disc with the check cut out -- the only filled icon in the product.
   It is now the outline circle-check (--sm-icon-check, section 8), the same
   drawing as the sprite's i-success, at the same 18px.

   .selected-card is JS-toggled (matching.html:253-255, 387-390): restyled,
   not renamed. A card that is chosen and then rejected shows only the
   rejected state (8.1).
   -------------------------------------------------------------------------- */

.cards-container .potential-match-card {
  margin-top: var(--sm-space-1);                   /* was 1px: room for the ring */
  margin-bottom: 27px;                             /* was 30px: deck height unchanged */
}

.cards-container .potential-match-card.selected-card:not(.removed) {
  border: 2px solid var(--sm-brand);               /* was #007bff */
  box-shadow: 0 0 0 3px rgba(1, 138, 198, 0.18),   /* HANDOFF.md 3.13's ring, verbatim */
              var(--sm-e1);
}

/* The badge sits in the card's top-right corner, 8px in, out of the flow.
   Inline beside the heading it pushed "94% NetSuite Potential Match" onto
   two lines at the 290px card (<= 768), and the deck stretches every card
   to the tallest, so the whole deck grew. The heading text starts 23px
   down and ends 26px from the right edge at the narrowest card, which
   clears an 18px disc centred 17px from the corner. No card positions a
   descendant absolutely, so relative re-anchors nothing (8.1 checked). */
.cards-container .potential-match-card.selected-card:not(.removed) {
  position: relative;
}

.cards-container .potential-match-card.selected-card:not(.removed)::after {
  content: "";
  position: absolute;
  top: var(--sm-space-2);
  right: var(--sm-space-2);
  width: 18px;
  height: 18px;
  background-color: var(--sm-brand-deep);
  -webkit-mask: var(--sm-icon-check) center / contain no-repeat;
          mask: var(--sm-icon-check) center / contain no-repeat;
}

/* --------------------------------------------------------------------------
   13.2 The dashboard's metric tiles             HANDOFF.md 3.19, 2.3 · #34
   "--sm-surface on 1px --sm-line, 16px radius, --sm-e1. micro label,
   display number." The four colourways (.card-blue / -purple / -cyan /
   -yellow) were two field hues -- Email's indigo and Name's amber, which
   2.3 forbids "not a metric tile" by name -- plus one blue used twice.
   HANDOFF.md 3.19 retires all four, not just the two that break 2.3: the
   tint carried no meaning, and the number is what the tile is for. So the
   tiles become one tile, four times. The colour classes stay on the markup
   and now style nothing.

   What the approved tile does not have is an icon; the template does, and
   removing it is a template edit. It stays, in --sm-ink-quiet, so it reads
   as a glyph beside the label rather than competing with the number. The
   hover lift (translateY(-2px) and a deeper shadow) goes: 2.10 says depth is
   carried by the border, so hover is a brand border instead.

   Thousands separators ("48,217") need |intcomma, which needs
   django.contrib.humanize in INSTALLED_APPS: settings, not reachable here
   (DEFERRED-FIXES #69).
   -------------------------------------------------------------------------- */

.dashboard-metrics-grid .metric-card {
  background: var(--sm-surface);                   /* was four tints at 30% */
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e1);                        /* was rgba(149,157,165,.05) 0 8px 24px */
}

.dashboard-metrics-grid .metric-card:hover {
  transform: none;                                 /* was translateY(-2px) */
  border-color: var(--sm-brand);
  box-shadow: var(--sm-e1);
}

/* The rule above would otherwise take the ring off (5.7's .metric-card
   :focus-visible is (0,2,0) and earlier). */
.dashboard-metrics-grid .metric-card:focus-visible {
  box-shadow: var(--sm-focus);
  border-color: var(--sm-brand);
}

.metric-icon {
  color: var(--sm-ink-quiet);                      /* was #334155, slate */
}

.metric-title {
  font-size: var(--sm-type-micro-size);            /* was 13px/500 */
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: var(--sm-type-micro-transform);
  color: var(--sm-ink-quiet);
}

.metric-value {
  font-size: var(--sm-type-display-size);          /* was clamp(26px, 2.2vw, 32px) */
  line-height: var(--sm-type-display-leading);     /* was 1.1 */
  font-weight: var(--sm-type-display-weight);      /* was 700 */
  letter-spacing: var(--sm-type-display-tracking);
  color: var(--sm-ink);
}

/* --------------------------------------------------------------------------
   13.3 The rest of the dashboard                         HANDOFF.md 2.1, 2.2
   The Match / Related count buttons were #018fc8 -- a retired blue --
   carrying white 15px text (the 2.2 failure). They are links, so
   --sm-brand-strong, with --sm-brand-deep on hover like every primary. A
   count of zero was the same blue at opacity .4, which 5.6 retires ("not
   opacity on a saturated fill"); it takes 5.6's disabled look, drawn as an
   inset line so the button does not change size. Scoped to .match-related:
   Upload Status uses .gradient-colored for something else entirely.
   -------------------------------------------------------------------------- */

.top-level-heading {
  color: var(--sm-ink);                            /* was #1f2937 */
}

.home-content-wrapper {
  border: 1px solid var(--sm-line);
  box-shadow: var(--sm-e1);                        /* was rgba(0,0,0,.08) 0 4px 20px */
}

.match-related .gradient-colored {
  background-color: var(--sm-brand-strong);        /* was #018fc8 */
  color: var(--sm-surface);
}

.match-related .gradient-colored:hover {
  background-color: var(--sm-brand-deep);
}

.match-related .gradient-colored.disabled-link {
  opacity: 1;                                      /* was 0.4 */
  background-color: var(--sm-surface);
  color: var(--sm-ink-faint);
  box-shadow: inset 0 0 0 1px var(--sm-line);
}

/* A zero count links nowhere, so it has no link glyph. (Until round 2 the
   glyph was a white PNG that was also invisible on the neutral surface and
   pushed the "0" off-centre; it is now the sprite's i-external-link, which
   would render -- in --sm-ink-faint -- so this rule is now the decision, not
   a workaround.) */
.match-related .gradient-colored.disabled-link .external-link-icon {
  display: none;
}

/* --------------------------------------------------------------------------
   13.4 The profile card on both detail pages         HANDOFF.md 2.3 · #34
   #fff9eb on #fceecb is Name's amber on a card that is not a name field.
   #34 said: --sm-surface with --sm-line, and if it needs an accent,
   --sm-brand-tint. It does not need one -- the avatar and the 26px name
   already make it the head of the page -- so it is the plain card. The
   avatar was #4e8098, the retired logo teal-grey; the token table gives
   avatars --sm-surface-sunk, and the sidebar's own avatar
   (base_styles.css:242) is already sunk, 1px line, --sm-ink-body initials.
   This is the same thing larger. The line is inset so the 110px circle
   stays 110px.
   -------------------------------------------------------------------------- */

.profile-summary-card {
  background-color: var(--sm-surface);             /* was #fff9eb */
  border-color: var(--sm-line);                    /* was #fceecb */
  box-shadow: var(--sm-e1);                        /* was rgba(0,0,0,.01) 0 4px 12px */
}

.avatar-circle-badge {
  background-color: var(--sm-surface-sunk);        /* was #4e8098 */
  box-shadow: inset 0 0 0 1px var(--sm-line);
}

.avatar-circle-badge span {
  color: var(--sm-ink-body);                       /* was #ffffff */
  font-weight: var(--sm-weight-semibold);          /* was 700 */
}

/* --------------------------------------------------------------------------
   13.5 Mapping                                   HANDOFF.md 1.1, 2.10, 4.3
   Mapping sits in the upload flow and was painted in a third grey system:
   #1a1a1a / #333 / #555 / #888 / #999 text, #f5f7fa and #e8edf2 boxes,
   #e0e0e0 and #f0f0f0 lines. Each maps to its role in the one scale. The
   mapped state stays green -- a mapped column is done, which is the one
   thing green means (4.3) -- but its text is --sm-success-ink, not the
   off-token #0a7a6e. The error state's text moves from the danger MARK to
   the danger INK, which is the one meant to be read.

   The dropdown classes are generic (.custom-dropdown-selected is also the
   customer list's filter), so every dropdown rule is scoped to .mapping-rows.
   -------------------------------------------------------------------------- */

.mapping-header h2 {
  color: var(--sm-ink);                            /* was #1a1a1a */
}

.mapping-filename {
  color: var(--sm-ink-quiet);                      /* was #888 */
}

.mapping-container {
  border: 1px solid var(--sm-line);
  box-shadow: var(--sm-e1);                        /* was rgba(14,30,37,.08) 0 2px 12px */
}

.mapping-col-header {
  color: var(--sm-ink-muted);                      /* was #555 */
}

.mapping-field {
  background-color: var(--sm-surface-sunk);        /* was #f5f7fa */
  border-color: var(--sm-line);                    /* was #e8edf2 */
  color: var(--sm-ink);                            /* was #333 */
}

.mapping-select {
  border-color: var(--sm-line-strong);             /* was #e0e0e0 */
  color: var(--sm-ink);                            /* was #333 */
}

.mapping-footer {
  border-top-color: var(--sm-line);                /* was #f0f0f0 */
}

.mapping-rows .custom-dropdown-selected {
  border-color: var(--sm-line-strong);             /* was #e0e0e0 */
  color: var(--sm-ink-faint);                      /* was #999: the placeholder */
}

.mapping-rows .custom-dropdown-selected.has-value {
  background-color: var(--sm-success-tint);
  border-color: var(--sm-success-mark);
  color: var(--sm-success-ink);                    /* was #0a7a6e */
}

.mapping-rows .custom-dropdown.open .custom-dropdown-selected {
  border-color: var(--sm-brand);
}

.mapping-rows .custom-dropdown.select-required .custom-dropdown-selected {
  color: var(--sm-danger-ink) !important;          /* was #e74c3c !important */
}

.mapping-rows .custom-dropdown-options {
  box-shadow: var(--sm-e2);                        /* was rgba(0,0,0,.1) 0 6px 16px */
}

.mapping-rows .custom-dropdown-item {
  color: var(--sm-ink-body);                       /* was #333 */
  border-bottom-color: var(--sm-line);             /* was #f0f0f0 */
}

.mapping-rows .custom-dropdown-item:hover {
  background-color: var(--sm-brand-wash);          /* was #f0f8ff */
}

.mapping-rows .custom-dropdown-item.selected {
  background-color: var(--sm-success-tint);        /* was #eaf8f6 */
  color: var(--sm-success-ink);                    /* was #0caf9c: the mark, as text */
}

.mapping-rows .custom-dropdown-item.map-yourself-item {
  color: var(--sm-success-ink);                    /* was #0a7a6e */
}

.mapping-rows .custom-dropdown-item.map-yourself-item:hover {
  background-color: var(--sm-success-border);      /* was #c0e8e4 */
}

/* --------------------------------------------------------------------------
   13.6 Badges                                  HANDOFF.md 2.4, 3.4 · 6.12
   6.12 set the badge's geometry and left its tones to "the badge task",
   which never ran. The three tones were three saturated blues with white
   text: #018ac6 (the 2.2 failure), #00a3c4 (a retired cyan) and #015f88.
   Every one of them is a classification -- Family, Cohab, Company -- and
   2.4 is explicit: classifications are all --sm-brand-tint with
   --sm-brand-deep text, and the label carries the meaning.
   -------------------------------------------------------------------------- */

.badge-pill.pill-blue,
.badge-pill.pill-cyan,
.badge-pill.pill-dark-blue {
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
}

/* --------------------------------------------------------------------------
   13.7 The Relationship suggestion cards        HANDOFF.md 3.12, 2.4, 4.3 · #35
   The deck column still wore the .32 16px shadow that 7.6 took off the
   compiled record beside it; it gets the same card. Inside it:
   - .relationship-1 (Company) glowed teal and .relationship-2 (Family) grey:
     a hue per classification, which 2.4 rules out. Both are one resting
     card. The hairline is drawn as a 1px shadow, not a border, so no card
     changes height and the pager (11.7, #62) lands exactly where it did.
   - p.matching-attribute -- a value that agrees with the compiled record --
     was the retired #60c5c3 -> #14c584 gradient with white text. Agreement
     is information, not completion (4.3), so it takes the info pair:
     --sm-brand-tint with --sm-brand-deep text.
   - every other label and value chip was #f9f9f9: an inert chip, so
     --sm-surface-sunk.
   relationship-1 / relationship-2 are JS-read (relationship.html:292-305):
   restyled, not renamed.
   -------------------------------------------------------------------------- */

.relationship-record-container .right-container-relationship {
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);            /* was 25px */
  box-shadow: var(--sm-e1);                        /* was the .32 16px shadow */
}

.right-container-relationship .relationship.relationship-1,
.right-container-relationship .relationship.relationship-2 {
  border-radius: var(--sm-radius-card);            /* was 20px */
  box-shadow: 0 0 0 1px var(--sm-line), var(--sm-e1);  /* was teal / .32 glows */
}

.relationship-record-container .attributes p {
  background-color: var(--sm-surface-sunk);        /* was #f9f9f9 */
}

.relationship-record-container .attributes p.matching-attribute {
  background-image: none;                          /* was #60c5c3 -> #54ccaf -> #14c584 */
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);                     /* was #fff */
}

/* --------------------------------------------------------------------------
   13.8 The chrome on every page                   HANDOFF.md 1.1, 1.5, 3.18
   Small, but on 10 to 41 of the 43 states each:
   - Text colour. No stylesheet sets one on the wizard, list and detail
     pages, so their titles and unstyled text were the UA's #000; login's
     was #333. Both become --sm-ink (declared on body, so it reaches exactly
     what inherited the default and nothing that sets its own colour).
   - The rail's rgba(0,0,0,.03) shadow is a 1px --sm-line edge (2.10: a flat
     surface gets a line, not a shadow). Expanded over the page at <= 768 it
     floats, so --sm-e3. The toggle already has a 1px line; it gets --sm-e1.
   - The active nav item (.special) was #f0f7ff; 3.18 gives it
     --sm-brand-tint, with --sm-brand-deep for its label when expanded. The
     brand-strong icon 3.18 asks for arrived in round 2 with the sprite:
     section 15.2 (DEFERRED-FIXES #9).
   - The wizard progress track, #e0e6ed, is --sm-line.
   - The list search icon turned #007cc7, a retired blue, on focus.
   - Login's inputs rested on #ccc; inputs at rest are --sm-line-strong.
   - The customer-list dropdown and date menus had two ad-hoc black shadows
     each; menus are --sm-e2.
   - Review's Alternatives slide-over was #f0f4f8, a blue-grey off the
     scale, with a black .15 shadow: it is the page ground inside, and
     --sm-e3, the slide-over elevation.
   -------------------------------------------------------------------------- */

body {
  color: var(--sm-ink);                            /* was the UA's #000; login's #333 */
}

.left-bar {
  box-shadow: 1px 0 0 var(--sm-line);              /* was rgba(0,0,0,.03) 1px 0 10px */
}

@media (max-width: 768px) {
  .left-bar.open {
    box-shadow: var(--sm-e3);                      /* was rgba(0,0,0,.15) 10px 0 30px */
  }
}

.open-left-bar {
  box-shadow: var(--sm-e1);                        /* was rgba(0,0,0,.08) 0 2px 8px */
}

.icon-link-wrapper.special {
  background-color: var(--sm-brand-tint);          /* was #f0f7ff */
}

.icon-link-wrapper.special .container-text {
  color: var(--sm-brand-deep);
}

.bottom-progress-container {
  background-color: var(--sm-line);                /* was #e0e6ed */
}

.search-wrapper input:focus ~ .search-icon,
.search-wrapper:focus-within .search-icon {
  color: var(--sm-brand);                          /* was #007cc7 */
}

/* Inputs do not inherit colour, so body's --sm-ink never reached the typed
   text in the list search, the login fields or the export calendar: it
   stayed the UA's #000. */
.search-wrapper input,
.login-container input,
.export-modal-overlay input {
  color: var(--sm-ink);
}

/* The detail pages' tab panel: rgba(0,0,0,.01) 0 4px 12px, a shadow too faint
   to see, on a card that already has its 1px line. Relationship detail
   repeats the panel with an inline style (.03), which this cannot reach
   without !important (DEFERRED-FIXES #22). */
.tabs-navigation-panel-container {
  box-shadow: var(--sm-e1);
}

.login-container input[type="email"],
.login-container input[type="password"],
.login-container input[type="text"] {
  border-color: var(--sm-line-strong);             /* was #ccc */
}

.dropdown-menu-content,
.date-dropdown-menu {
  box-shadow: var(--sm-e2);                        /* was two rgba(0,0,0,.04-.1) layers */
}

.relationship-panel {
  background-color: var(--sm-surface-page);        /* was #f0f4f8 */
  box-shadow: var(--sm-e3);                        /* was rgba(0,0,0,.15) -5px 0 25px */
}

.panel-header,
.alt-type-btns {
  background-color: var(--sm-surface-page);        /* was #f0f4f8 */
}

.alt-type-btns {
  border-top-color: var(--sm-line);                /* was #e0e0e0 */
}

/* --------------------------------------------------------------------------
   13.9 The file-preview modal and the error page         HANDOFF.md 3.17
   The preview modal's table was painted in the slate scale HANDOFF.md 1.1
   retires by name (#0f172a, #cbd5e1, #e2e8f0, #f1f5f9, #f8fafc), every
   declaration !important on an id selector -- so these are too. The
   header row takes the table-header treatment 6.3 gives every other table.
   The error page's card was the same slate plus two ad-hoc shadows; it sits
   flat on the page, so it is the resting card.
   -------------------------------------------------------------------------- */

#filePreviewModal .modal-container {
  box-shadow: var(--sm-e3) !important;             /* was rgba(0,0,0,.3) 0 20px 30px */
}

#filePreviewModal .modal-title {
  color: var(--sm-ink) !important;                 /* was #0f172a */
}

#filePreviewModal .table-wrapper {
  border-color: var(--sm-line) !important;         /* was #cbd5e1 */
}

#filePreviewModal #previewTable th,
#filePreviewModal #previewTable td {
  border-bottom-color: var(--sm-line) !important;  /* was #e2e8f0; th's 2px was #cbd5e1 */
  border-right-color: var(--sm-line) !important;   /* was #f1f5f9 */
}

#filePreviewModal #previewTable td {
  color: var(--sm-ink-body) !important;            /* was #0f172a */
}

#filePreviewModal #previewTable th {
  background-color: var(--sm-brand-tint) !important;   /* was #f8fafc */
  color: var(--sm-ink-muted) !important;               /* was #0f172a */
}

#filePreviewModal .modal-footer {
  border-top-color: var(--sm-line) !important;     /* was #e2e8f0 */
}

.error-card {
  border-color: var(--sm-line);                    /* was #f1f5f9 */
  border-radius: var(--sm-radius-card);            /* was 24px */
  box-shadow: var(--sm-e1);                        /* was two rgba(0,0,0,.02-.05) layers */
}

.error-card h2 {
  color: var(--sm-ink);                            /* was #1f2937 */
}

.error-card p {
  color: var(--sm-ink-muted);                      /* was #64748b */
}

/* ==========================================================================
   14. THE TYPEFACE -- IBM PLEX SANS AND IBM PLEX MONO       Round 2 · item 2
   --------------------------------------------------------------------------
   Poppins -> IBM Plex Sans for the UI, IBM Plex Mono for UUIDs. Loaded by a
   Google Fonts <link> in the five full-document templates; the Poppins +
   Tektur @import at line 1 of base_styles.css and login_styles.css is
   deleted (the one authorised edit to an original this round). Source:
   ../silk-matrix-design/type/silk-type.css. Every layout below this line
   was verified against Poppins metrics for twelve sessions, so the numbers
   in the comments are the Poppins -> Plex deltas the round-2 sweep measured.
   ========================================================================== */

/* --------------------------------------------------------------------------
   14.1 The family, and the line-height nobody declared
   base_styles.css:9 and login_styles.css:33 both set `body` to Poppins;
   same selector, later file. Neither declares a line-height, so every line
   not styled by a token used the font's `normal`: 1.40 for Poppins, 1.30
   for Plex. Pinning 1.4 keeps each of those line boxes the height it was
   verified at. Token-driven leading is in px and is unaffected.
   -------------------------------------------------------------------------- */

body {
  font-family: var(--sm-type-family);              /* was "Poppins", sans-serif */
  line-height: 1.4;                                /* was the font's normal: 1.40 Poppins, 1.30 Plex */
}

/* The two originals that name Poppins directly instead of inheriting it. */
select {
  font-family: var(--sm-type-family);              /* name_selection_styles.css:389 */
}

/* Form controls do not inherit font-family, and only three rules ever
   opted them in (5.x .options-wrapper button, #filePreviewModal
   .btn-secondary, .jump-page-btn). So under Poppins every other button,
   input and textarea rendered in the browser's default -- Arial -- which
   includes the page-number buttons and the Go-to field: the pager's digits
   were Arial and its labels Poppins. One family for the UI means the
   controls too. Element selectors (0,0,1), so any class rule that sets a
   family or a leading still wins.

   The line-height is the same trap as body's, one level down. The UA's
   `font` shorthand on controls resets line-height to normal, so every
   control no rule gave a leading (the tab buttons, the date dropdown, both
   searches, the Go-to field, the login inputs, the relation tabs) was
   sized by Arial's 1.149 and grew 2px under Plex's 1.30 -- which put 2px
   on every customer-detail row (50 -> 52) and 4px on the wrapped ones.
   1.15 puts each of them back at the height it was verified at. The .btn
   system (section 5) sets its own 20px and was never affected. Selects
   are left alone: they were already Poppins, and their height is fixed. */
button,
input,
textarea {
  font-family: inherit;
  line-height: 1.15;                               /* Arial's normal (1.149); Plex's is 1.30 */
}

/* --------------------------------------------------------------------------
   14.2 Fixed-width digits
   Poppins' digits are proportional and nothing enabled tnum, so no numeric
   column in the product lined up digit for digit. Plex's default figures
   are already tabular; this is declared anyway so it survives the next
   font change. Scoped to what is compared down a column or read as a
   count, not prose. ([class*="count"] also matches .account-*; tnum only
   touches digits, so that is harmless.)
   -------------------------------------------------------------------------- */

table,
.metric-value,
.pagination-controls-wrapper-list,
.customer-list-footer-pagination-bar,
.jump-page-input,
.record-heading,
.badge-pill,
[class*="count"],
[class*="score"] {
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   14.3 Two widths sized to Poppins' glyphs
   Plex is narrower than Poppins in mixed-case body text, and the sweep found
   412 lines that now wrap less. It is WIDER in two places:

   - Small semibold tracked caps. The stacked-row label column (10.2) is
     7.5rem because "RELATIONSHIP TYPE" was 116px in Poppins micro; in Plex
     it is 126px, so on the relationship list at <= 768 every row's label
     wrapped to two lines and every row grew 11px (91 -> 102). 8rem is the
     smallest width that holds it. Same selectors as 10.2, later in the file.
   - Mono. Plex Mono's advance is 0.6em against Consolas' ~0.55em, so the
     36-character UUID is 281px, not 257px. On the detail headers at 390 the
     tag ("ID No: " + UUID) no longer fits on one line and the UUID broke
     mid-string. As a wrapping flex row the UUID moves whole onto its own
     line, and stays inline wherever it fits (it does at 768).
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
  .customers-table-container tbody td,
  .details-table-panel table tbody td,
  .master-relationship-table-container .relationship-group-card td {
    grid-template-columns: 8rem minmax(0, 1fr);    /* was 7.5rem (10.2) */
  }

  .card-id-tag {
    display: flex;                                 /* was block (10.4) */
    flex-wrap: wrap;
    column-gap: 0.3em;                             /* the collapsed space after "ID No:" */
  }
}

/* ==========================================================================
   15. ICONS -- ONE LUCIDE SPRITE                           Round 2 · item 5
   TYPE-AND-ICONS-HANDOFF.md Part 2 · HANDOFF.md 2.12
   --------------------------------------------------------------------------
   Every icon is now one <svg><use href="#i-NAME"></svg> reference into
   templates/partials/silk-icons.svg (Lucide v0.469.0, 49 symbols since the
   app round added the toggle's two, DEFERRED-FIXES #110), which the
   five full-document templates include first thing in <body>. JavaScript
   emits icons only through smIcon() (static/icons/silk-icons.js), which
   writes the same markup. What went, counted in the template source (106):
   25 inline Bootstrap and 11 inline Lucide SVGs, 20 <img> icons drawing on
   17 PNG/SVG files, 34 HTML glyphs (entities and ".."), 11 emoji, and 5
   icon drawings pasted into JS strings.

   Colour: the symbols are stroked in currentColor with fill="none" set
   INSIDE the symbol, so an icon is always its parent's `color` -- the rules
   below only ever set `color` on a parent. A CSS `fill` or `stroke` on the
   outer <svg> cannot reach the shadow tree's shapes, which is why the stray
   `fill: #25c196` at review_styles.css:721 is inert and why 7.9's
   `fill: currentColor` could be deleted.

   Not here: the four CSS mask icons (section 8's :root), which draw on
   pseudo-elements and cannot use a sprite.
   ========================================================================== */

/* --------------------------------------------------------------------------
   15.1 Sizes and strokes -- icons/silk-icons.css, verbatim
   16px inline with 13-14px text (buttons, triggers, table actions,
   chevrons); 20px sidebar, step markers, icon-only buttons; 24px metric
   tiles, empty states, popup heads. The stroke is declared here and
   inherited into the symbol, so these three rules are the icon weight for
   the whole product. No stylesheet sets stroke-width anywhere else.
   -------------------------------------------------------------------------- */

.sm-icon {
  display: inline-block;
  flex: none;
  width: 20px;
  height: 20px;
  vertical-align: middle;
  stroke-width: 2;            /* 2 in a 24 viewBox = 1.67px at 20px */
  pointer-events: none;       /* the button/link owns the click, never the svg */
}

/* 16px — inline with 13–14px text: buttons, filter triggers, table actions.
   Stroke bumped so it lands on 1.5px instead of 1.33px and holds against
   the text weight beside it. */
.sm-icon--16 { width: 16px; height: 16px; stroke-width: 2.25; }

/* 20px — sidebar nav, wizard step markers, icon buttons. The default. */
.sm-icon--20 { width: 20px; height: 20px; stroke-width: 2; }

/* 24px — metric tiles, empty states, modal headers. */
.sm-icon--24 { width: 24px; height: 24px; stroke-width: 2; }

/* The one place a legacy "X svg" size rule outranks the size classes:
   review_styles.css:493 gives .close-panel-btn svg 14px at (0,1,1). The
   panel's close is the date-range panel's icon button (19.2), so 16px. */
.close-panel-btn .sm-icon--16 {
  width: 16px;
  height: 16px;
}

/* --------------------------------------------------------------------------
   15.2 The sidebar, and the filter: invert() hack retired
   base_styles.css:159-165 recoloured the active item's icons with
   filter: invert(36%) sepia(85%) saturate(1422%) hue-rotate(175deg)
   brightness(92%) contrast(101%) -- the only way to turn a dark PNG brand
   blue. No <img> icon is left in any template (the two that remain are the
   logos), so the filter is switched off on the same four selectors
   (equal specificity, later in the cascade; the original's filter is not
   !important). Delete the original rule when base_styles.css unfreezes.
   The rail's icons are --sm-ink-muted (was an inline #4b5563 on two of them
   and dark PNG fills on three); the current page's is --sm-brand-strong,
   the interactive brand (HANDOFF.md 3.18, 2.1).
   -------------------------------------------------------------------------- */

.icon-link-wrapper.special .icon-wrapper img,
.icon-link-wrapper.special .icon-relationship,
.icon-link-wrapper.special .customer-list-svg-icon,
.icon-link-wrapper.special .relationship-svg-icon {
  filter: none;
}

.icon-link-wrapper .icon-wrapper {
  color: var(--sm-ink-muted);
}

.icon-link-wrapper.special .icon-wrapper {
  color: var(--sm-brand-strong);
}

/* The collapse toggle. UA button padding would leave a 32px button an 18px
   content box, so it goes. App round item 3: the rotated chevron is replaced
   by two sprite icons, panel-left-open while the rail is collapsed and its
   mirror, panel-left-close, while it is open. One is shown at a time. */
.open-left-bar {
  padding: 0;
  color: var(--sm-ink-muted);
}

.left-bar:not(.open) .open-left-bar .sm-toggle-close,
.left-bar.open .open-left-bar .sm-toggle-open {
  display: none;
}

/* --------------------------------------------------------------------------
   15.2a The sidebar mark and toggle     Round 3 · items 3 + 4 · SHELL-HANDOFF 1
   shell/sidebar-rail.css, appended here as SHELL-HANDOFF.md 1 directs, with
   the template change in left_bar.html (1.3); aria-expanded and the label
   are kept in step by syncSilkSidebarToggle() (1.4).

   The rail stays 72px / 240px, so --sm-inset, every page's left inset and
   the 276px expanded push (sections 1-2) do not move. What changes:
     the header is 72px (was 90) and holds the mark alone, 40px (was 55) on
       the nav tiles' 16px column. LOGO-HANDOFF 8.1's 55px floor has ONE
       exception, this sidebar (SHELL-HANDOFF 1.7). Nowhere else;
     expanded, the 204x40 lockup replaces the mark, its own mark at the same
       x = 16, so nothing jumps; .container-text (a second, live-text
       wordmark) is gone, as the lockup carries the wordmark;
     the toggle is no longer a floating circle straddling the rail edge on
       top of the logo. It is an ordinary row at the head of .section-2, so
       it sits directly above Settings -- and, with Settings hidden (item 6),
       directly above the rule and the profile.

   App round (round 5), items 1, 4 and 3:
     the mark and lockup are the 7c Weave (partials/logo.html), at the same
       40px / 204x40 and the same x = 16;
     the header is 88px, not 72: 24px (--sm-space-5) above the logo and 24px
       below it to the Dashboard row, was 16 and 16. Measured, collapsed and
       expanded alike: logo top 16 -> 24, logo to Dashboard row 16 -> 24,
       logo to Dashboard icon 26 -> 34;
     the toggle is icon only, a 40px square on the nav icon column in both
       states, with its aria-label as a tooltip.
   -------------------------------------------------------------------------- */

/* Collapsed the rail has no padding, so the header pads 16px. Expanded,
   .left-bar.open already pads 16px (base_styles.css:35), so the header pads
   0. Either way the mark's left edge is at x = 16. base's align-items:
   center splits the height evenly above and below the 40px logo. */
.top-container {
  height: calc(40px + 2 * var(--sm-space-5));     /* 88px; was 72px (90px in base) */
  justify-content: flex-start;
  padding-left: var(--sm-space-4);
  gap: 0;
}

.left-bar.open .top-container {
  padding-left: 0;                                 /* was 4px */
  gap: 0;                                          /* was 12px */
}

.bottom-container {
  height: calc(100% - 40px - 2 * var(--sm-space-5)); /* follows the header */
}

.top-container .logo {
  width: 40px;                                     /* was 55px */
  height: 40px;
  transition: none;
}

.top-container .logo-lockup {
  display: none;
  width: 204px;                                    /* the lockup at M = 40px, */
  height: 40px;                                    /* its minimum (SHELL-HANDOFF 4.3) */
}

.left-bar.open .top-container .logo { display: none; }
.left-bar.open .top-container .logo-lockup { display: block; }

/* The template no longer renders .container-text. This keeps a restored
   copy from painting a second wordmark beside the lockup. */
.container-text,
.left-bar.open .container-text { display: none; }

/* The toggle: a 40px icon button on the nav icon column. Same id, class and
   onclick as before. Relative only to anchor the tooltip below. */
.open-left-bar {
  position: relative;                              /* was absolute; fixed at <=768px */
  left: auto;                                      /* base_styles.css:328; relative would honour it */
  right: auto !important;                          /* beats base_styles.css:82 */
  top: auto;
  z-index: auto !important;                        /* beats base_styles.css:93 */
  flex: none;
  width: 40px;
  height: 40px;
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  border: 1px solid transparent;                   /* reserves the focus border */
  border-radius: var(--sm-radius-control);
  background-color: transparent;
  box-shadow: none;                                /* was --sm-e1 (section 13) */
  color: var(--sm-ink-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  font: inherit;
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.open-left-bar:hover {
  background-color: var(--sm-surface-sunk);
  color: var(--sm-ink);
}

.open-left-bar:active {
  background-color: var(--sm-line);
  color: var(--sm-ink);
}

/* Focus: 5.7's .open-left-bar:focus-visible ring (box-shadow + border),
   unchanged, except at <= 768 collapsed, where the floating circle below
   restates it. */

/* The icon's box is an .icon-wrapper for its size only. base_styles.css:466
   and :474 give every .icon-wrapper its own hover fill, lift and press
   squash; on the toggle the row carries the state, so those go. */
.open-left-bar .icon-wrapper {
  width: 24px;
  height: 24px;
  cursor: inherit;
}

.open-left-bar .icon-wrapper:hover,
.open-left-bar .icon-wrapper:active {
  background-color: transparent;
  transform: none;
}

/* Expanded (round 7, item A: sidebar-toggle-1a): a nav row, the full 208px
   with the icon and "Collapse", built like the nav rows above it: 12px to
   the 24px icon box (11px padding + the 1px reserved border), 14px to the
   label (.nav-text's own margin), 14/500. Collapsed it is the 40px icon
   square at x = 16, as the nav icons are, and the label is display: none
   (.nav-text, base_styles.css:187). */
.left-bar.open .open-left-bar {
  left: auto;                                      /* base_styles.css:335 (240px at <=768) */
  width: 100%;                                     /* was 40px */
  margin-left: 0;                                  /* was 12px */
  padding-left: 11px;
  justify-content: flex-start;
}

.open-left-bar .nav-text {
  color: inherit;                                  /* the button's rest / hover colour */
  line-height: 20px;                               /* the handoff's 14/20 */
}

/* Expanded, the tooltip sits above the row on its icon column (the
   handoff's left 12px, bottom 46px): beside it, it would hang past the
   rail edge over the page. */
.left-bar.open .open-left-bar::after {
  left: 12px;
  top: auto;
  bottom: calc(100% + 6px);
  transform: none;
}

/* The tooltip: the aria-label, so the visible and spoken names cannot
   drift. Beside the button, over the page (the rail is overflow: visible,
   base_styles.css:29, and z-index 99999). Hover shows it after 300ms, on
   devices that hover; keyboard focus shows it at once. */
.open-left-bar::after {
  content: attr(aria-label);
  position: absolute;
  left: calc(100% + var(--sm-space-2));
  top: 50%;
  transform: translateY(-50%);
  padding: var(--sm-space-1) var(--sm-space-2);
  border-radius: var(--sm-radius-control);
  background-color: var(--sm-ink);
  color: var(--sm-surface);
  box-shadow: var(--sm-e2);
  font-size: var(--sm-type-small-size);            /* 13px, the floor */
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-medium);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 120ms ease, visibility 0s linear 120ms;
}

.open-left-bar:focus-visible::after {
  opacity: 1;
  visibility: visible;
  transition: opacity 120ms ease, visibility 0s;
}

@media (hover: hover) {
  .open-left-bar:hover::after {
    opacity: 1;
    visibility: visible;
    transition: opacity 120ms ease 300ms, visibility 0s linear 300ms;
  }
}

.section-2 .open-left-bar {
  margin-bottom: var(--sm-space-2);                /* 8px, the nav gap */
}

@media (prefers-reduced-motion: reduce) {
  .open-left-bar,
  .open-left-bar::after { transition: none; }
}

/* At <=768px the rail is off-canvas (base_styles.css:317, left -80px), and
   the toggle moved into it would go too. SHELL-HANDOFF 1.6 says it was out
   of reach before as well; measured, it was not: base_styles.css:327 pinned
   the old circle position: fixed at (72, 20), on screen, and tapping it was
   the only way to open the sidebar on a phone. Until the top-bar menu button
   exists (DEFERRED-FIXES #82), the collapsed toggle keeps exactly that: the
   32px circle at base's own left/top. Open, it is the collapse button in
   the rail, as at every other width. */
@media (max-width: 768px) {
  .left-bar:not(.open) .open-left-bar {
    position: fixed;
    left: 72px;                                    /* base_styles.css:328 */
    top: 20px;                                     /* base_styles.css:331; 15.2a's top: auto would win */
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border-color: var(--sm-line);
    background-color: var(--sm-surface);
    box-shadow: var(--sm-e1);
  }

  /* here: the rule above is (0,3,0) and outranks 5.7's (0,2,0) ring, so the
     one control that opens the sidebar on a phone had no focus state
     (session 23). The ring goes back on at the same specificity, later. */
  .left-bar:not(.open) .open-left-bar:focus-visible {
    border-color: var(--sm-brand);
    box-shadow: var(--sm-focus);
  }
}

/* --------------------------------------------------------------------------
   15.3 The wizard step markers                          HANDOFF.md 3.14, 2.7
   The header drew each step as one of 15 PNGs: a grey outlined disc per
   step for pending AND current (they were identical), and a solid green
   disc with a white check for done. Now one icon per step inside a 30px
   marker whose state the template writes as a class, with a completed step
   showing i-step-done instead of its own icon:
     done      --sm-success-tint + --sm-success-ink check
     current   --sm-brand-tint + --sm-brand-deep + 1px --sm-brand inset ring
     upcoming  --sm-surface-sunk + --sm-ink-quiet
   30px and the 10px under it are the <img>'s (base_styles.css:412), so no
   step changes height.
   -------------------------------------------------------------------------- */

.top-bar .right-container .step-marker {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 30px;
  height: 30px;
  margin-bottom: 10px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-surface-sunk);
  color: var(--sm-ink-quiet);
}

.top-bar .right-container .step-marker.is-current {
  background-color: var(--sm-brand-tint);
  box-shadow: inset 0 0 0 1px var(--sm-brand);
  color: var(--sm-brand-deep);
}

.top-bar .right-container .step-marker.is-done {
  background-color: var(--sm-success-tint);
  color: var(--sm-success-ink);
}

/* --------------------------------------------------------------------------
   15.4 Where a glyph became an icon
   The glyph wrappers keep their elements and class names (JS and 6.5 read
   them); only their content changed. As text they were line boxes; as
   icon holders they are sized by the icon, so they are flex boxes.
   .dropdown-arrow-indicator keeps its --sm-ink-faint (#9ca3af) from
   customer_styles.css:210 -- an icon, not text, so 2.8's rule on #9ca3af
   does not apply.
   -------------------------------------------------------------------------- */

.chevron-arrow,
.dropdown-arrow-indicator,
.pagination-ellipsis-break {
  display: inline-flex;
  align-items: center;
}

/* Two places the icon's box was bigger than the glyph's and moved a layout.
   The customer-detail date button is an inline-flex row around a 15px line
   (13px x 1.15). The 16px chevron made it 26px tall, not 24.9 (every row
   with a date menu 1px taller), and 8px wider than the 10px glyph plus its
   4px margin, which was enough to re-wrap the email table's columns at 900
   and 800. chevron-down inks only x 6-18, y 9-15 of its 24 units (4-12 of
   16px), so 2px of box back at each side and 1px top and bottom clips no
   ink, and the button is 113 x 24.9 again, to the pixel.
   The pagination ellipsis was ".." (7px) in 4px side padding; the 16px icon
   carries its own side bearing (the dots span x 5-19 of 24), so the padding
   goes. Without that, the relationship list's pager wrapped under the
   entries count at 768 (208 + 487 + 12px gap in a 704px bar). */
.date-dropdown-btn .dropdown-arrow-indicator {
  margin-left: 0;                                  /* was 4px, customer_styles.css:211 */
}

.date-dropdown-btn .dropdown-arrow-indicator .sm-icon {
  margin: -1px -2px;
}

.pagination-ellipsis-break {
  padding-inline: 0;                               /* was 4px, customer_styles.css:442 */
}

/* The sort indicator was two stacked 10px glyphs; it is one icon slot. At
   rest it holds i-sort (chevrons-up-down); once a column is sorted the list
   script writes the direction into it (i-chevron-up / -down, with the
   .active-arrow class 6.5 already colours) and aria-sort onto the header. */
.sort-arrows-indicator {
  line-height: 0;
}

/* Both calendars' month arrows are the date-range panel's
   .sm-daterange__icon-btn now (section 17, item 22). */

/* Review's related-card deck arrows (.scroll-arrow) were deleted from the
   template in 19.2: the relationships no longer page, and no script ever
   bound the two buttons (DEFERRED-FIXES #76). */

/* --------------------------------------------------------------------------
   15.5 Where an image became an icon
   -------------------------------------------------------------------------- */

/* Mapping's row arrow was a Bootstrap arrow with fill="#018ac6" hard-coded
   into a JS string -- --sm-brand as a fill (HANDOFF.md 2.1). It is static,
   so --sm-brand-deep (TYPE-AND-ICONS-HANDOFF.md). */
.mapping-arrow {
  color: var(--sm-brand-deep);
}

/* The dashboard drop zone's Upload.png was a 42-56px green-gradient
   illustration. It is i-upload at 24px on a 48px disc: the footprint stays
   close, and brand-strong because the whole zone is the file picker's
   label -- it is interactive. */
.upload-drop-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-strong);
}

/* The arbitration screens' empty columns were a 2rem emoji (📭). The
   handoff's empty-state disc, TYPE-AND-ICONS-HANDOFF.md item 13: pending
   neutral, never danger red -- an empty column is not an error. "No
   Artifact Record Found" takes i-no-artifact; "No NetSuite Record Found"
   and "No Record Found" take i-empty, the inbox the emoji was. The 8px
   under it is the emoji's .5rem. Nine of the eleven sit in an inline
   `text-align: center` wrapper, which centred the inline emoji but does not
   centre a block box, so the disc centres itself. */
.sm-empty-icon {
  width: 48px;
  height: 48px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sm-surface-sunk);
  color: var(--sm-ink-quiet);
  margin: 0 auto var(--sm-space-2);
}

/* Round 2 (DEFERRED-FIXES #77, confirmed): the Artifact panel is itself
   --sm-surface-sunk, so the disc there was the panel's own grey behind the
   glyph. The four "No Artifact Record Found" icons carry --bare and drop it;
   the disc's box stays, so the glyph and the text under it do not move. */
.sm-empty-icon--bare {
  background: none;
}

/* ==========================================================================
   16. CLIENT CHECKLIST -- ROUND 2 BUG FIXES       Round 2 · items 8-24, #77
   --------------------------------------------------------------------------
   Each rule names its checklist item. Items 1, 6, 12, 21 and #77(b) live
   elsewhere: 1 is not done (DEFERRED-FIXES), 6 and 21 are template-only,
   12 is edited in place in 7.6, #77(b) sits beside .sm-empty-icon in 15.
   ========================================================================== */

/* --------------------------------------------------------------------------
   16.1 The dashboard's NetSuite list ends with its last row   items 8, 9
   .lines-wrapper was a scroll box capped at 42vh (300px below 1280,
   home_styles.css:158, :401). Its bottom edge fell wherever the cap did,
   through the middle of a row: with four sources, the fourth was cut at
   1100, 768 and 390, and at 1440 on a 740px-tall window. The box also
   began flush with its first row, so the row's 1px hover lift
   (translateY(-1px), home_styles.css:179) pushed its top border under
   the box's clip edge. With the cap gone the list is as tall as its rows
   and there is no clip edge to pass under, so one rule closes both. The
   count is the number of connected source systems (4 in the real app, 5
   in the preview); if that ever runs to dozens, the list wants a
   row-snapped scroll box, not this cap back. The 260px floor stays.
   -------------------------------------------------------------------------- */
.lines-wrapper {
  max-height: none;
  overflow-y: visible;
}

/* --------------------------------------------------------------------------
   16.2 Matching: the deck pager gets room                    item 11
   The two 32px pagers sat 20px from the deck's top and 30px from its
   right, 12px apart, with 18px between them and the first card (48px of
   box padding, matching_styles.css:168, and 3.5em below 1023px, :625 --
   below 1200px until WIZARD-HANDOFF §0 moved that breakpoint).
   Now 16px apart on the space scale, inset 16/24px, and the box padding
   is 64px, so there are 32px between the pager and the cards. Same
   selector as the originals, so this beats both on source order. The 44px
   hit areas (5.9) no longer touch. The padding and the pager top are
   superseded by 18.1 (a 48px header band); the gap stands.
   -------------------------------------------------------------------------- */
.main-content .top-cards .right-card-box {
  padding-top: var(--sm-space-8);                  /* was 3em / 3.5em */
}

.right-card-box .navigation-buttons {
  top: var(--sm-space-4);                          /* was 20px */
  right: var(--sm-space-5);                        /* was 30px */
  gap: var(--sm-space-4);                          /* was 12px (5.9) */
}

/* --------------------------------------------------------------------------
   16.3 List pages: an applied filter looks applied           item 20
   HANDOFF.md 3.9 ("the active filter pill shows its selection") and 1.1
   (--sm-brand-tint: "active filter pills"). The template adds
   .filter-applied to a trigger when its filter is in the request: Source,
   Type and Date on the customer list, and the search field on both list
   pages once it holds 3+ characters (the length the views act on). The
   relationship list has no other filter: its tabs already show which one
   is active. Show: N is page size, not a filter, so it never takes this.
   -------------------------------------------------------------------------- */
.dropdown-trigger.filter-applied,
.dropdown-trigger.filter-applied:hover {
  border-color: var(--sm-brand);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-strong);
}

.search-wrapper.filter-applied input {
  border-color: var(--sm-brand);
  background-color: var(--sm-brand-tint);
}

.search-wrapper.filter-applied .search-icon {
  color: var(--sm-brand-strong);
}

/* --------------------------------------------------------------------------
   16.4 The export modal: a close button and typed dates      items 23, 24
   Removed in round 3 (item 22). Session 15's stopgap -- .export-modal-header,
   .export-modal-close, .export-range-fields and .export-range-field -- is
   replaced by the date-range panel, whose close button and From / To fields
   are section 17's.
   -------------------------------------------------------------------------- */

/* ==========================================================================
   17. THE DATE-RANGE COMPONENT                  SHELL-HANDOFF.md 3 · item 22
   --------------------------------------------------------------------------
   Round 3. One panel, two contexts: .sm-daterange--modal (the export modal,
   left_bar.html, every page) and .sm-daterange--popover (the customer
   list's Date filter). Markup: templates/partials/date_range.html.
   17.1-17.9 are silk-matrix-design/shell/daterange.css as handed off, with
   the changes marked "here:"; 17.10 is what this codebase adds around it.

   Replaces session 15's stopgap (16.4: the export modal's close button and
   typed From / To) and supersedes 12.3 (the calendar on every page), #55's
   pill-row wrap (5.11) and the month-arrow buttons (15).

   Day cells keep the class names both scripts always generated: .day-dot,
   .active-day, .in-range. New: .is-today, set by one line in each script.
   ========================================================================== */

/* 17.1 The panel */
.sm-daterange {
  width: 320px;
  max-width: calc(100vw - 32px);
  box-sizing: border-box;
  padding: 20px;
  background-color: var(--sm-surface);
  border-radius: var(--sm-radius-card);
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-4);
  color: var(--sm-ink);
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  text-align: left;
}

.sm-daterange--modal { box-shadow: var(--sm-e3); }

.sm-daterange--popover {
  position: absolute;
  top: calc(100% + var(--sm-space-2));             /* 8px under the trigger */
  right: 0;                                        /* right-aligned: the Date pill is last in the bar */
  z-index: 50;
  padding: 19px;                                   /* here: the 1px border comes out of the 320px, so */
  border: 1px solid var(--sm-line);                /* 19px keeps the content box at 280 = 7 x 40, the */
  box-shadow: var(--sm-e2);                        /* modal's; at 20px the grid overhung it by 2px */
}

/* 17.2 Head: title (modal) or eyebrow label (popover), plus close. */
.sm-daterange__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: -4px -8px -4px 0;                        /* optically aligns the 32px close with the padding */
}

.sm-daterange__title {
  margin: 0;
  font-size: var(--sm-type-subtitle-size);
  line-height: var(--sm-type-subtitle-leading);
  font-weight: var(--sm-type-subtitle-weight);
}

.sm-daterange--popover .sm-daterange__title {
  font-size: var(--sm-type-micro-size);
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: uppercase;
  color: var(--sm-ink-quiet);
}

.sm-daterange__icon-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--sm-radius-control);
  background: transparent;
  color: var(--sm-ink-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.sm-daterange__icon-btn:hover { background-color: var(--sm-surface-sunk); color: var(--sm-ink); }
.sm-daterange__icon-btn:active { background-color: var(--sm-line); }
.sm-daterange__icon-btn:focus-visible { outline: none; border-color: var(--sm-brand); box-shadow: var(--sm-focus); }

/* 17.3 Presets: three equal pills. aria-pressed carries the state; the
   existing .pill-active class is also honoured. Both scripts set both.
   Here: the hover pins background and colour, because 5.11's
   .quick-pill-btn:hover (same specificity, earlier) would otherwise lend it
   the brand-wash fill; the handoff's hover is the border alone. */
.sm-daterange__presets {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}

.sm-daterange__preset {
  height: 30px;
  box-sizing: border-box;
  padding: 0 var(--sm-space-2);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-surface);
  color: var(--sm-ink-body);
  font: inherit;
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  white-space: nowrap;
  cursor: pointer;
}
.sm-daterange__preset:hover {
  border-color: var(--sm-brand);
  background-color: var(--sm-surface);             /* here: see 17.3 */
  color: var(--sm-ink-body);
}
.sm-daterange__preset:focus-visible { outline: none; border-color: var(--sm-brand); box-shadow: var(--sm-focus); }
.sm-daterange__preset[aria-pressed="true"],
.sm-daterange__preset.pill-active {
  background-color: var(--sm-brand-tint);
  border-color: var(--sm-info-border);
  color: var(--sm-brand-deep);
  font-weight: var(--sm-weight-semibold);
}

/* 17.4 Typed From / To. Native type="date": keyboard entry and the OS
   picker for free. */
.sm-daterange__fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sm-space-2);
}

.sm-daterange__field {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-1);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  color: var(--sm-ink-muted);
}

.sm-daterange__field input {
  width: 100%;
  min-width: 0;
  height: 38px;
  box-sizing: border-box;
  padding: 0 10px;
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-radius-control);
  background-color: var(--sm-surface);
  color: var(--sm-ink);
  font: inherit;
  font-size: var(--sm-type-body-size);
  font-variant-numeric: tabular-nums;
}
.sm-daterange__field input:focus-visible { outline: none; border-color: var(--sm-brand); box-shadow: var(--sm-focus); }
.sm-daterange__field input[aria-invalid="true"] { border-color: var(--sm-danger-solid); background-color: var(--sm-danger-tint); }

/* 17.5 Month header */
.sm-daterange__month {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-right: -8px;
}
.sm-daterange__month-label { font-weight: var(--sm-weight-semibold); }
.sm-daterange__month-nav { display: flex; gap: var(--sm-space-1); }

/* 17.6 Grid: 7 x 40px = 280px, the panel's content width.
   Here: gap, size, weight, colour and alignment are pinned because
   customer_styles.css:249 gives .calendar-days-grid a 6px row-gap, 12px/500
   and centring on the three pages that load it and nothing elsewhere; the
   panel must be the same on every page. */
.sm-daterange__weekdays,
.sm-daterange .calendar-days-grid {
  display: grid;
  grid-template-columns: repeat(7, 40px);
  margin: 0;
}

.sm-daterange .calendar-days-grid {
  gap: 0;
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-body);
  text-align: center;
}

.sm-daterange__weekdays span {
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--sm-type-micro-size);
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  color: var(--sm-ink-quiet);
}

/* 17.7 A day is one element: the element is the 40x36 cell and carries the
   range band; ::before is the 32px circle behind the number; ::after the
   today dot. */
.sm-daterange .day-dot {
  position: relative;
  isolation: isolate;
  height: 36px;
  width: auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--sm-ink-body);
  font: inherit;
  font-size: var(--sm-type-small-size);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: none;                                /* here: customer_styles.css:269 animates "all" on 3 pages */
}

.sm-daterange .day-dot::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 2px;
  left: 4px;
  width: 32px;
  height: 32px;
  border-radius: var(--sm-radius-pill);
}

.sm-daterange .day-dot:hover::before { background-color: var(--sm-surface-sunk); }
.sm-daterange .day-dot:focus-visible { outline: none; }
.sm-daterange .day-dot:focus-visible::before { box-shadow: 0 0 0 1px var(--sm-brand), var(--sm-focus); }

/* Blank leading cells */
.sm-daterange .day-dot:empty { pointer-events: none; }
.sm-daterange .day-dot:empty::before { display: none; }

/* Here: !important because customer_styles.css:283 sets .in-range's fill and
   ink !important. Same values as the handoff's. */
.sm-daterange .day-dot.in-range {
  background-color: var(--sm-brand-tint) !important;
  color: var(--sm-brand-deep) !important;
}
.sm-daterange .day-dot.in-range:hover::before { background-color: var(--sm-brand-wash); }

/* Start and end. Clickable, so brand-strong (HANDOFF 2.1).
   Here: customer_styles.css:276 fills the whole .active-day element
   #018ac6 !important; the circle is ::before, so the element's own fill is
   cleared the same way. The half-band below is a background-image, which
   that declaration does not touch. */
.sm-daterange .day-dot.active-day {
  background-color: transparent !important;
  color: #ffffff !important;
  font-weight: var(--sm-weight-semibold);
}
.sm-daterange .day-dot.active-day::before { background-color: var(--sm-brand-strong); }
.sm-daterange .day-dot.active-day:hover::before { background-color: var(--sm-brand-deep); }

/* Half-band so the range reads as one shape. Both scripts give start and end
   the same class, so the side is found from the neighbour. */
.sm-daterange .day-dot.active-day:has(+ .in-range),
.sm-daterange .day-dot.active-day:has(+ .active-day) {
  background: linear-gradient(to right, transparent 50%, var(--sm-brand-tint) 50%);
}
.sm-daterange .day-dot.in-range + .active-day,
.sm-daterange .day-dot.active-day + .active-day {
  background: linear-gradient(to left, transparent 50%, var(--sm-brand-tint) 50%);
}

.sm-daterange .day-dot.is-today { font-weight: var(--sm-weight-semibold); }
.sm-daterange .day-dot.is-today::after {
  content: "";
  position: absolute;
  bottom: 5px;
  left: 18px;
  width: 4px;
  height: 4px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-brand);
}
.sm-daterange .day-dot.active-day.is-today::after { background-color: #ffffff; }

.sm-daterange .day-dot[aria-disabled="true"] {
  color: var(--sm-ink-faint);
  cursor: not-allowed;
}
.sm-daterange .day-dot[aria-disabled="true"]::before { background: none; }

/* 17.8 Hint: replaces the open-ended-range alert() */
.sm-daterange__hint {
  margin: -8px 0 0;
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  color: var(--sm-ink-muted);
}
.sm-daterange__hint[hidden] { display: none; }

/* Extra options slot (export: New customers only) */
.sm-daterange__option {
  display: flex;
  align-items: center;
  gap: var(--sm-space-2);
  color: var(--sm-ink-body);
  cursor: pointer;
}
.sm-daterange__option input {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--sm-brand-strong);
  cursor: pointer;
}

/* 17.9 Footer. The buttons keep their existing classes (.calendar-clear-btn
   is tertiary, 5.4; .calendar-done-btn is primary, 5.3) and so their
   existing styling. The Clear button used to wear .calendar-done-btn with an
   inline override (#15); it is .calendar-clear-btn now. */
.sm-daterange__foot {
  display: flex;
  gap: var(--sm-space-2);
  padding-top: var(--sm-space-4);
  border-top: 1px solid var(--sm-line);
}
.sm-daterange__foot > button {
  flex: 1;
  margin: 0;
}
.sm-daterange__foot .calendar-done-btn:disabled {
  background-color: var(--sm-surface);
  border-color: var(--sm-line);
  color: var(--sm-ink-faint);
  cursor: not-allowed;
}

/* The trigger. Here: no .is-filled. The handoff's filled state follows the
   selection as it is being picked; 16.3's .filter-applied, set by the
   template from the request, already shows the APPLIED range in the same
   active-pill treatment as Source and Type, and a second, client-side
   "filled" look would claim a filter that has not been applied. */
#dateDropdown { position: relative; }
#dateDropdown .dropdown-trigger #dateRangeLabel { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   17.10 The two wrappers around the panel
   -------------------------------------------------------------------------- */

/* The export modal keeps .calendar-window-frame as the panel's wrapper,
   because its inline onclick="event.stopPropagation()" is one of the six
   markup-wired handlers. Its inline style="" (310px, 20px radius, 18px
   padding, shadow: #67) is deleted; customer_styles.css:217 still gives the
   class 290px and 15px padding on three pages, so it is cleared here and
   the panel is the whole visual. */
.export-modal-overlay .calendar-window-frame {
  width: auto;
  max-width: none;
  padding: 0;
}

/* Centred in the area right of the rail, as section 4 does for
   .modal-overlay; the scrim still covers the full viewport. The overlay is
   box-sizing: border-box !important (base_styles.css:676), so the padding
   comes out of its 100vw. */
.export-modal-overlay {
  padding-left: var(--sm-inset);
}

@media (min-width: 1024px) {
  .left-bar.open ~ .export-modal-overlay {
    padding-left: calc(var(--sm-inset) + 168px);
  }
}

/* The list filter keeps #dateDropdown's .dropdown-menu-content as the
   wrapper, because the custom-dropdown engine opens and closes it with
   .menu-open (customer_styles.css:181) and must not be replaced
   (SHELL-HANDOFF 3.4). Its own panel look (border, 14px radius, 6px padding,
   shadow, min-width, absolute position) is removed so it only shows and
   hides; the popover then anchors to #dateDropdown itself. */
#dateDropdown .dropdown-menu-content {
  position: static;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

/* ==========================================================================
   18. WIZARD PAGES                          WIZARD-HANDOFF.md rev 2 · Phase 4
   --------------------------------------------------------------------------
   silk-matrix-design/wizard/*.css as handed off, one subsection per file.
   The stack breakpoint these rules assume (side by side from 1024px up) is
   not here: WIZARD-HANDOFF §0 moved it in place, matching_styles.css:608
   and name_selection_styles.css:563, 1200px -> 1023px. Those two lines are
   the round's only edit to the originals.
   ========================================================================== */

/* --------------------------------------------------------------------------
   18.1 Matching: the deck responds to the match count     items 10, 11
   wizard/matching-deck.css below its own header, with the four changes
   marked "here:", each found by measuring: the new record's centring, the
   one-card floor on the tray, the pair's equal widths, the snap line. It
   must stay after 16.2 (tray padding and pager top) and 13.1 (the cards'
   4px top margin): same selectors, so source order is what lets it win.
   -------------------------------------------------------------------------- */
/* --- The header band: pagers and count in the tray's top 48px -------------
   Tray padding 44 + the scroller's 4px top padding = 48 to the card tops.
   The 4px is inside the scroller so the selected card's 3px ring is not
   clipped by its overflow. Pagers: 32px, top 8, so their centre is at 24,
   the middle of the band. These apply at every width (the band works
   stacked too); they replace §16.2's padding-top and pager top. */
.main-content .top-cards .right-card-box {
  padding-top: calc(var(--sm-space-7) - 4px);      /* was 64px (§16.2) */
}

.right-card-box .navigation-buttons {
  top: var(--sm-space-2);                          /* was 16px (§16.2) */
  right: var(--sm-space-5);
}

.right-card-box .cards-container {
  margin-top: 0;                                   /* was 2% of the tray's width: ~18px at 1440, ~11px at 1024 */
  padding-top: 4px;                                /* was 0 */
}

.cards-container .potential-match-card {
  margin-top: 0;                                   /* 13.1 set 4px */
}

/* --- The new record's top on the same line (side by side only) -----------
   was padding-top 2em + the card's margin-top 20px = 52px.
   here: plus a centring offset the handoff did not count. The row stretches
   .left-card-box to the tray's height and it is justify-content: center
   (matching_styles.css:29), so a new record shorter than the match cards sat
   lower still: measured 92.4px at 1440 at HEAD, 71.5px with only the two
   declarations below. flex-start pins it to the padding edge. */
@media (min-width: 1024px) {
  .main-content .top-cards .left-card-box {
    min-width: max(30%, 300px);                    /* 30% alone is 265px at 1024: too narrow for the label/value rows */
    padding-top: var(--sm-space-7);                /* 48 */
    justify-content: flex-start;                   /* here: was center */
  }

  /* here: the tray never gets narrower than one whole card. The new record
     is flex-shrink: 0 (matching_styles.css:33), so every pixel the row lacks
     came out of the tray: at a 1024 window with the sidebar expanded it was
     312px, under one 340px card, and the pager stopped mid-card. Now the
     tray still gives way first -- its shrink factor dwarfs the new record's,
     so wherever it stays above the floor the new record keeps its width to
     the pixel -- and once the tray reaches 392 the new record shrinks
     toward its 300px floor instead. 392 is one card (30px padding + 10px
     margin + 340) with 12px clear on its right, and it is exactly what the
     tightest row from 1024 up leaves: a 1024 window with the sidebar open
     is a 716px row, less the 24px gap and the 300px floor. The pair resets
     both (below). */
  .main-content .top-cards .left-card-box {
    flex-shrink: 1;                                /* was 0 */
  }

  .main-content .top-cards .right-card-box {
    min-width: 392px;                              /* was 0 */
    flex-shrink: 1000;
  }

  .top-cards .left-card-box .new-record-card {
    margin-top: 0;
  }
}

/* The tray is a size container so "do N cards fit" is asked of the tray's
   own width, not the viewport's. That keeps it right when the sidebar
   expands and pushes the content at 1024+. Its width is set by flex, never
   by its content, so inline-size containment changes nothing about it. */
.main-content .top-cards .right-card-box {
  container: sm-deck / inline-size;
}

/* --- The count, top-left of the header band -------------------------------
   A CSS counter so it drops the moment a card is rejected. The ::after is the
   last box in .cards-container, so it reads the final total. It is absolutely
   positioned against .right-card-box (position: relative, matching_styles.css
   :168); .cards-container and the form are not positioned, so the scroller
   does not clip it. */
.right-card-box .cards-container {
  counter-reset: sm-match;
}

.right-card-box .cards-container > .potential-match-card {
  counter-increment: sm-match;
}

.right-card-box .cards-container > .potential-match-card.removed {
  counter-increment: none;
}

.right-card-box .cards-container::after {
  content: counter(sm-match) " potential matches";
  position: absolute;
  top: calc(var(--sm-space-2) + 7px);              /* 8 + (32 - 18) / 2: centred on the pagers, band centre 24 */
  left: 40px;                                      /* the first card's left edge: 30px container padding + 10px card margin */
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.right-card-box .cards-container:not(:has(> .potential-match-card:not(.removed) ~ .potential-match-card:not(.removed)))::after {
  content: "1 potential match";
}

/* --- Snap points ------------------------------------------------------------
   The pager script scrolls by 350px; a card plus its margin and the gap is
   360px, so each click drifted 10px and the fourth click landed mid-card.
   Snapping makes every click land on a card edge. The script is unchanged.
   here: 40px, not the handoff's 30. The card edge is at 40 (30px container
   padding + 10px card margin, as the count's left says), so at 30 the rest
   position 0 was not a snap point and every stop sat 10px left of it. */
.right-card-box .cards-container {
  scroll-snap-type: x mandatory;
  scroll-padding-left: 40px;                       /* here: was 30px */
}

.right-card-box .cards-container > .potential-match-card {
  scroll-snap-align: start;
}

/* --- When every card fits: fill the tray, drop the pagers ------------------ */
.right-card-box .cards-container:not(:has(> .potential-match-card:nth-child(2))) > .potential-match-card {
  flex: 1 1 0;
  min-width: 0;
  max-width: 480px;
}

.right-card-box:not(:has(.potential-match-card:nth-child(2))) .navigation-buttons {
  display: none;
}

@container sm-deck (min-width: 724px) {           /* 2 x 340 + gap + padding */
  .right-card-box .cards-container:not(:has(> .potential-match-card:nth-child(3))) > .potential-match-card {
    flex: 1 1 0;
    min-width: 0;
    max-width: 480px;
  }

  .right-card-box:not(:has(.potential-match-card:nth-child(3))) .navigation-buttons {
    display: none;
  }
}

@container sm-deck (min-width: 1100px) {          /* 3 x 340 + 2 gaps + padding */
  .right-card-box .cards-container:not(:has(> .potential-match-card:nth-child(4))) > .potential-match-card {
    flex: 1 1 0;
    min-width: 0;
    max-width: 480px;
  }

  .right-card-box:not(:has(.potential-match-card:nth-child(4))) .navigation-buttons {
    display: none;
  }
}

/* --- One match: the pair ---------------------------------------------------
   The new record and its only candidate side by side, the same width, tops
   on the same 48px line. From 769px up: at 1024+ it is side by side anyway;
   769-1023 is the one place the pair overrides the (moved) stack rule, as
   agreed in the first pass. Below 769 the stack stays (phone pass). */
@media (min-width: 769px) {
  .main-content .top-cards:has(.cards-container > .potential-match-card:only-child) {
    flex-direction: row;
    align-items: flex-start;
  }

  .main-content .top-cards:has(.cards-container > .potential-match-card:only-child) > .left-card-box {
    flex: 1 1 0;
    min-width: 0;                                  /* beats max(30%, 300px) and the <=1023 100% */
    max-width: calc(520px + 2 * var(--sm-space-4));
    padding: var(--sm-space-7) var(--sm-space-4) 0; /* the same 16px sides as the tray, so the two cards are the same width at every size */
  }

  .main-content .top-cards:has(.cards-container > .potential-match-card:only-child) .new-record-card {
    margin-top: 0;
  }

  /* here: basis 32px, not 0. The left box carries its 16px sides itself and
     the tray carries them on the scroller inside it, and a border-box flex
     item's base size cannot go below its own padding, so at 0 / 0 the left
     started 32px ahead: 534 / 502 at 1200. min-width: 0 undoes the 392px
     one-card floor above, which is for the deck. */
  .main-content .top-cards:has(.cards-container > .potential-match-card:only-child) > .right-card-box {
    flex: 1 1 calc(2 * var(--sm-space-4));         /* here: was 1 1 0 */
    min-width: 0;                                  /* here */
    width: auto;
    max-width: calc(520px + 2 * var(--sm-space-4));
  }

  .main-content .top-cards:has(.cards-container > .potential-match-card:only-child) .cards-container {
    padding: 4px var(--sm-space-4) var(--sm-space-4);
  }

  .main-content .top-cards:has(.cards-container > .potential-match-card:only-child) .cards-container > .potential-match-card {
    flex: 1 1 0;
    max-width: none;
    margin-left: 0;
    margin-bottom: 0;
  }

  .main-content .top-cards:has(.cards-container > .potential-match-card:only-child) .cards-container::after {
    left: var(--sm-space-4);
  }
}

/* --------------------------------------------------------------------------
   18.2 Name and Address: every card in a deck is the same height    §0
   Not in the handoff; found verifying the breakpoint move. The selection
   decks page by the first card's pitch (getCardHeight(): the second card's
   top less the first's), so every card must be that tall. Stacked, the
   columns were full width and every card came out the same. Side by side
   at 1024-1200 they are 345-518px, values wrap by different amounts, and
   the cards differ: Name 357 / 317 / 267px at a 1024 window with the
   sidebar open, Address 246 / 268px at 1024 and 1100. Each click then
   landed further off the card top, 32px by the last stop on Name.

   A flex column cannot equalise its items; a grid with 1fr auto rows in an
   auto-height container makes every row the height of the tallest card.
   The pitch is then the same for every card, which is what the script
   assumes. Gap, margins, width and the transform the pager writes all
   carry over from flex unchanged. Where the cards were already equal
   (1440, every stacked width) it changes nothing. .cards-container2 is left
   out: it is the primary record's box, which no script pages, and it is
   display: block, so its card's margins collapse through it; as a grid the
   box would grow by them (the card would not move, but the box would).

   Email and Phone were excluded until their session, on the handoff's
   word that 18.3 (wizard/contact-rows.css) makes their rows equal by
   construction. Measured, it does for the rows it was built for, but not
   for every value: 18.3's last track grows for a value longer than the row
   (a 66-character email at a 115px column is five lines), and then only
   that card grows. So the equaliser covers all four screens: fixed rows
   keep the usual card at its designed height, this keeps the deck equal
   when data outruns it. On Email and Phone it is the right deck's
   .cards-container only; neither page has another. DEFERRED-FIXES #85.
   -------------------------------------------------------------------------- */
.top-cards :is(.left-cards, .right-cards) :is(.cards-container, .cards-container1, .cards-container3) {
  display: grid;                                   /* was flex, column */
  grid-auto-rows: 1fr;
}

/* --------------------------------------------------------------------------
   18.3 Email and Phone: rows and artifact decks          items 15, 16, 17
   wizard/contact-rows.css below its own header. After 7.3 (the rows it
   re-lays) and 18.2 (the deck equaliser, which now covers these two screens
   too). Four changes marked "here:", each found by measuring:
     - the Phone one-row form needs a 1130px row, not 1000: at a 1060 row
       (1200, sidebar closed) the number column is 154px and the preview's
       "+1 (210) 555-0147 ext. 2214" (187px) wraps, the one card that does
       goes 144px against 134, and the pager drifts 10px a stop;
     - the value's last track grows instead of letting the text spill: at a
       716px row (1024, sidebar open) the value column is 115px, not the
       ~160 the handoff's 358px columns imply, and a 66-character email
       takes five lines, which ran 8px past the row's divider;
     - --sm-deck-card counts the Count / Date row as 39px, not 38 (it keeps
       7.3's 1px divider): Email 176 / 200, Phone 134 / 176;
     - the deck minimum reserves 112px for the pagers, not the script's 104:
       each pager carries 12px margins above and below, 48 in all, not 40.
   -------------------------------------------------------------------------- */
/* ==========================================================================
   Items 15, 16, 17 — Email and Phone selection rows and artifact decks
   Append to overrides.css after §7.3 (rows). CSS only. See WIZARD-HANDOFF.md
   §2 and §3.

   THE RULE, shared with Name and Address: a value on the left; the selects
   that act on it in a column on the card's right edge, 7.5rem per select,
   top-aligned with the value's first line.
     Name     one select per row                     (unchanged)
     Address  four selects stacked beside five rows  (unchanged)
     Email    two selects STACKED beside the value   (new: values run to 65+
              characters and need the width)
     Phone    two selects side by side on one row    (unchanged: values are
              short)
   The Count / Date row on every artifact card puts its date select in the
   same right-hand column, so it lines up with the selects above it.

   Scoped to 601px and up. The <=600 rules in §7.3 (the phone pass) stand.

   PREREQUISITE (WIZARD-HANDOFF §0): name_selection_styles.css:563 changes
   from @media (max-width: 1200px) to @media (max-width: 1023px), so the two
   columns stay side by side from 1024px up. Edit that line; do not add a
   counter-rule.

   Side by side at 1024 makes a column as narrow as 345px (1024 with the
   sidebar expanded; the handoff said 358). Two row forms therefore depend on the width of the
   .top-cards row, measured with a container query, not the viewport, so
   the sidebar state is accounted for without script:
     Phone  one row when the row is 1130px or wider (here: the handoff said
            1000); below that, the Email form (value | two stacked
            selects), because three columns leave an extension number
            too narrow to stay on one line
     Email  a third 20px track when the row is under 800px, so a 65-character
            address can take four lines and the row height stays fixed
   Every card in a deck has the same width, so every card takes the same
   form and the deck script's equal-height rule holds.
   ========================================================================== */

.top-cards:has(> .right-cards) {
  container: sm-sel / inline-size;
}

@media (min-width: 601px) {

  /* --- Email: value | stacked control column ------------------------------
     Replaces the wrap-and-right-align layout (value on its own line, both
     selects on the next line pushed to the right edge), which left the value
     and its controls on different rows at opposite edges of the card.

     Row height is 8 + 30 + 4 + 30 + 8 = 80px plus the 1px divider, whatever
     the value's length, up to three lines (60px). That makes the card height
     structural, which is what the deck script needs (§ deck, below). It is
     10px shorter than the 90px row it replaces. */
  .top-cards .record :is(.email-attributes, .email-attributes-staging)
    .line:not(:has(> .count_date)) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 7.5rem;
    grid-template-rows: 30px minmax(30px, auto);   /* here: was 30px 30px */
    column-gap: var(--sm-space-3);
    row-gap: var(--sm-space-1);
    align-items: start;
    align-content: start;
    min-height: 0;                                 /* the 2-line reservation in §7.3 is no longer needed */
    padding-block: var(--sm-space-2);
  }

  .top-cards .record :is(.email-attributes, .email-attributes-staging)
    .line:not(:has(> .count_date))::after {
    display: none;                                 /* the flex line-break pseudo from §7.3 */
  }

  /* The value spans both control rows. 5px puts its first line's centre on
     the first select's centre (8 + 15 = 23 = 8 + 5 + 10).
     here: the last track is minmax(30px, auto), so a value longer than the
     row was built for grows the row instead of running over its divider;
     18.2 then grows every card in the deck with it, so the pitch holds.
     The -4px bottom margin lets the value use half the row's bottom
     padding, so three lines (5 + 60 - 4 = 61 of 64) still fit the 80px
     row exactly as the handoff intends, and only a fourth line grows it. */
  .top-cards .record :is(.email-attributes, .email-attributes-staging)
    .line:not(:has(> .count_date)) > p {
    grid-column: 1;
    grid-row: 1 / span 2;
    align-self: start;
    max-width: none;
    margin: 5px 0 -4px;                            /* here: was 5px 0 0 */
  }

  .top-cards .record :is(.email-attributes, .email-attributes-staging)
    .line:not(:has(> .count_date)) > select {
    grid-column: 2;
    grid-row: 1;
    width: 100%;
  }

  .top-cards .record :is(.email-attributes, .email-attributes-staging)
    .line:not(:has(> .count_date)) > .options-wrapper {
    grid-column: 2;
    grid-row: 2;
    width: 100%;
    margin: 0;                                     /* here: name_selection_styles.css:597 gives it 8px on top at <=768, which hung the tag select 8px below its track and grew the row to 89px */
  }

  .top-cards .record :is(.email-attributes, .email-attributes-staging)
    .line:not(:has(> .count_date)) > .options-wrapper select {
    width: 100%;
  }

  /* --- Phone: value | type | tag, one row ----------------------------------
     Already one row. Made explicit as a grid so the two select columns are
     the same x on every card whatever the value's length. */
  .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
    .line:not(:has(> .count_date)) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 7.5rem 7.5rem;
    column-gap: var(--sm-space-3);
    align-items: center;
  }

  .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
    .line:not(:has(> .count_date)) > .options-wrapper {
    margin: 0;                                     /* here: as Email; at <=768 the 8px put the tag select 4px below the type select and made the row 47px */
  }

  .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
    .line:not(:has(> .count_date)) > .options-wrapper select {
    width: 100%;
  }

  /* --- Count / Date, both screens -------------------------------------------
     Label left; the count chip, the slash and the date select end-aligned,
     the date select 7.5rem so its edges are the select column's edges. It
     used to start at the 32% label column, which is not an x anything else
     on an email or phone card uses. */
  .top-cards .record :is(.email-attributes, .phone-attributes) .line:has(> .count_date) {
    display: flex;
    justify-content: space-between;
  }

  .top-cards .record :is(.email-attributes, .phone-attributes) .line:has(> .count_date) > p:first-child {
    flex: 1 1 auto;
  }

  .top-cards .record :is(.email-attributes, .phone-attributes) .line > .count_date {
    flex: 0 0 auto;
    justify-content: flex-end;
    color: var(--sm-ink-quiet);                    /* the " / " text node */
  }

  .top-cards .record :is(.email-attributes, .phone-attributes) .line > .count_date select.date-selection {
    flex: 0 0 7.5rem;
    width: 7.5rem;
    font-variant-numeric: tabular-nums;
  }
}

/* --- Narrow side-by-side columns (1024+, row under 1000 / 800px) ----------- */
@media (min-width: 1024px) {
  /* Phone takes the Email form: value | type over tag. Row 81px.
     here: under a 1130px row, not 1000. The number column is row / 2 - 376,
     so a 1130 row leaves 189px, the width of "+1 (210) 555-0147 ext. 2214";
     at the handoff's 1000 it is 124px and an extension number wraps. */
  @container sm-sel (max-width: 1129px) {
    .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
      .line:not(:has(> .count_date)) {
      grid-template-columns: minmax(0, 1fr) 7.5rem;
      grid-template-rows: 30px minmax(30px, auto);   /* here: as Email */
      row-gap: var(--sm-space-1);
      align-items: start;
      align-content: start;
      padding-block: var(--sm-space-2);
    }

    .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
      .line:not(:has(> .count_date)) > p {
      grid-column: 1;
      grid-row: 1 / span 2;
      margin: 5px 0 -4px;                          /* here: as Email */
    }

    .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
      .line:not(:has(> .count_date)) > select {
      grid-column: 2;
      grid-row: 1;
      width: 100%;
    }

    .top-cards .record :is(.phone-attributes, .phone-attributes-staging)
      .line:not(:has(> .count_date)) > .options-wrapper {
      grid-column: 2;
      grid-row: 2;
      width: 100%;
    }
  }

  /* Email gets a third 20px track: 8 + 30 + 4 + 30 + 4 + 20 + 8 + 1 = 105px,
     room for four lines of value (5 + 80 = 85 of 88). */
  @container sm-sel (max-width: 799px) {
    .top-cards .record :is(.email-attributes, .email-attributes-staging)
      .line:not(:has(> .count_date)) {
      grid-template-rows: 30px 30px minmax(20px, auto);   /* here: was 20px */
    }

    .top-cards .record :is(.email-attributes, .email-attributes-staging)
      .line:not(:has(> .count_date)) > p {
      grid-row: 1 / span 3;
    }
  }
}

/* ==========================================================================
   The artifact deck (items 16, 17)
   The deck script sets .right-cards height and max-height, and the viewport
   height, inline from the LEFT column's height. It pages by the distance
   between the first two cards' tops, so every card in a deck must be the
   same height. Nothing below sets height, max-height or transform.
   ========================================================================== */

/* One card's height, per screen. Structural now: heading band 42 + the
   attributes' 4 + 8 padding + the rows + the 2px border.
     email  2 + 42 + 12 + 81 (value row) + 39 (Count / Date) = 176
            200 in a row under 800px (105px value row, above)
     phone  2 + 42 + 12 + 39 (value row) + 39 (Count / Date) = 134
            176 in a row under 1130px at 1024+ (Email form, above)
   here: each row is its 38px minimum plus 7.3's 1px divider, which the
   last row keeps (transparent), so Count / Date is 39, not 38. These are
   floors: a value longer than its row grows the card (above) and 18.2
   grows the rest of the deck to match.
   If a row height above changes, change these. The narrow values must come
   AFTER the two base lines: same specificity, so source order decides. */
.right-cards:has(.email-artifact-record) { --sm-deck-card: 176px; }   /* here: was 175 */
.right-cards:has(.phone-artifact-record) { --sm-deck-card: 134px; }   /* here: was 132 */

@media (min-width: 1024px) {
  @container sm-sel (max-width: 1129px) {
    .right-cards:has(.phone-artifact-record) { --sm-deck-card: 176px; }   /* here: was 175, under 1000 */
  }
  @container sm-sel (max-width: 799px) {
    .right-cards:has(.email-artifact-record) { --sm-deck-card: 200px; }   /* here: was 199 */
  }
}

/* The deck can never be shorter than one card. min-height beats both the
   inline height and max-height, so this holds against the script. It fixes
   Phone with two staging numbers, where the left column is shorter than one
   artifact card and the deck showed a clipped sliver. The viewport rule is
   the one that matters: the script measures viewport.clientHeight to count
   visible cards, and clientHeight honours min-height. The extra 32px is the
   bottom fade (below), so the one whole card is never under it. 112 = the
   pagers' real space: 2 x (12 + 32 + 12). here: the script reserves 104
   (32 + 32 + 40), so the viewport renders 8px under the inline height it
   is given; at 104 the down pager would hang 8px out of the deck. */
.right-cards:is(:has(.email-artifact-record), :has(.phone-artifact-record)) {
  min-height: calc(var(--sm-deck-card) + 32px + 112px);   /* here: was 104px */
}

/* here: stacked, name_selection_styles.css:574 gives the deck 1.5rem of
   block padding inside its border box, so the minimum carries it too; at
   the handoff's value the down pager hung 12px below the deck at 900. */
@media (max-width: 1023px) {
  .right-cards:is(:has(.email-artifact-record), :has(.phone-artifact-record)) {
    min-height: calc(var(--sm-deck-card) + 32px + 112px + 3rem);
  }
}

.right-cards:is(:has(.email-artifact-record), :has(.phone-artifact-record)) .cards-viewport {
  min-height: calc(var(--sm-deck-card) + 32px);
}

/* Even spacing between cards. The script's step is second-top minus
   first-top, so a gap is included in the step automatically. */
.right-cards:is(:has(.email-artifact-record), :has(.phone-artifact-record)) .cards-container {
  gap: var(--sm-space-4);
  counter-reset: sm-artifact;
}

.right-cards:is(:has(.email-artifact-record), :has(.phone-artifact-record)) .cards-container > .record {
  margin: 0;
  counter-increment: sm-artifact;
}

/* The card's position in the deck, right-aligned in its band: ARTIFACT
   RECORD ........ 3. Tells the steward where they are while paging. */
.right-cards .cards-container > .record > p.heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sm-space-3);
}

.right-cards:is(:has(.email-artifact-record), :has(.phone-artifact-record)) .cards-container > .record > p.heading::after {
  content: counter(sm-artifact);
  color: var(--arb-ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

/* The viewport height is the left column's height minus the buttons, which
   is rarely a whole number of cards, so a partial card shows at the bottom.
   While there is more below (the script clears .disabled on the down
   button), that partial card fades out over 32px, so it reads as "more"
   rather than as a card cut in half. */
.right-cards:has(.scroll-btn.down:not(.disabled)) .cards-viewport {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 32px), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 32px), transparent);
}

/* --- Native selects, as far as CSS goes -----------------------------------
   appearance: none replaces the OS arrow, which renders differently on
   Windows, macOS and each browser, with the sprite's chevron-down, so every
   select in a card is the same 30px box with the same arrow at the same x.
   The open list stays native: it cannot be styled, it renders in the OS
   font, and it ignores the card's width. Nothing here tries to change that.
   The chevron is a background, not the sprite, because a select cannot hold
   a <use>. It is drawn from the same Lucide path at stroke 2.25 (16px). */
.top-cards .record select {
  -webkit-appearance: none;
          appearance: none;
  padding-right: 28px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234b5563' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 14px 14px;
  text-overflow: ellipsis;
}

.top-cards .record select:disabled,
.top-cards .record select.disabled-dropdown {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* A select whose value differs from its data-original-* is a pending edit.
   The page scripts track changes in sessionStorage; they do not mark the
   select, so this is the one JS line the design asks for (handoff §2.5).
   Brand tint, not a field hue: this is state, not field identity. */
.top-cards .record select.is-changed {
  border-color: var(--sm-info-border);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
}

/* ==========================================================================
   19. REVIEW                                 WIZARD-HANDOFF.md rev 2 · §4, §5
   --------------------------------------------------------------------------
   silk-matrix-design/wizard/review.css below its own headers, with the
   changes marked "here:". After 7.7 (the compiled record and the alternate
   cards), 8.2 (the scrim), 8.6 (the empty state), 11.6 (Review's gutters
   below 1024) and 13.8 (the panel's surfaces): same selectors, so source
   order is what lets these win.
   ========================================================================== */

/* --------------------------------------------------------------------------
   19.1 Review: layout                                           item 18, §4
   Before, at 1440: .left-part { margin-left: 20%; width: 60% } and the card
   inside it at 90% with a 4rem left margin put the record at x = 438 (the
   handoff says 368: it leaves out the 108px inset the 20% sits after),
   719px wide, in the middle of the page. .right-part (20%) pushed the
   actions to its bottom edge with justify-content: flex-end. At 1024 x 768
   "View Relationships" wrapped to two lines in the 183px column and put
   Back / Done below the fold.

   After, from 1024 up: the record starts at the page inset like every other
   wizard step and takes the width up to 1100px (1036 at 1440, 620 at 1024).
   The actions stay where production has them, bottom right, in a 240px card
   on the page's right edge, but sticky to the bottom of the viewport: when
   the record ends above the fold the card sits level with the record's
   bottom edge; when it runs past the fold the card pins 24px above the
   viewport's bottom and rides there until the record's end scrolls into
   view. No script. Below 1024 the one-column layout in 7.7 stands.

   Session 3 kept the 20% margin because it was the only thing keeping the
   record off the sidebar; .main-content's padding-left is the inset now
   (section 3), so the margin can go. The compiled-record card itself is not
   restyled: it is shared with the Relationship pages (7.7).

   Trap: nothing between .right-part and the viewport may clip or scroll
   (overflow other than visible), or sticky stops working.
   -------------------------------------------------------------------------- */

@media (min-width: 1024px) {
  .overall-page {
    padding-right: var(--sm-space-6);              /* Review's .main-content has no right padding */
  }

  .overall-page .wrapper {
    display: grid;
    grid-template-columns: minmax(0, 1100px) 240px;
    justify-content: space-between;                /* the rail on the page's right edge at every width */
    column-gap: var(--sm-space-5);
    align-items: start;
  }

  .overall-page .left-part {
    width: auto;
    margin: 0 0 var(--sm-space-6);                 /* was margin-left: 20%, width: 60%, margin-bottom: 2% */
  }

  .overall-page .left-container-relationship {
    box-sizing: border-box;
    width: auto;                                   /* was 90% */
    margin: var(--sm-space-6) 0 0;                 /* was 4rem top, 4rem left */
  }

  /* align-self: end puts the rail at the bottom of the record's grid row;
     its margin-bottom matches .left-part's, so at rest its bottom edge is
     the card's bottom edge. A sticky grid item is constrained to its grid
     area, the full height of the record, so it can travel the whole way. */
  .overall-page .right-part {
    align-self: end;
    position: sticky;
    bottom: var(--sm-space-5);
    box-sizing: border-box;
    width: auto;                                   /* was 20% */
    margin: 0 0 var(--sm-space-6);
    padding: 20px;
    justify-content: flex-start;                   /* was flex-end */
    align-items: stretch;
    gap: var(--sm-space-3);
    background-color: var(--sm-surface);
    border: 1px solid var(--sm-line);
    border-radius: var(--sm-radius-card);
    box-shadow: var(--sm-e2);                      /* reads as lifted when pinned */
  }
}

/* The rail's contents, at every width (below 1024 the rail is the full-width
   row under the record). */
.overall-page .relationship-div {
  gap: var(--sm-space-2);
  padding: 0;                                      /* was padding-inline: 10% */
  align-items: stretch;
}

/* Alternates and View Relationships open panels; Done commits. So the two
   panel buttons are the outlined secondary and Done is the one filled
   button. Before, all three were filled. */
.overall-page .relationship-div :is(.view-alt-btn, .view-relationship) {
  width: 100%;                                     /* was 82% */
  min-height: 38px;
  margin: 0;                                       /* here: .view-alt-btn's 10px margin-bottom on top of the 8px gap */
  gap: var(--sm-space-2);
  background: var(--sm-surface);
  border: 1px solid var(--sm-brand);
  color: var(--sm-brand-strong);
}

.overall-page .relationship-div :is(.view-alt-btn, .view-relationship):hover {
  background: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

.overall-page .relationship-div :is(.view-alt-btn, .view-relationship):active {
  background: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
}

.overall-page .bottom-buttons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sm-space-2);
  padding: var(--sm-space-3) 0 0;                  /* was 0.65rem top, 2rem bottom */
  border-top: 1px solid var(--sm-line);
}

.overall-page .bottom-buttons form {
  display: flex;
}

.overall-page .bottom-buttons :is(a.back, .next) {
  width: 100%;                                     /* was 94px */
  height: 38px;                                    /* was 33px */
  border-width: 1px;
  border-radius: var(--sm-radius-control);
  font-size: var(--sm-type-body-size);
  font-weight: var(--sm-weight-medium);
}

/* here: Back is one of the rail's three outlined buttons (§4.3), so it takes
   their pressed state; 5.2 stops at hover for .bottom-buttons a.back. */
.overall-page .bottom-buttons a.back:active {
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
}

/* --------------------------------------------------------------------------
   19.2 Review: the Alternates and Relationships panels          item 19, §5
   Template changes these rules depend on (review_page.html): #relContent's
   malformed tag fixed, #relationshipScroller's inline style removed,
   data-count on the category buttons, the category buttons moved above the
   list in the DOM (here: so keyboard order is visual order, and no CSS
   `order` is needed), the relationship empty state rendered directly in the
   scroller (here: it sat three wrappers deep, where :has(> .alt-empty)
   cannot see it), the dead scroll arrows deleted (here: no script ever
   bound them; DEFERRED-FIXES #76).
   -------------------------------------------------------------------------- */

.relationship-overlay {
  background-color: var(--sm-scrim);               /* was rgba(90,90,90,.4) */
  -webkit-backdrop-filter: none;
          backdrop-filter: none;                   /* was blur(4px); HANDOFF 4.11 */
}

.relationship-panel {
  background-color: var(--sm-surface-page);
  box-shadow: var(--sm-e3);
}

/* Header: title left, close right, one 64px row. Before, the close sat alone
   top-left and the title was centred on its own row under it. The header
   floats over the title's row; the title reserves room for it. */
.relationship-panel .panel-header {
  position: absolute;
  top: var(--sm-space-4);
  right: var(--sm-space-5);
  left: auto;
  padding: 0;
  background: none;
}

.relationship-panel .panel-title {
  margin: 0;
  padding: 18px 72px var(--sm-space-4) var(--sm-space-5);   /* 18 + 28 + 16 = 62; the close is centred on the title's line */
  text-align: left;
  color: var(--sm-ink);
  font-size: var(--sm-type-title-size);
  line-height: var(--sm-type-title-leading);
  font-weight: var(--sm-type-title-weight);
}

/* The close: the date-range panel's icon button (17.2), so the product's
   dismiss controls are one control. Ghost at rest, no circle, no border,
   16px icon (template). Every property is set, so no earlier rule (5.x, or
   review_styles.css's grey disc) shows through. */
.close-panel-btn {
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--sm-radius-control);
  background: transparent;
  box-shadow: none;
  outline: none;
  color: var(--sm-ink-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transform: none;
}

.close-panel-btn:hover {
  background: var(--sm-surface-sunk);
  border-color: transparent;
  color: var(--sm-ink);
}

.close-panel-btn:active {
  background: var(--sm-line);
}

.close-panel-btn:focus-visible {
  outline: none;
  border-color: var(--sm-brand);
  box-shadow: var(--sm-focus);
}

/* Alternates: the category control is a segmented filter under the title.
   It filters one list, so it reads as a filter, not as four actions. */
#altContent > .alt-type-btns {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sm-space-1);
  margin: 0 var(--sm-space-5) var(--sm-space-4);
  padding: var(--sm-space-1);
  border: 0;
  border-radius: var(--sm-radius-control);
  background-color: var(--sm-surface-sunk);
}

#altContent .alt-type-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
  min-height: 32px;
  padding: 0 var(--sm-space-2);
  border: 1px solid transparent;
  border-radius: 6px;                              /* control radius minus the 4px inset, so the corners nest */
  background: transparent;
  box-shadow: none;
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-medium);
}

#altContent .alt-type-btn:hover {
  background: var(--sm-surface);
  border-color: transparent;
  color: var(--sm-ink);
}

#altContent .alt-type-btn.active,
#altContent .alt-type-btn.active:hover {
  background: var(--sm-surface);
  border-color: var(--sm-line);
  box-shadow: var(--sm-e1);
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
}

#altContent .alt-type-btn:focus-visible {
  outline: none;
  border-color: var(--sm-brand);
  box-shadow: var(--sm-focus);
}

/* The count, from data-count. A pseudo-element, not a child span:
   switchAltType() matches buttons on textContent.
   here: 13px (small), not the handoff's 11px micro. A count is read to
   decide which category to open, and HANDOFF 2.9 puts nothing a steward
   reads below 13px. */
#altContent .alt-type-btn[data-count]::after {
  content: attr(data-count);
  min-width: 20px;
  padding: 0 6px;
  box-sizing: border-box;
  border-radius: var(--sm-radius-pill);
  background: var(--sm-surface-page);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

#altContent .alt-type-btn.active[data-count]::after {
  background: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
}

#altContent .alt-type-btn[data-count="0"]:not(.active) {
  color: var(--sm-ink-quiet);
}

/* The list: flush with the 24px panel gutter. */
#altContent .alt-cards-scroll {
  padding: 0 var(--sm-space-5) var(--sm-space-5);
  gap: var(--sm-space-3);
}

#altContent .alt-section {
  gap: var(--sm-space-3);
}

#altContent .alt-address-card {
  margin: 0;                                       /* was 0 20px, on top of the scroller's own 20px */
  background: var(--sm-surface);
}

#altContent .alt-address-row {
  grid-template-columns: 7rem minmax(0, 1fr);      /* was 1fr 1.5fr: labels in one fixed column */
}

/* Empty state: the item-13 pattern. A 48px sunk disc with the 24px inbox,
   a 16/600 line, centred in the list area. Replaces 8.6's dashed frame at
   the top of an otherwise blank panel.
   here: the disc is ::before and the icon is ::after, masked and painted in
   currentColor like every other icon (HANDOFF 2.12). The handoff drew the
   icon as a background image with #6b7280 baked into its data URI. */
#altContent .alt-section:has(> .alt-empty),
#relContent #relationshipScroller:has(> .alt-empty) {
  flex: 1;
  justify-content: center;
}

.relationship-panel .alt-empty {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sm-space-3);
  padding: var(--sm-space-7) var(--sm-space-5);
  border: 0;
  color: var(--sm-ink);
  text-align: center;
  font-size: var(--sm-type-subtitle-size);
  line-height: var(--sm-type-subtitle-leading);
  font-weight: var(--sm-type-subtitle-weight);
}

.relationship-panel .alt-empty::before {
  content: "";
  flex: none;
  width: 48px;
  height: 48px;
  margin: 0;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-surface-sunk);
  -webkit-mask: none;
          mask: none;
}

.relationship-panel .alt-empty::after {
  content: "";
  position: absolute;
  top: calc(var(--sm-space-7) + 12px);
  left: calc(50% - 12px);
  width: 24px;
  height: 24px;
  color: var(--sm-ink-quiet);
  background-color: currentColor;
  -webkit-mask: var(--sm-icon-empty) center / contain no-repeat;
          mask: var(--sm-icon-empty) center / contain no-repeat;
}

/* Relationships: a vertical list, like Alternates. Before: one card per
   100%-wide page of a horizontal snap scroller, 110px of top padding, a
   green glow. Three relationships took three pages to read. */
#relContent #relationshipScroller {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-3);
  overflow-x: hidden;
  overflow-y: auto;
  scroll-snap-type: none;
  padding: 0 var(--sm-space-5) var(--sm-space-5);
}

#relContent .relationship-card {
  flex: none;
  height: auto;
  padding: 0;
  overflow: visible;
  scroll-snap-align: none;
}

#relContent .card-content-wrapper {
  padding: 0;
  overflow: hidden;
  background: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e1);
}

/* The relationship kind ("Family", "Company") as the card's band. Neutral:
   a relationship is not one of the four field types (HANDOFF 2.3). */
#relContent .record-section .record-subtitle {
  margin: 0;
  padding: var(--sm-space-3) var(--sm-space-4);
  background: var(--sm-surface-sunk);
  color: var(--sm-ink-muted);
  text-align: left;
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: var(--sm-type-micro-transform);
}

#relContent .record-grid {
  grid-template-columns: 7rem minmax(0, 1fr);
  gap: 0 var(--sm-space-3);
  padding: var(--sm-space-1) var(--sm-space-4) var(--sm-space-2);
}

#relContent :is(.label-box, .value-box) {
  min-height: 38px;
  box-sizing: border-box;
  padding: var(--sm-space-2) 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--sm-line);
  border-radius: 0;
}

#relContent .record-grid > :nth-last-child(-n + 2) {
  border-bottom-color: transparent;
}

#relContent .label-box {
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-small-weight);
}

#relContent .value-box {
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  overflow-wrap: anywhere;
  min-width: 0;
}

/* ==========================================================================
   20. THE LIST PATTERN            LISTS-HANDOFF.md §1 · item 27, #79, #61, #56
   --------------------------------------------------------------------------
   silk-matrix-design/lists/list-pattern.css, applied to the two list pages
   only: every selector the export shared with .details-table-panel is cut
   back to the two list containers, because the detail pages are their own
   session. "here:" marks each departure from the export.

   Both pages, top to bottom:
     1  Header      title + total           .customer-list-header /
                                            .relationship-page-header
     2  Toolbar     search first, filters, Show: N pushed right
     3  Chips       applied filters, each removable + Clear all (#79)
     4  Views       Company / Family / Cohab (Relationship list only)
     5  Table card  Actions pinned right from 769 (#61)
     6  Footer      "Showing a–b of N" + the shared pagination

   here: the handoff's §1.1 table puts Views second, above the toolbar; its
   own CSS says "directly above the table". The markup already has the tabs
   inside the panel, above the table, and that is what is kept.

   No load-bearing class is renamed, no data-column-index moves, and the
   tbody.relationship-group-card structure is the same. After 6.x, 10.x and
   16.x: same selectors or one more, so source order decides.
   ========================================================================== */

/* --- 20.1 Header ------------------------------------------------------------ */
.customer-list-header,
.relationship-page-header {
  display: flex;
  flex-direction: row;          /* here: both originals switch to column at <= 1024 */
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  column-gap: var(--sm-space-3);
  row-gap: var(--sm-space-4);
  margin-bottom: var(--sm-space-4);
}

/* The title takes its own row, so the toolbar is always the row under it. */
:is(.customer-list-header, .relationship-page-header) > .sm-list-title {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--sm-space-3);
}

.sm-list-title .page-main-title {
  margin: 0;
}

/* The total when nothing is filtered. Under a filter the views only know the
   filtered count, and the chip row carries it (DEFERRED-FIXES #89). */
.sm-list-count {
  color: var(--sm-ink-quiet);
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  font-variant-numeric: tabular-nums;
}

/* --- 20.2 Toolbar ------------------------------------------------------------
   Search first and widest; the filters beside it; Show: N last and pushed to
   the right edge, because it is page size, not a filter. Show: N stays in
   #filterForm (its radios submit with it); only its order moves. */
.customer-list-header #filterForm,
.relationship-page-header .header-actions-flex-wrapper {
  flex: 1 0 100%;
}

.filter-action-bar,
.relationship-page-header .header-actions-flex-wrapper {
  display: flex;
  flex-direction: row;          /* here: both originals switch to column at <= 1024 */
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sm-space-2);
}

.filter-action-bar .unified-filter-group {
  display: contents;            /* its dropdowns join the toolbar row */
}

:is(.filter-action-bar, .header-actions-flex-wrapper) .search-wrapper {
  order: -1;                    /* first on both pages; second in the Relationship list's DOM */
  flex: 1 1 280px;
  max-width: 420px;
  margin: 0;
}

:is(.filter-action-bar, .header-actions-flex-wrapper) #limitDropdown {
  order: 99;
  margin-left: auto;
}

/* here: the Date popover is right-anchored to its trigger (§17), 320px wide.
   When the toolbar wrapped, Date could start the second row at the inset,
   which put the popover at x = -85 at 769-800. So when the header is too
   narrow for one row (~1000px with the longest applied labels), the search
   takes the first row and the four dropdowns share the second, which needs
   ~700px: Date is never first in a row above 768. */
.customer-list-header {
  container-type: inline-size;
}

@container (max-width: 1000px) {
  .filter-action-bar .search-wrapper {
    flex-basis: 100%;
    max-width: none;
  }
}

/* here: at phone width the search takes its row, as the dropdowns already
   do (6.9). */
@media (max-width: 768px) {
  :is(.filter-action-bar, .header-actions-flex-wrapper) .search-wrapper {
    flex-basis: 100%;
    max-width: none;
  }
}

/* --- 20.3 Applied-filter chips (#79) -----------------------------------------
   Server-rendered (partials/filter_chips.html). Each chip is a link to the
   same URL without its parameter and without page, so removing one is an
   ordinary navigation: no script, and the loader shows through
   data-buffer-show. The pills keep showing their own selection (16.3); the
   chip carries the same words. */
.sm-filter-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sm-space-2);
  margin: 0 0 var(--sm-space-4);
}

.sm-filter-chips__result {
  margin-right: var(--sm-space-1);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.sm-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  max-width: 320px;
  box-sizing: border-box;
  padding: 0 6px 0 10px;
  border: 1px solid var(--sm-info-border);
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  text-decoration: none;
  white-space: nowrap;
}

.sm-chip__key { color: var(--sm-ink-muted); }

.sm-chip__value {
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: var(--sm-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.sm-chip .sm-icon {
  flex: none;
  width: 16px;
  height: 16px;
  padding: 1px;
  box-sizing: border-box;
  border-radius: var(--sm-radius-pill);
}

.sm-chip:hover { border-color: var(--sm-brand); color: var(--sm-brand-deep); }
.sm-chip:hover .sm-icon { background-color: var(--sm-surface); }
.sm-chip:active { background-color: var(--sm-info-border); }
.sm-chip:focus-visible { outline: none; border-color: var(--sm-brand); box-shadow: var(--sm-focus); }

.sm-filter-chips__clear {
  height: 28px;
  display: inline-flex;
  align-items: center;
  padding: 0 var(--sm-space-2);
  border-radius: var(--sm-radius-control);
  color: var(--sm-brand-strong);
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-medium);
  text-decoration: none;
}
.sm-filter-chips__clear:hover { background-color: var(--sm-brand-wash); color: var(--sm-brand-strong); }
.sm-filter-chips__clear:focus-visible { outline: none; box-shadow: var(--sm-focus); }

/* --- 20.4 Views (Relationship list) --------------------------------------------
   The white panel that wrapped the tabs and the table made a card inside a
   card once the table became one. It is a plain wrapper at every width. */
.relationship-content-panel {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

.relationship-tabs-bar {
  display: flex;
  gap: var(--sm-space-5);
  margin: 0 0 var(--sm-space-4);
  padding: 0;
  border-bottom: 1px solid var(--sm-line);
  background: none;
}

.relationship-tabs-bar .relation-tab-btn {
  position: relative;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--sm-ink-muted);
  font: inherit;
  font-size: var(--sm-type-body-size);
  font-weight: var(--sm-weight-medium);
  cursor: pointer;
}

.relationship-tabs-bar .relation-tab-btn:hover { color: var(--sm-ink); }

.relationship-tabs-bar .relation-tab-btn.active-tab {
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
  background: none;
  box-shadow: inset 0 -2px 0 var(--sm-brand-strong);
}

/* here: the underline is the box-shadow above; relationship_list_styles.css
   :42 (and 5.11) still draw a 3px ::after 6px lower. */
.relationship-tabs-bar .relation-tab-btn.active-tab::after {
  content: none;
}

.relationship-tabs-bar .relation-tab-btn:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
  border-radius: var(--sm-radius-inline);
}

/* --- 20.5 The table card ------------------------------------------------------ */
.customers-table-container,
.master-relationship-table-container {
  background-color: var(--sm-surface);
}

.customers-table-container {
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e1);
}

/* here: the export gave the Relationship list the same card at every width.
   At <= 768 each group is already its own card (10.2), so the outer card is
   from 769 up, where the groups become row groups of ONE table: collapsed
   borders instead of the 12px border-spacing, and no per-group ring. The
   tbodies and their classes are unchanged; only their paint is. */
.master-relationship-table-container {
  background-color: transparent;
}

@media (min-width: 769px) {
  .master-relationship-table-container {
    border: 1px solid var(--sm-line);
    border-radius: var(--sm-radius-card);
    box-shadow: var(--sm-e1);
    padding-block: 0;
    background-color: var(--sm-surface);
  }

  .master-relationship-table-container table {
    border-collapse: collapse;
    border-spacing: 0;
  }

  .master-relationship-table-container .relationship-group-card {
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }

  .master-relationship-table-container table thead th:first-child,
  .master-relationship-table-container table thead th:last-child {
    border-radius: 0;           /* the card clips the corners */
  }

  .master-relationship-table-container .relationship-group-card td {
    border-bottom: 1px solid var(--sm-line);
  }
}

/* Long unbroken strings (emails have no spaces) set the column's min-content
   to the whole address, about 400px at 14px. That, not a min-width, is why
   the customer list scrolled sideways at 769-1023 (#56). overflow-wrap:
   anywhere lets the browser break one only when the column cannot fit it; at
   1440 nothing wraps. Addressed by data-label, which every cell has, so no
   positional selector is involved. */
:is(.customers-table-container, .master-relationship-table-container)
  td:is([data-label="Email"], [data-label="Email Address"], [data-label="Value"], [data-label="Address"]) {
  overflow-wrap: anywhere;
  min-width: 12ch;
}

:is(.customers-table-container, .master-relationship-table-container)
  td:is([data-label="Phone No"], [data-label="Phone Number"], [data-label="Phone"], [data-label="Date"], [data-label="Count"], [data-label="Type"], [data-label="Relationship Type"]) {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Missing values. The customer view prints "—" for an empty name, phone or
   email; the template wraps it:
   <span class="sm-missing">—<span class="sr-only">Not on record</span></span> */
.sm-missing {
  color: var(--sm-ink-faint);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* --- 20.6 Actions pinned right (#61) -------------------------------------------
   The last column stays on screen whenever the table scrolls. Sticky cells
   need their own background, or scrolled cells show through. The 1px left
   rule is always drawn, so the pinned edge reads the same scrolled or not.
   From 769 up: at 768 and below the rows are cards (10.2). */
@media (min-width: 769px) {
  :is(.customers-table-container, .master-relationship-table-container)
    tbody td:last-child:has(> .action-external-trigger),
  :is(.customers-table-container, .master-relationship-table-container)
    thead th:last-child {
    position: sticky;
    right: 0;
    z-index: 1;
    width: 1%;                  /* shrink to the icon button */
    padding-top: 0;             /* the 32px button must not set the row height */
    padding-bottom: 0;
    white-space: nowrap;
    text-align: center;
    box-shadow: inset 1px 0 0 var(--sm-line);
  }

  /* here: the header keeps its own vertical padding; only the body cells
     hold the 32px button. */
  :is(.customers-table-container, .master-relationship-table-container)
    thead th:last-child {
    padding-top: var(--sm-space-3);
    padding-bottom: var(--sm-space-3);
    background-color: var(--sm-brand-tint);   /* the header row's fill (6.3) */
  }

  :is(.customers-table-container, .master-relationship-table-container)
    tbody td:last-child:has(> .action-external-trigger) {
    background-color: var(--sm-surface);
  }

  :is(.customers-table-container, .master-relationship-table-container)
    tbody tr:hover td:last-child:has(> .action-external-trigger) {
    background-color: var(--sm-surface-sunk);
  }
}

/* The row action: a 32px ghost icon button, 44px hit area kept by the cell.
   here: scoped to the two lists; relationship detail keeps 5.x's. */
:is(.customers-table-container, .master-relationship-table-container) .action-external-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--sm-radius-control);
  color: var(--sm-ink-muted);
}
:is(.customers-table-container, .master-relationship-table-container) .action-external-trigger:hover {
  background-color: var(--sm-surface);
  color: var(--sm-brand-strong);
  box-shadow: inset 0 0 0 1px var(--sm-line);
}
:is(.customers-table-container, .master-relationship-table-container) .action-external-trigger:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

/* --- 20.7 Relationship list: groups ----------------------------------------------
   The tbody.relationship-group-card grouping, the .parent-row / .child-row
   levels and the .active-group-tab show/hide stay exactly as they are. */
@media (min-width: 769px) {
  .master-relationship-table-container .relationship-group-card + .relationship-group-card tr.parent-row td {
    border-top: 1px solid var(--sm-line-strong);   /* a firmer rule between groups than between members */
  }

  .master-relationship-table-container tr.parent-row td {
    color: var(--sm-ink);
  }

  /* here: the Type column starts at its left edge, as the customer list's
     does. Centred, the parent's badge + "N members" and the members' lone
     badges had different centres and did not line up. The Actions column
     (the last child) stays centred (20.6). */
  .master-relationship-table-container table thead th.center-column-items:not(:last-child),
  .master-relationship-table-container .relationship-group-card td.center-column-items:not(:last-child) {
    text-align: left;
  }

  /* The tree elbow: members hang off their group. 6.4 indents the member
     cell 44px with !important; the elbow sits in that indent. */
  .master-relationship-table-container tr.child-row td.child-name-muted {
    position: relative;
  }

  .master-relationship-table-container tr.child-row td.child-name-muted::before {
    content: "";
    position: absolute;
    left: 24px;
    top: 0;
    width: 10px;
    height: 50%;
    border-left: 1px solid var(--sm-line-strong);
    border-bottom: 1px solid var(--sm-line-strong);
    border-bottom-left-radius: 4px;
  }

  .master-relationship-table-container tr.child-row:not(:last-child) td.child-name-muted::after {
    content: "";
    position: absolute;
    left: 24px;
    top: 50%;
    bottom: 0;
    border-left: 1px solid var(--sm-line-strong);
  }
}

/* "N members", after the badge in the parent row's Type cell (not the Name
   cell: the sort engine reads column 0's text). */
.sm-group-count {
  margin-left: var(--sm-space-2);
  color: var(--sm-ink-quiet);
  font-size: var(--sm-type-small-size);
  font-variant-numeric: tabular-nums;
}

/* here: at <= 768 the Type cell is a label | value grid (10.2), so the count
   is its own grid item. Auto-placed it took a third row (parent cards 94 ->
   115px); it shares the badge's cell instead, at its far end. */
@media (max-width: 768px) {
  .sm-group-count {
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
    margin-left: 0;
  }

  /* Explicitly placed items go first, so the badge needs the same cell or
     auto-placement moves it to the next row. */
  .master-relationship-table-container .relationship-group-card td > .badge-pill {
    grid-column: 2;
    grid-row: 1;
  }
}

/* --- 20.8 Empty states -------------------------------------------------------------
   One block for both lists and both reasons, inside the {% empty %} row
   (partials/list_empty.html). It replaces 8.6's dashed frame on these two
   tables only: the row no longer carries .table-empty-notice-block or the
   inline #9ca3af style that 8.6 matched. */
:is(.customers-table-container, .master-relationship-table-container) .sm-list-empty-row td,
:is(.customers-table-container, .master-relationship-table-container) .sm-list-empty-row:hover td {
  padding: 0;
  background-color: var(--sm-surface);
}

.sm-list-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sm-space-3);
  padding: var(--sm-space-8) var(--sm-space-5);
  text-align: center;
}

.sm-list-empty__icon {
  width: 48px;
  height: 48px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-surface-sunk);
  color: var(--sm-ink-quiet);
  display: flex;
  align-items: center;
  justify-content: center;
}

.sm-list-empty__title {
  margin: 0;
  color: var(--sm-ink);
  font-size: var(--sm-type-subtitle-size);
  line-height: var(--sm-type-subtitle-leading);
  font-weight: var(--sm-type-subtitle-weight);
}

.sm-list-empty__body {
  margin: 0;
  max-width: 360px;
  color: var(--sm-ink-quiet);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
}

/* --- 20.9 Footer ---------------------------------------------------------------------- */
.customer-list-footer-pagination-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sm-space-3);
  margin-top: var(--sm-space-4);
}

.footer-entries-info-text {
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   21. THE DETAIL PATTERN          LISTS-HANDOFF.md §2 · items 25, 26, 27, #61
   --------------------------------------------------------------------------
   silk-matrix-design/lists/detail-pattern.css, applied to the two detail
   pages. Every selector is scoped under .sm-detail, the wrapper both detail
   templates now have, because the export styled .tab-nav-link,
   .tabs-header-action-row and .detail-page-header-row globally. "here:"
   marks each departure from the export, and why.

   Both pages, top to bottom:
     1  Back link    ← Customers / ← Relationships
     2  Header       record name as the title; meta line (type, count, ID)
     3  Facts        Customer detail: Name, Phone, Email, Address, each in
                     its field hue. Relationship detail: a neutral Members
                     tile, then Phone and Email (never for Family / Cohab; for
                     Company only when there is one) and Address.
     4  Attributes   one card. Customer detail: five tabs, and the card takes
                     the open field tab's hue. Relationship detail: Members.

   The profile card (avatar, meta grid, absolutely positioned ID tag) and the
   footer Back button are gone from both templates. Their rules in 6.6,
   6.10, 10.4 and 13.4 stay and now match nothing (DEFERRED-FIXES #95).

   The four field hues are reserved (HANDOFF.md 2.3). Here they appear only
   on the four tiles of the same name, the four field tabs, and the card
   while one of those tabs is open. The Relationship tab, the Members tile,
   the Members card and the members table's Phone / Email columns take none.
   ========================================================================== */

/* --- 21.1 Back link ---------------------------------------------------------- */
.sm-back {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: 6px;
  margin-bottom: var(--sm-space-2);
  padding: 4px 8px 4px 4px;
  border-radius: var(--sm-radius-control);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-medium);
  text-decoration: none;
}
.sm-back:hover { background-color: var(--sm-surface-sunk); color: var(--sm-ink); }
.sm-back:focus-visible { outline: none; box-shadow: var(--sm-focus); }

/* --- 21.2 Header ------------------------------------------------------------- */
.sm-detail .detail-page-header-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sm-space-4);
  margin: 0 0 var(--sm-space-5);
}

.sm-detail-title {
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-2);
  min-width: 0;
}

.sm-detail-title .page-main-title {
  margin: 0;
  overflow-wrap: anywhere;
}

.sm-detail-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--sm-space-3);
  row-gap: var(--sm-space-1);
  color: var(--sm-ink-quiet);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-variant-numeric: tabular-nums;
}

/* here: once the ID line is deleted (the client's plan), Customer detail's
   meta line has nothing left in it. It then takes no space, so removing the
   ID stays a one-line template change. */
.sm-detail-meta:not(:has(> *)) {
  display: none;
}

/* The ID, demoted from the card corner (customer_styles.css:467, position:
   absolute) to a meta item. Same classes, so 6.2's mono value still applies.
   10.4 and 14.3 restyle .card-id-tag at <= 768 (block / flex, a margin);
   this selector is heavier and cancels them. */
.sm-detail-meta .card-id-tag {
  position: static;
  inset: auto;
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--sm-ink-quiet);
  font-size: var(--sm-type-small-size);
  overflow-wrap: anywhere;
}

.sm-detail-meta .card-id-tag .row-identifier-value {
  font-family: var(--sm-type-mono-family);
  font-size: var(--sm-type-mono-size);
  color: var(--sm-ink-muted);
}

.sm-detail .detail-page-header-row .netsuite-external-btn {
  flex: none;
}

/* --- 21.3 Facts ----------------------------------------------------------------
   Four across where the content is 960px or wider, two by two below that. A
   container query on .sm-detail, so it follows the sidebar. */
.sm-detail {
  container: sm-detail / inline-size;
}

.sm-facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sm-space-3);
  margin: 0 0 var(--sm-space-5);
}

/* here: Relationship detail can have two or three tiles (Members and
   Address always; Phone and Email only where there is one to show, see
   relationship_detail.html). Two by two, a third tile takes the row. */
.sm-facts > :last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

@container sm-detail (min-width: 960px) {
  .sm-facts { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* here: in a four-track row, two or three tiles would leave empty tracks
     beside them, which reads as missing tiles. They share the row instead. */
  .sm-facts:not(:has(> :nth-child(3))) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sm-facts:has(> :nth-child(3)):not(:has(> :nth-child(4))) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .sm-facts > :last-child:nth-child(odd) { grid-column: auto; }
}

/* A tile is a button on Customer detail (it opens its tab; the template
   gives it data-jump-tab) and a <div> on Relationship detail. */
.sm-fact {
  --fld-tint: var(--sm-surface-sunk);
  --fld-mark: var(--sm-line-strong);
  --fld-ink:  var(--sm-ink-muted);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  min-width: 0;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--sm-line);
  border-left: 3px solid var(--fld-mark);
  border-radius: var(--sm-radius-card);
  background-color: var(--sm-surface);
  box-shadow: var(--sm-e1);
  color: inherit;
  font: inherit;
  text-align: left;
}

button.sm-fact { cursor: pointer; }

.sm-fact[data-field="name"]    { --fld-tint: var(--sm-field-name-tint);  --fld-mark: var(--sm-field-name-mark);  --fld-ink: var(--sm-field-name-ink); }
.sm-fact[data-field="phone"]   { --fld-tint: var(--sm-field-phone-tint); --fld-mark: var(--sm-field-phone-mark); --fld-ink: var(--sm-field-phone-ink); }
.sm-fact[data-field="email"]   { --fld-tint: var(--sm-field-email-tint); --fld-mark: var(--sm-field-email-mark); --fld-ink: var(--sm-field-email-ink); }
.sm-fact[data-field="address"] { --fld-tint: var(--sm-field-addr-tint);  --fld-mark: var(--sm-field-addr-mark);  --fld-ink: var(--sm-field-addr-ink); }

.sm-fact__label {
  display: flex;
  justify-content: space-between;
  gap: var(--sm-space-2);
  padding: 8px 12px;
  background-color: var(--fld-tint);
  color: var(--fld-ink);
  font-size: var(--sm-type-micro-size);
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: uppercase;
}

.sm-fact__count {
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.sm-fact__value {
  padding: 10px 12px 12px;
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  overflow-wrap: anywhere;
}

.sm-fact__sub {
  display: block;
  margin-top: 2px;
  color: var(--sm-ink-quiet);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-type-small-weight);
}

.sm-fact__value .sm-missing-text {
  color: var(--sm-ink-quiet);
  font-weight: var(--sm-type-body-weight);
}

/* here: hover and focus only where the tile is a control. A <div> tile on
   Relationship detail opens nothing, so it does not answer the pointer. */
button.sm-fact:hover { border-color: var(--sm-line-strong); border-left-color: var(--fld-mark); }
button.sm-fact:focus-visible { outline: none; box-shadow: var(--sm-focus); }

/* The tile of the open tab is marked, from the tab state alone (trap 5). */
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="name"])    .sm-fact[data-field="name"],
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="phone"])   .sm-fact[data-field="phone"],
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="email"])   .sm-fact[data-field="email"],
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="address"]) .sm-fact[data-field="address"] {
  box-shadow: 0 0 0 1px var(--fld-mark), var(--sm-e1);
}

/* here: the four rules above are (0,6,0) and beat button.sm-fact:focus-visible,
   so the open tab's tile showed no ring when focused (session 23). */
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="name"])    .sm-fact[data-field="name"]:focus-visible,
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="phone"])   .sm-fact[data-field="phone"]:focus-visible,
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="email"])   .sm-fact[data-field="email"]:focus-visible,
.sm-detail:has(.tab-nav-link.active-tab[data-target-tab="address"]) .sm-fact[data-field="address"]:focus-visible {
  box-shadow: 0 0 0 1px var(--fld-mark), var(--sm-focus);
}

/* --- 21.4 Attributes: one card, tabs on top ------------------------------------- */
.sm-detail .tabs-navigation-panel-container {
  --fld-tint: var(--sm-brand-tint);
  --fld-mark: transparent;
  --fld-ink:  var(--sm-ink-muted);
  --fld-hl:   var(--sm-surface-sunk);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);
  background-color: var(--sm-surface);
  box-shadow: var(--sm-e1);
}

/* here: the export's overflow: hidden never applies. customer_styles.css:552
   sets overflow: visible !important on this card so the Kind menu and the
   date menus can open past its edge; without clipping, the square corners
   of the header row and the last row stick out of the 16px radius. Clip
   (no scroll container, so 21.7's sticky Actions still pins to the table's
   own scroller) whenever no menu is open; while one is, the card lets it out
   as before. !important only to meet the original's. */
.sm-detail .tabs-navigation-panel-container:not(:has(.menu-open)) {
  overflow: clip !important;
}

/* The card takes the active field's hue. The Relationship tab and the
   Members card keep the neutral defaults above. */
.sm-detail .tabs-navigation-panel-container:has(.tab-nav-link.active-tab[data-target-tab="name"]) {
  --fld-tint: var(--sm-field-name-tint);  --fld-mark: var(--sm-field-name-mark);  --fld-ink: var(--sm-field-name-ink);  --fld-hl: var(--sm-field-name-hl);
}
.sm-detail .tabs-navigation-panel-container:has(.tab-nav-link.active-tab[data-target-tab="phone"]) {
  --fld-tint: var(--sm-field-phone-tint); --fld-mark: var(--sm-field-phone-mark); --fld-ink: var(--sm-field-phone-ink); --fld-hl: var(--sm-field-phone-hl);
}
.sm-detail .tabs-navigation-panel-container:has(.tab-nav-link.active-tab[data-target-tab="email"]) {
  --fld-tint: var(--sm-field-email-tint); --fld-mark: var(--sm-field-email-mark); --fld-ink: var(--sm-field-email-ink); --fld-hl: var(--sm-field-email-hl);
}
.sm-detail .tabs-navigation-panel-container:has(.tab-nav-link.active-tab[data-target-tab="address"]) {
  --fld-tint: var(--sm-field-addr-tint);  --fld-mark: var(--sm-field-addr-mark);  --fld-ink: var(--sm-field-addr-ink);  --fld-hl: var(--sm-field-addr-hl);
}

/* 6.9 wraps this row with a 12px gap and 10.1 pads it 12px 16px at <= 768.
   The tabs are 48px tall on their own, so it is 0 16px at every width. */
.sm-detail .tabs-header-action-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  column-gap: var(--sm-space-4);
  row-gap: 0;
  padding: 0 var(--sm-space-4);
  border-bottom: 1px solid var(--sm-line);
  background-color: var(--sm-surface);
}

/* here: the export made the strip a hidden-scrollbar scroller. 6.9 already
   wraps it, which keeps every tab visible at every width, and a scroller
   would clip the tabs' focus ring. The wrap stays; the gap is the export's. */
.sm-detail .tabs-nav-list-wrapper {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--sm-space-1);
  min-width: 0;
}

.sm-detail .tab-nav-link {
  --tab-dot: var(--sm-ink-faint);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--sm-space-2);
  height: 48px;
  padding: 0 var(--sm-space-3);
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--sm-ink-muted);
  font: inherit;
  font-size: var(--sm-type-body-size);
  font-weight: var(--sm-weight-medium);
  white-space: nowrap;
  cursor: pointer;
}

.sm-detail .tab-nav-link[data-target-tab="name"]    { --tab-dot: var(--sm-field-name-mark); }
.sm-detail .tab-nav-link[data-target-tab="phone"]   { --tab-dot: var(--sm-field-phone-mark); }
.sm-detail .tab-nav-link[data-target-tab="email"]   { --tab-dot: var(--sm-field-email-mark); }
.sm-detail .tab-nav-link[data-target-tab="address"] { --tab-dot: var(--sm-field-addr-mark); }

.sm-detail .tab-nav-link::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--tab-dot);
}

/* here: the Relationship tab is not a field type. The export gave it an
   --sm-ink-faint dot, and a grey dot in a row of four coloured ones still
   reads as a fifth colour key. It has none. */
.sm-detail .tab-nav-link[data-target-tab="relationship"]::before {
  content: none;
}

.sm-detail .tab-nav-link:hover { color: var(--sm-ink); background-color: var(--sm-surface-page); }

.sm-detail .tab-nav-link.active-tab {
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
  background: none;
  box-shadow: inset 0 -3px 0 var(--tab-dot);
}

.sm-detail .tab-nav-link[data-target-tab="relationship"].active-tab { box-shadow: inset 0 -3px 0 var(--sm-ink-muted); }

/* here: customer_styles.css:587 still draws its own underline as ::after,
   21.6px below the tab (brand, 3px since 5.x). At 1440 the column headers
   paint over it; at <= 768 10.3 moves it to bottom: 0, on top of the hue
   underline. The underline is the box-shadow above, so the old one goes. */
.sm-detail .tab-nav-link.active-tab::after {
  content: none;
}

/* The Relationship selector is here because its .active-tab rule above is
   (0,4,0) and would otherwise hide the ring while that tab is open (session 23). */
.sm-detail .tab-nav-link:focus-visible,
.sm-detail .tab-nav-link[data-target-tab="relationship"]:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--sm-brand), var(--sm-focus); }

/* Template: <span class="sm-tab-count">4</span> inside each tab button, and
   in the Members card title. The tab script keys on data-target-tab. */
.sm-tab-count {
  min-width: 20px;
  box-sizing: border-box;
  padding: 0 6px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-surface-sunk);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-micro-size);
  line-height: 18px;
  font-weight: var(--sm-weight-semibold);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.sm-detail .tab-nav-link.active-tab .sm-tab-count { background-color: var(--fld-tint); color: var(--fld-ink); }

/* The Alternate / Variant filter: the custom dropdown, its inline style gone.
   here: the export asked for 32px; 5.x's min-height: 38px (HANDOFF.md 3.9,
   every dropdown trigger) outranks a height, and one control height on the
   page is right, so it stays 38. Variant is the filtered view: the page's
   script adds 16.3's .filter-applied, the list pages' applied look. */
.sm-detail #kindFilterDropdown .dropdown-trigger {
  padding: 0 10px 0 12px;
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-radius-control);
  color: var(--sm-ink-body);
  font-size: var(--sm-type-small-size);
}
.sm-detail #kindFilterDropdown .dropdown-trigger.filter-applied {
  border-color: var(--sm-brand);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-strong);
}

/* here: the export drew the table's left edge as an inset box-shadow on
   .tabs-tables-viewport. Every cell has an opaque background
   (customer_styles.css:633) and an inset shadow paints under the children:
   1px inside the viewport the pixel is the white first cell, so the edge
   never showed. It is a 3px strip above the cells instead, which also takes
   no width from the table. The card's clip (above) rounds it at the corner. */
.sm-detail .tabs-tables-viewport {
  position: relative;
}
.sm-detail .tabs-tables-viewport::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  z-index: 2;
  width: 3px;
  background-color: var(--fld-mark);
  pointer-events: none;
}

.sm-detail .details-table-panel table thead th {
  background-color: var(--fld-tint);
  color: var(--fld-ink);
}

/* here: the export made Value / Phone / Email / Address cells body-strong
   wherever they are, which on Relationship detail made the members' phone
   and email heavier than their names. It is Customer detail's rule, for the
   value a field tab is about, so it is scoped to the tabbed card. */
.sm-detail .tabs-navigation-panel-container:has(.tab-nav-link) .details-table-panel table tbody
  td:is([data-label="Value"], [data-label="Phone"], [data-label="Email"], [data-label="Address"]) {
  color: var(--sm-ink);
  font-weight: var(--sm-type-body-strong-weight);
}

/* Primary: a static badge in the field's own highlight. It marks which value
   is the record's, not an action. */
.sm-primary {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 8px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--fld-hl, var(--sm-surface-sunk));
  color: var(--fld-ink, var(--sm-ink-muted));
  font-size: var(--sm-type-small-size);
  font-weight: var(--sm-weight-semibold);
  white-space: nowrap;
}

/* --- 21.5 Relationship detail: the Members card --------------------------------
   A header row where the tabs would be. Neutral: brand-tint column headers
   (the defaults above), no left edge. */
.sm-card-title {
  display: flex;
  align-items: center;
  gap: var(--sm-space-2);
  height: 48px;
  margin: 0;
  color: var(--sm-ink);
  font-size: var(--sm-type-body-size);
  font-weight: var(--sm-weight-semibold);
}

/* The members' names lead the row, as the deleted inline style had them
   (600, --sm-ink), now from the tokens. 6.3's cell rule outranks
   .bold-text-cell alone, so this takes the table's selector. */
.sm-detail .details-table-panel table tbody td.bold-text-cell {
  color: var(--sm-ink);
  font-weight: var(--sm-type-body-strong-weight);
}

/* --- 21.6 Wrapping by column type (#56 / #61, LISTS-HANDOFF.md 1.7) -------------
   20.5's two rules, for the detail tables. Emails have no break
   opportunities, so one email set its column's min-content to the whole
   address; anywhere lets the browser break it only when the column cannot
   fit it. Phone numbers, dates, counts and badges stay on one line. By
   data-label, never by position. */
.sm-detail .details-table-panel table tbody
  td:is([data-label="Email"], [data-label="Value"], [data-label="Address"], [data-label="Relationship Name"]) {
  overflow-wrap: anywhere;
  min-width: 12ch;
}

.sm-detail .details-table-panel table tbody
  td:is([data-label="Phone"], [data-label="Last seen"], [data-label="Count"], [data-label="Primary"], [data-label="Relationship"]) {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* --- 21.7 Actions pinned right (#61) -----------------------------------------------
   20.6, for the members table, the only detail table with a row action. From
   769 up; at <= 768 the rows are cards (10.2). */
@media (min-width: 769px) {
  .sm-detail .details-table-panel table:has(td > .action-external-trigger) tbody td:last-child:has(> .action-external-trigger),
  .sm-detail .details-table-panel table:has(td > .action-external-trigger) thead th:last-child {
    position: sticky;
    right: 0;
    z-index: 1;
    width: 1%;
    padding-top: 0;
    padding-bottom: 0;
    white-space: nowrap;
    text-align: center;
    box-shadow: inset 1px 0 0 var(--sm-line);
  }

  .sm-detail .details-table-panel table:has(td > .action-external-trigger) thead th:last-child {
    padding-top: var(--sm-space-3);
    padding-bottom: var(--sm-space-3);
    background-color: var(--fld-tint);
  }

  .sm-detail .details-table-panel table:has(td > .action-external-trigger) tbody td:last-child:has(> .action-external-trigger) {
    background-color: var(--sm-surface);
  }

  .sm-detail .details-table-panel table tbody tr:hover td:last-child:has(> .action-external-trigger) {
    background-color: var(--sm-surface-sunk);
  }
}

/* The row action: 20.6's 32px ghost icon button. */
.sm-detail .details-table-panel .action-external-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--sm-radius-control);
  color: var(--sm-ink-muted);
}
.sm-detail .details-table-panel .action-external-trigger:hover {
  background-color: var(--sm-surface);
  color: var(--sm-brand-strong);
  box-shadow: inset 0 0 0 1px var(--sm-line);
}
.sm-detail .details-table-panel .action-external-trigger:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

/* --- 21.8 Empty tabs ------------------------------------------------------------------
   partials/detail_empty.html: 20.8's block, in the detail tables. */
.sm-detail .details-table-panel .sm-list-empty-row td,
.sm-detail .details-table-panel .sm-list-empty-row:hover td {
  padding: 0;
  background-color: var(--sm-surface);
}

/* --- 21.9 Phone widths (LISTS-HANDOFF.md 4) ----------------------------------------------
   The header stacks and the facts take one column. 10.2's cards stand; a
   badge keeps its own width in the value column, as 10.2 does for
   .badge-pill. */
@media (max-width: 768px) {
  .sm-detail .detail-page-header-row { flex-direction: column; }
  .sm-facts,
  .sm-facts:not(:has(> :nth-child(3))) { grid-template-columns: minmax(0, 1fr); }
  .sm-facts > :last-child:nth-child(odd) { grid-column: auto; }

  .sm-detail .details-table-panel table tbody td > .sm-primary {
    justify-self: start;
  }
}

/* ==========================================================================
   22. CLIENT FINAL REVIEW                     Round 4 · items C, D, E, F, G
   --------------------------------------------------------------------------
   Items A, B and H are template and script changes (the loader messages,
   the standalone Relationship header, the eclipse's backing store); nothing
   here. Each subsection names the item it answers.
   ========================================================================== */

/* --------------------------------------------------------------------------
   22.1 Relationships: Back / Next pinned bottom right            item C, #16
   19.1's Review rail, reused: from 1024 up the footer is the flex row's last
   item, aligned to the row's bottom edge and sticky 24px above the
   viewport's bottom. When the record runs past the fold the buttons ride
   there; once its end scrolls into view they settle level with the two
   columns' bottom edge. Below 1024, 11.7's static right-aligned row stands.

   What it replaces: tagging_styles.css:194's margin-top: 38% (a percentage
   of the container's WIDTH, so the buttons sat 437px down at 1440 and below
   the fold on a long record) and its 2rem block padding, which would lift
   the settled buttons 32px off the columns' edge. The column stays a column
   (Back above Next), as it was.

   Trap, as 19.1: nothing between .bottom-buttons and the viewport may clip
   or scroll (overflow other than visible), and the footer must not take
   `top` or align-self: stretch, or it goes back below the fold. The deck's
   own overflow is inside .right-container-relationship, a sibling.
   -------------------------------------------------------------------------- */

@media (min-width: 1024px) {
  /* Room under the columns at least the pin's 24px, as 19.1's 32px margin
     under Review's record: with tagging_styles.css:255's 1em the page ended
     16px below the columns, so at the end of the scroll the buttons were
     still pinned, 8px above the columns' edge instead of level with it. */
  .main-content div.relationship-record-container {
    padding-bottom: var(--sm-space-6);             /* was 1em */
  }

  .relationship-record-container > .bottom-buttons {
    align-self: flex-end;
    position: sticky;
    bottom: var(--sm-space-5);
    margin-top: 0;                                 /* was 38% (DEFERRED-FIXES #16) */
    padding-block: 0;                              /* was 2rem */
  }
}

/* --------------------------------------------------------------------------
   22.2 The step header: room between the steps                    item D
   7.8 left the steps touching: no gap from 1024 up, where the only space
   was the 12px connector, and 16px below. Now each step is kept clear of
   its neighbour. From 1024 up the gap goes either side of the connector,
   so the step-to-step distance is connector + 2 x gap; below 1024 the
   connectors are hidden (7.8) and the gap is the whole distance.

     >= 1100   8px either side   28px step to step (was 12)
     1024-1099 4px either side   20px (was 12): at 1024 the widest header,
                                  Review's, has 65px of slack beside the
                                  title; 8px would push it under the title
     769-1023  16px -> 24px      grows with the viewport (was 16). The steps
                                  have their own row here, but the 96px inset
                                  leaves Review 688px at 800: a flat 24px
                                  wrapped four headers to two rows at 800.
                                  The ramp is Review's width solved for the
                                  gap (579.5px of steps + 6 gaps <= 100vw -
                                  112px), less 1px of margin; 24px from 845.
     <= 768    24px              24px (was 16); the 16px gutter leaves room

   Measured at 1440 / 1200 / 1100 / 1024 / 900 / 800 / 768, both sidebar
   states, in DEFERRED-FIXES #102.
   -------------------------------------------------------------------------- */

@media (min-width: 1024px) {
  .top-bar .right-container {
    column-gap: var(--sm-space-1);
  }
}

@media (min-width: 1100px) {
  .top-bar .right-container {
    column-gap: var(--sm-space-2);
  }
}

@media (max-width: 1023px) {
  .top-bar .right-container {
    column-gap: clamp(var(--sm-space-4), 16.5vw - 115px, var(--sm-space-5));   /* was 16px (7.8) */
  }
}

@media (max-width: 768px) {
  .top-bar .right-container {
    column-gap: var(--sm-space-5);
  }
}

/* --------------------------------------------------------------------------
   22.3 The NetSuite record's band, a step darker                  item E
   7.2 gave every card in a family the same --arb-tint band, so on Name,
   Address, Email and Phone the New Event Record and the NetSuite record
   beside it became identical; before the redesign the NetSuite card was
   the saturated one (#f9e0a6 against the new record's white). Their band
   now takes the family's shade (tokens.css section 3, --sm-field-*-shade:
   ours, halfway from tint to highlight). Same hue family, so the field
   hue still says which field this is; the step says which record.
   .matched-record is every NetSuite card (Primary and Alternate); the
   artifact cards in the right-hand deck keep the tint.
   -------------------------------------------------------------------------- */

.top-cards .record.matched-record > p.heading {
  background-color: var(--arb-shade);
}

/* --------------------------------------------------------------------------
   22.4 The export modal's scope: All customers / New customers only   item F
   Two options stacked tighter than the panel's own gap, so they read as
   one group. The interaction is in left_bar.html (syncExportScope).
   -------------------------------------------------------------------------- */

.sm-daterange__scope {
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-2);
}

/* --------------------------------------------------------------------------
   22.5 Selected is brand, never green                            item G
   HANDOFF.md 4.3: green means completion and nothing else. Four selected
   states still painted it. Each now takes the brand selected pair already
   used by the date presets, the chips and the in-range days
   (--sm-brand-tint fill, --sm-brand-deep ink), or, for a trigger, the
   applied-filter treatment (16.3).
   Green stays on the completed step markers and pills (15.3, 7.8), the
   success popup (8.3) and the Upload Status "done" badge.
   -------------------------------------------------------------------------- */

/* The option menus: "Show: N", Source, Type, both detail pages' Variant and
   Kind menus, the relationship list's limit. customer_styles.css:202 is
   #0aa8a5 on #e6f6f5, both !important -- a teal, the one the client saw. */
.option-item.active-tint {
  background-color: var(--sm-brand-tint) !important;
  color: var(--sm-brand-deep) !important;
}

/* Mapping. The selected option was success tint and ink (13.5). A mapped
   column's trigger was green too; a chosen value is a selection, not a
   finished job, so it takes 16.3's applied-filter look. The "Mapping
   Required..." option (value "", .map-yourself-item) is the unmapped
   choice: it was green as well, and is now an ordinary option. */
.mapping-rows .custom-dropdown-item.selected,
.mapping-rows .custom-dropdown-item.map-yourself-item.selected {
  background-color: var(--sm-brand-tint);          /* was --sm-success-tint */
  color: var(--sm-brand-deep);                     /* was --sm-success-ink */
}

.mapping-rows .custom-dropdown-selected.has-value {
  background-color: var(--sm-brand-tint);          /* was --sm-success-tint */
  border-color: var(--sm-brand);                   /* was --sm-success-mark */
  color: var(--sm-brand-strong);                   /* was --sm-success-ink */
}

.mapping-rows .custom-dropdown-item.map-yourself-item {
  background-color: transparent;                   /* was #d4f0ee */
  color: var(--sm-ink-muted);                      /* was --sm-success-ink */
}

.mapping-rows .custom-dropdown-item.map-yourself-item:hover {
  background-color: var(--sm-brand-wash);          /* was --sm-success-border; as every other item */
}

/* Relationships: the card chosen to Add was 3px #2dfc52 over a green wash,
   scaled 1.02 (tagging_styles.css:614, all !important but the transform);
   Reject was the same in #ff3131. DEFERRED-FIXES #18's last open half.
   Add now matches Matching's chosen card (13.1): brand edge, the 3.13 ring
   and the check badge, no scale. Reject takes the danger edge. Both are
   drawn as shadows, not borders, so no card changes height and the pager
   (11.7, #62) still lands where it did; the viewport's 9px padding
   (tagging_styles.css:631) has room for the 5px ring. */
.right-container-relationship .cards-container .record.selected-add,
.right-container-relationship .cards-container .record.selected-reject {
  border: 0 none !important;
  background-color: var(--sm-surface) !important;
  transform: none;
}

.right-container-relationship .cards-container .record.selected-add {
  position: relative;
  box-shadow: 0 0 0 2px var(--sm-brand),
              0 0 0 5px rgba(1, 138, 198, 0.18),   /* HANDOFF.md 3.13's ring */
              var(--sm-e1);
}

.right-container-relationship .cards-container .record.selected-add::after {
  content: "";
  position: absolute;
  top: var(--sm-space-2);
  right: var(--sm-space-2);
  width: 18px;
  height: 18px;
  background-color: var(--sm-brand-deep);
  -webkit-mask: var(--sm-icon-check) center / contain no-repeat;
          mask: var(--sm-icon-check) center / contain no-repeat;
}

.right-container-relationship .cards-container .record.selected-reject {
  box-shadow: 0 0 0 2px var(--sm-danger-solid), var(--sm-e1);
}

/* ==========================================================================
   23. LOGIN COMPOSITION                        LOADER-LOGIN-HANDOFF.md §2
   --------------------------------------------------------------------------
   Handed off as "§22"; numbered 23 here because 22 is round 4's. After §9,
   as the handoff requires. No template change, no change to
   silk-eclipse.js. Replaces, at 769px and up, the §9 canvas rule
   (width 80%) and login_styles.css's 90vh / 50% / 30% split.

   The eclipse's canvas is not centred on its ring: in the 842-unit source
   the annulus is centred at (423, 384) with an outer radius of 360, so the
   canvas carries 24 units of air above the ring and 98 below. That air is
   why the ring sat 25px above the form and why the page scrolled.

   Here the layout box IS the ring (--ring = its outer diameter) and the
   canvas hangs off it, offset by the measured air, so centring the box
   centres the ring and the gap to the form is measured ring-to-form.
   ========================================================================== */

body:has(.login-container) {
  background-color: var(--sm-surface-page);        /* was #f7f7f7, login_styles.css:36 */
}

/* 96px tall exactly. The inline <img> left a descender gap (header was 101.6). */
.header {
  box-sizing: border-box;
  height: 96px;
  padding: var(--sm-space-6) 0 0 40px;
}

.header img {
  display: block;
  width: auto;
  height: 64px;
}

@media (min-width: 769px) {
  .main-container:has(> .login-container) {
    /* Ring diameter. Height-led so the form is never pushed below the fold:
       100vh - 240 leaves the 96px header, 24px above and 96px + 24px below.
       42vw - 40 keeps ring + gap + form inside the width from 769 to 1440. */
    --ring: max(280px, min(520px, calc(100vh - 240px), calc(42vw - 40px)));

    box-sizing: border-box;
    height: auto;                                  /* was 90vh */
    min-height: calc(100vh - 96px);
    padding: var(--sm-space-5) 40px 96px;          /* bottom = header height: the group centres on the viewport */
    display: grid;
    grid-template-columns: var(--ring) minmax(320px, 400px);
    /* The gap scales with the ring, plus one space step (app round item 2:
       the ring and the form read as one block). 120 at 520 (1440), 96 at 390
       (1024), 76 at 283 (769); was 96 / 72 / 52. Horizontal only, so the
       form's height and the fold are unchanged. */
    column-gap: max(var(--sm-space-7), calc(var(--ring) * 0.185 + var(--sm-space-5)));
    justify-content: center;                       /* ring + gap + form centred as one group */
    align-items: center;                           /* ring centre = form centre */
  }

  .main-container:has(> .login-container) .logo-container {
    position: relative;
    display: block;
    width: var(--ring);                            /* was 50% */
    height: var(--ring);
  }

  .main-container:has(> .login-container) .logo-container canvas.logo-eclipse {
    position: absolute;
    left: calc(var(--ring) * -0.0875);             /* 63 / 720: source air left of the ring */
    top: calc(var(--ring) * -0.03333);             /* 24 / 720: source air above the ring */
    width: calc(var(--ring) * 1.16944);            /* 842 / 720 */
    max-width: calc(var(--ring) * 1.16944);        /* lets it shrink after load; the inline px pins width */
    height: auto !important;                       /* beats the inline px silk-eclipse.js sets */
    aspect-ratio: 1 / 1;
  }

  .main-container:has(> .login-container) .login-container {
    width: 100%;
    max-width: none;                               /* was 30%; the grid track is 320-400 */
  }
}

/* ==========================================================================
   24. UPLOAD STATUS: THE SUMMARY LINE           LOADER-LOGIN-HANDOFF.md §1
   --------------------------------------------------------------------------
   The "Processing N rows from <file>..." message lives here, not in an
   overlay (handoff 1.2). upload_status.html's renderSummary() rewrites the
   lead ("Uploaded" / "Processing N rows from" / "Processed N rows from")
   and the tail ("..." while PROCESSING). It replaces "Successfully
   Uploaded <file>".
   ========================================================================== */

.header-content .upload-summary {
  margin: var(--sm-space-1) 0 0;
  color: var(--sm-ink-body);
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  font-variant-numeric: tabular-nums;              /* the count does not jitter between refreshes */
  overflow-wrap: anywhere;                         /* a 45-character filename wraps at 1024 with the rail open */
}
.header-content .upload-summary .upload-filename {
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
}

/* ==========================================================================
   25. REVIEW: THE RECORD'S WIDTH AND PLACE      Round 5 · item 10, round 6 D
   --------------------------------------------------------------------------
   Round 5 item 10 made the record a fit-content grid with subgrid blocks:
   708px at x = 108 at 1440, which read as too narrow and pushed left.
   Reverted here to 19.1's track (minmax(0, 1100px), 1036px at 1440, blocks
   1fr / 1fr / 1fr), then one adjustment:

   - The record is at most 900px and centred in its track, the space left
     of the rail (x 108-1144 at 1440, so x = 176). Centred in the whole
     content area (108-1408) it would end at x = 1208, past the rail's left
     edge at 1168, so it is centred beside the rail, never under it. The
     record and the rail are separate grid tracks, so they cannot overlap
     at any width or scroll position. Where the track is narrower than
     900px (1280: 876; 1024: 620; the sidebar pushed at 1440: 868) the
     record fills it and starts at the page inset, as before.
   - The label column is 0.6 of a value column (was 1 of 3 equal), so a
     label sits closer to its value without packing them: 131-190px from a
     label's last letter to its value at 1440 (was 262-321 at 1036px; round
     5's minimum was 63-122). Every block keeps the same template, so the
     columns still line up from block to block.

   19.1's rail is untouched: same 240px track, still at the page's right
   edge, still sticky with align-self: end in the record's row. Below 1024
   the one-column layout in 7.7 stands. Scoped by .overall-page, which only
   Review renders; the Relationship pages draw the same record and keep
   their layout.
   ========================================================================== */

@media (min-width: 1024px) {
  .overall-page .left-part {
    justify-self: center;                          /* centred in the record's track, beside the rail */
    width: 100%;
    max-width: 900px;                              /* was the track, up to 1100px (1036 at 1440) */
  }

  .overall-page .left-container-relationship > :is(.name-wrapper, .address-wrapper, .email-wrapper, .phone-wrapper) {
    grid-template-columns: minmax(0, 0.6fr) minmax(0, 1fr) minmax(0, 1fr);   /* was 1fr 1fr 1fr */
  }
}

/* ==========================================================================
   26. DASHBOARD DRAG AND DROP                  APP-ROUND-HANDOFF.md §2
   --------------------------------------------------------------------------
   App round item 6, from upload-drop/upload-drop.css revision 2. Keys on
   the attributes upload-drop.js sets. Every state lives in the upload
   section (.upload-records[data-drop-section]); nothing covers, tints or
   dims the page, and the review list beside it (.review-records) is never
   styled here.

   Not shipped from the design file:
   - `html[data-drop-busy] #bufferOverlay { display: none !important }`.
     It leaves the overlay open (.active, its intent consumed, its orb's
     frame loop polling) behind a CSS rule, and whether the orb draws then
     rests on thinking-orb.js reading that rule through computed styles. The
     upload never opens it instead: #inputFile carries no data-buffer-*, and
     home.html's change handler no longer adds .active.
   - The rest-state border on the section and the zone's rest restyle. The
     section is a column of the shared card, not a card of its own, and the
     zone at rest is home_styles.css's, unchanged.
   - 12px hint and file-size text: the 13px floor (HANDOFF.md) makes both
     --sm-type-small.
   ========================================================================== */

/* 26.1 The section. The cue is drawn inside it (an inset ring), so nothing
   moves; the card clips its corners (overflow: hidden, 16px), so the
   section takes the matching radius on the side it shares with the card. */
.upload-records[data-drop-section] {
  position: relative;
  transition: box-shadow 120ms ease;
}

html[data-drag] [data-drop-section] {
  box-shadow: inset 0 0 0 1px var(--sm-brand), inset 0 0 0 4px var(--sm-brand-tint);
  border-radius: 0 var(--sm-radius-card) var(--sm-radius-card) 0;
}

@media (max-width: 1279px) {
  /* home_styles.css stacks the two columns here: the section is the
     card's bottom */
  html[data-drag] [data-drop-section] {
    border-radius: 0 0 var(--sm-radius-card) var(--sm-radius-card);
  }
}

html[data-drag] [data-drop-section]:has([data-drop-state="reject"]) {
  box-shadow: inset 0 0 0 1px var(--sm-danger-mark), inset 0 0 0 4px var(--sm-danger-tint);
}

/* "Drop here": over the section's top right corner, out of the flow, so it
   appearing never moves the heading or the zone. */
.sm-drop-hint {
  position: absolute;
  top: var(--sm-space-4);
  right: var(--sm-space-4);
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 10px;
  border-radius: var(--sm-radius-pill);
  background: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
  pointer-events: none;
}

.sm-drop-hint[hidden] {
  display: none;
}

/* 26.2 The zone. Same specificity as home_styles.css's :hover, later. */
[data-drop-zone] {
  transition: border-color 120ms ease, background-color 120ms ease;
}

[data-drop-zone][data-drop-state="ready"] {
  border-color: var(--sm-brand);
  background-color: var(--sm-brand-wash);
}

[data-drop-zone][data-drop-state="over"] {
  border-style: solid;
  border-color: var(--sm-brand);
  background-color: var(--sm-brand-tint);
}

[data-drop-zone][data-drop-state="over"] .upload-drop-icon {
  background-color: var(--sm-brand-deep);
  color: var(--sm-surface);
}

[data-drop-zone][data-drop-state="reject"] {
  border-color: var(--sm-danger-mark);
  background-color: var(--sm-danger-tint);
}

[data-drop-zone][data-drop-state="reject"] .upload-drop-icon {
  background-color: var(--sm-surface);
  color: var(--sm-danger-ink);
}

[data-drop-zone][data-drop-state="reject"] [data-drop-title] {
  color: var(--sm-danger-ink);
}

/* Copy is swapped during a drag; its children must not become drag targets */
[data-drop-zone][data-drop-state] [data-drop-title],
[data-drop-zone][data-drop-state] [data-drop-sub] {
  pointer-events: none;
}

/* 26.3 Uploading: the file row under the copy and a progress bar under it
   (round 7, item B; was a 64px orb in the icon's place). The zone is the
   file picker's label; a second pick mid-upload would submit the form
   twice, so it stops taking clicks. */
[data-drop-zone][data-drop-state="uploading"] {
  border-style: solid;
  border-color: var(--sm-brand);
  background-color: var(--sm-surface);
  cursor: default;
  pointer-events: none;
}

[data-drop-zone][data-drop-state="uploading"] .upload-drop-icon {
  display: none;
}

/* The bar. The upload is a plain form POST, which tells the page nothing
   about its progress (DEFERRED-FIXES #121), so it is indeterminate: a 40%
   segment crossing the track, in the bottom progress bar's colours (27):
   --sm-line track, --sm-brand fill, 4px. The design files have no bar
   state. Reduced motion: no sweep; the whole track is the tint, as 27's
   current-step segment. */
.sm-drop-busy {
  display: block;
  align-self: stretch;
}

.sm-drop-busy[hidden] {
  display: none;
}

.sm-drop-bar {
  position: relative;
  display: block;
  height: 4px;
  overflow: hidden;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-line);
}

.sm-drop-bar__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 40%;
  border-radius: inherit;
  background-color: var(--sm-brand);
  animation: sm-drop-bar 1.2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

/* translateX is a share of the fill: -100% is just off the left end,
   250% (2.5 x 40%) just off the right. */
@keyframes sm-drop-bar {
  from { transform: translateX(-100%); }
  to { transform: translateX(250%); }
}

@media (prefers-reduced-motion: reduce) {
  .sm-drop-bar__fill {
    width: 100%;
    background-color: var(--sm-info-border);
    animation: none;
  }
}

.sm-drop-files {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-2);
  align-self: stretch;
}

.sm-drop-files[hidden] {
  display: none;
}

.sm-drop-file {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sm-space-3);
  min-height: 44px;
  padding: 0 var(--sm-space-3);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-control);
  background: var(--sm-surface);
}

.sm-drop-file__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-semibold);
  color: var(--sm-ink);
}

.sm-drop-file__meta {
  flex: none;
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  color: var(--sm-ink-quiet);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .upload-records[data-drop-section],
  [data-drop-zone] {
    transition: none;
  }
}


/* ==========================================================================
   27. MATCHING PROGRESS                        APP-ROUND-HANDOFF.md §3
   --------------------------------------------------------------------------
   App round items 8 and 9, from progress/matching-progress.css. The step
   header and the bottom bar are one system: seven positions, one blue, one
   rule (a step is blue once it is reached).

   Two values come from markup, not from here:
   - the fill is the steps COMPLETED over 7 (progress_bar.html), so it ends
     on a segment edge; Review is 6 of 7 until Done succeeds, when
     review_page.html fills the seventh and marks Review done;
   - the bar starts at the sidebar's edge, --sm-sidebar-w, which
     left_bar.html's syncSilkSidebarToggle() sets to 240px on open and
     removes on close (this section's 72px, 0 at <= 768, then applies).

   Not taken from the design file:
   - its `column-gap: 6px` on the steps. 22.2's gaps stand (4px / 8px
     either side of a connector from 1024, the 16 -> 24px ramp below), so
     round 2's step spacing is not undone.
   - its `flex: 0 1 600px` basis. .top-bar wraps (7.8), and a wrapping row
     breaks on the basis: 600px would put the steps under the title at 1024
     with the sidebar expanded. The basis is the row's own minimum instead
     (min-content), growing to the 600px cap, so the steps stay beside the
     title wherever that minimum fits.
   - 12px step names are the handoff's (§3: "labels 12px"), under the 13px
     floor; logged with #36's precedent in DEFERRED-FIXES #116.
   ========================================================================== */

/* --- 27.1 Step header ---------------------------------------------------- */
.top-bar .right-container {
  align-items: flex-start;
  counter-reset: sm-step;
}

@media (min-width: 1024px) {
  .top-bar .left-container {
    flex: none;                                    /* the title never truncates */
  }

  /* base_styles.css:373 gives the title width: 50%, which in a flex: none
     column would halve it. */
  .top-bar .left-container h3 {
    width: auto;
  }

  .top-bar .right-container {
    flex: 1 1 0;
    min-width: min-content;                        /* the one-row minimum is the line-break basis */
    max-width: 600px;
    margin-left: auto;
    flex-wrap: nowrap;
  }
}

/* the connectors are <div>s too, hence :not(.connector) */
.top-bar .right-container > div:not(.connector) {
  counter-increment: sm-step;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
}

/* STEP N and the status pill go: the marker and its name carry the state.
   STEP N stays for screen readers. */
.top-bar .right-container > div p:first-of-type {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.top-bar .right-container > div p:nth-of-type(3) {
  display: none;
}

.top-bar .right-container > div p:nth-of-type(2) {
  color: var(--sm-ink-quiet);
  font-size: 12px;                                 /* handoff §3; see #116 */
  line-height: 16px;
  font-weight: var(--sm-weight-regular);
  white-space: nowrap;
}

.top-bar .right-container .step-marker {
  width: 24px;                                     /* was 30px (15.x) */
  height: 24px;
  margin: 0;                                       /* was 10px below */
  background-color: var(--sm-surface);
  box-shadow: inset 0 0 0 1px var(--sm-line-strong);
  color: var(--sm-ink-muted);
}

.top-bar .right-container .step-marker .sm-icon {
  width: 14px;
  height: 14px;
}

/* Pending: the step number replaces the grey icon. */
.top-bar .right-container .step-marker:not(.is-current):not(.is-done) .sm-icon {
  display: none;
}

.top-bar .right-container .step-marker:not(.is-current):not(.is-done)::before {
  content: counter(sm-step);
  font-size: 12px;
  line-height: 16px;
  font-weight: var(--sm-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.top-bar .right-container .step-marker.is-current {
  background-color: var(--sm-brand-deep);
  box-shadow: 0 0 0 3px var(--sm-brand-tint);
  color: var(--sm-surface);
}

.top-bar .right-container .step-marker.is-current ~ p:nth-of-type(2) {
  color: var(--sm-brand-deep);
  font-weight: var(--sm-weight-semibold);
}

.top-bar .right-container .step-marker.is-done {
  background-color: var(--sm-brand-tint);
  box-shadow: none;
  color: var(--sm-brand-deep);
}

.top-bar .right-container .step-marker.is-done .sm-icon {
  stroke-width: 2.5;
}

.top-bar .right-container .step-marker.is-done ~ p:nth-of-type(2) {
  color: var(--sm-ink-body);
  font-weight: var(--sm-weight-medium);
}

/* A connector is blue once the step after it is reached. Below 1024 7.8
   still hides them, and the steps wrap with 22.2's gaps. */
.top-bar .right-container .connector {
  height: 2px;
  margin-top: 11px;                                /* the marker's centre */
  border-radius: 1px;
}

.top-bar .right-container .connector:has(+ div .step-marker.is-current),
.top-bar .right-container .connector:has(+ div .step-marker.is-done) {
  background-color: var(--sm-brand);
}

/* --- 27.2 Bottom bar ----------------------------------------------------- */
:root {
  --sm-sidebar-w: 72px;                            /* left_bar.html sets 240px while open */
}

@media (max-width: 768px) {
  :root {
    --sm-sidebar-w: 0px;                           /* the rail is off-canvas */
  }
}

.bottom-progress-container {
  left: var(--sm-sidebar-w);                       /* was 0, under the sidebar */
  right: 0;
  width: auto;                                     /* was 100% */
  height: 4px;                                     /* was 8px */
  background: transparent;
  transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);   /* the sidebar's own */
}

/* Seven segments with 2px gaps; the mask cuts track and fill alike. */
.progress-track {
  container-type: inline-size;
  overflow: hidden;
  background-color: var(--sm-line);
  -webkit-mask: repeating-linear-gradient(90deg, #000 0 calc(100% / 7 - 2px), transparent calc(100% / 7 - 2px) calc(100% / 7));
          mask: repeating-linear-gradient(90deg, #000 0 calc(100% / 7 - 2px), transparent calc(100% / 7 - 2px) calc(100% / 7));
}

.progress-fill {
  position: relative;
  border-radius: 0;                                /* was 0 4px 4px 0 */
  background-color: var(--sm-brand);
  transition: width 0.4s ease-out;
}

/* The current step: the next segment, in tint. */
.progress-fill::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 100%;
  width: calc(100cqw / 7);
  background-color: var(--sm-info-border);
}

@media (prefers-reduced-motion: reduce) {
  .bottom-progress-container,
  .progress-fill {
    transition: none;
  }
}

/* ==========================================================================
   28. THE DETAIL PAGE HEADER                   APP-ROUND-HANDOFF.md §4
   --------------------------------------------------------------------------
   App round item 13, direction 4a revision 2, from detail/detail-header.css.
   One card on customer detail and relationship detail, replacing 21.2's
   title row and 21.3's tiles (their rules now match nothing: #95). The
   field table below is 21.4 on, untouched.

     .detail-page-header-row   the page's name ("Customer Details") and
                               View in System, as the design's page row
     header.sm-dh              name, type badge, ID; then the facts

   The template renders a fact only when it has a value, and decides the
   address variants (billing = shipping shown once as "Billing and shipping
   address", billing only, shipping only, both). The type badge is rendered
   only when the API sends a type (customer.record_type, #92). The ID is
   free-standing: deleting its line moves nothing else.

   11px labels (the micro role), 12px type badge and ID value are the
   design's; see #116.
   ========================================================================== */

.sm-detail .detail-page-header-row {
  align-items: center;
  margin: 0 0 var(--sm-space-5);
}

.sm-detail .detail-page-header-row .page-main-title {
  margin: 0;
  font-size: var(--sm-type-title-size);            /* was 30px (customer_styles.css:43) */
  line-height: var(--sm-type-title-leading);
  font-weight: var(--sm-type-title-weight);
  letter-spacing: normal;
  color: var(--sm-ink);
}

.sm-dh {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--sm-space-4);
  margin: 0 0 var(--sm-space-5);
  padding: var(--sm-space-5) 28px;
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e1);
}

.sm-dh__who {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
}

.sm-dh__name {
  margin: 0;
  font-size: 24px;
  line-height: 32px;
  font-weight: var(--sm-weight-semibold);
  letter-spacing: -0.3px;
  color: var(--sm-ink);
  overflow-wrap: anywhere;
  text-wrap: pretty;
}

.sm-dh__type {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 24px;
  padding: 0 10px;
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
  font-size: 12px;
  line-height: 16px;
  font-weight: var(--sm-weight-semibold);
  white-space: nowrap;
}

.sm-dh__type .sm-icon {
  width: 14px;
  height: 14px;
}

.sm-dh__id {
  display: inline-flex;
  align-items: stretch;
  max-width: 100%;
  min-height: 28px;
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-control);
  overflow: hidden;
}

.sm-dh__id-label {
  display: flex;
  flex: none;
  align-items: center;
  padding: 0 var(--sm-space-2);
  background-color: var(--sm-surface-sunk);
  border-right: 1px solid var(--sm-line);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-micro-size);
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
}

.sm-dh__id-value {
  display: flex;
  align-items: center;
  min-width: 0;
  padding: 0 10px;
  font-family: var(--sm-type-mono-family);
  font-size: 12px;
  line-height: 16px;
  color: var(--sm-ink-body);
  overflow-wrap: anywhere;
}

.sm-dh__facts {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--sm-space-4) var(--sm-space-5);
  margin: var(--sm-space-1) 0 0;
  padding: var(--sm-space-5) 0 0;
  border-top: 1px solid var(--sm-line);
}

.sm-dh__fact {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding-left: var(--sm-space-3);
  border-left: 3px solid var(--dh-mark);
}

.sm-dh__fact dt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--dh-ink);
  font-size: var(--sm-type-micro-size);
  line-height: var(--sm-type-micro-leading);
  font-weight: var(--sm-type-micro-weight);
  letter-spacing: var(--sm-type-micro-tracking);
  text-transform: var(--sm-type-micro-transform);
}

.sm-dh__fact dt .sm-icon {
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--dh-mark);
}

.sm-dh__fact dd {
  margin: 0;
  color: var(--sm-ink);
  font-size: 14px;
  line-height: 20px;
  overflow-wrap: anywhere;
}

/* here: the first phone or email when none is marked primary, as 21.3's
   tile said it. Not in the design, which has no such record. */
.sm-dh__note {
  display: block;
  color: var(--sm-ink-quiet);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
}

.sm-dh__fact[data-field="phone"]   { --dh-mark: var(--sm-field-phone-mark); --dh-ink: var(--sm-field-phone-ink); }
.sm-dh__fact[data-field="email"]   { --dh-mark: var(--sm-field-email-mark); --dh-ink: var(--sm-field-email-ink); }
.sm-dh__fact[data-field="address"] { --dh-mark: var(--sm-field-addr-mark);  --dh-ink: var(--sm-field-addr-ink); }

/* At 1023 and below the ID drops under the type. */
@media (max-width: 1023px) {
  .sm-dh {
    grid-template-columns: minmax(0, 1fr);
  }

  .sm-dh__id {
    justify-self: start;
  }
}

/* here: the design stops at 720px of content. At phone width the card's
   28px side padding leaves too little for the 220px fact track. */
@media (max-width: 768px) {
  /* 21.9 stacks the page row; keep the title at the start, not centred */
  .sm-detail .detail-page-header-row {
    align-items: flex-start;
  }

  .sm-dh {
    padding: var(--sm-space-4);
  }

  .sm-dh__facts {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==========================================================================
   29. A FILE DROPPED ON A STAGED FILE                  DEFERRED-FIXES #115
   --------------------------------------------------------------------------
   With a file staged there is no drop zone; upload-drop.js puts
   #processingBox[data-drop-staged] in 26's reject state for the length of
   a file drag. 26.1 already rings the section in danger for any reject
   state; this gives the box the zone's reject colours. The reason is 8.5's
   notice in the box's .messages, written during the drag (where the zone
   shows its reason) and kept as an alert on release. No "Drop here" tag:
   nothing can be dropped, and the tag's corner is the centred heading's
   at >= 1280.
   ========================================================================== */

[data-drop-staged] {
  transition: border-color 120ms ease, background-color 120ms ease;
}

[data-drop-staged][data-drop-state="reject"] {
  border-color: var(--sm-danger-mark);
  background-color: var(--sm-danger-tint);
}

[data-drop-staged] .messages {
  align-self: stretch;
}

[data-drop-staged] .messages:empty {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-drop-staged] {
    transition: none;
  }
}

/* ==========================================================================
   30. THE STEP HEADER: EVEN SPACING BETWEEN THE STEPS           round 6 · E
   --------------------------------------------------------------------------
   A step was as wide as its name, and the connectors share out the rest of
   the row equally, so a long name widened its own gaps: at 1440 the icons
   were 84-91px apart except either side of "Relationships" (106-109px).

   From 1024 up every step is now the same 40px box (the marker plus the
   widest end name, Review's), and its name is centred on the marker and
   allowed to run past the box on both sides. Seven equal boxes and six
   equal connectors put every icon the same distance from the next,
   whatever the names say.

   The connector's floor keeps that distance at 66px or more, which is
   "Relationships" (76px) and its widest neighbour, half each, plus 8px of
   clear space between the two names. The one-row minimum is then 7 x 40 +
   6 x 26 = 436px (was 405-412px, the names' own widths), still inside the
   narrowest place the steps sat beside the title before (Address at 1024,
   444px). The first and last names fit their boxes, so nothing runs past
   the row's ends. 22.2's gaps either side of the connector are unchanged.

   Below 1024 the connectors are hidden and the steps wrap (7.8), so a name
   that runs past its box could run past a row's end: that range keeps its
   own spacing (DEFERRED-FIXES #119).
   ========================================================================== */

@media (min-width: 1024px) {
  .top-bar .right-container > div:not(.connector) {
    width: 40px;                                   /* was the name's width */
  }

  .top-bar .right-container > div:not(.connector) p:nth-of-type(2) {
    text-align: center;
  }

  .top-bar .right-container .connector {
    min-width: 18px;                               /* 40 + 2 x 4 + 18 = 66px icon to icon, 1024-1099 */
  }
}

@media (min-width: 1100px) {
  .top-bar .right-container .connector {
    min-width: 10px;                               /* 40 + 2 x 8 + 10 = 66px icon to icon */
  }
}

/* ==========================================================================
   31. THE DASHBOARD TILES IN ONE ROW                            round 8 · A
   Total records, Individual, Company, Relationships, in that order
   (home.html). home_styles.css drops the grid to two columns at <= 1279
   (repeat(2, 1fr)) and auto-fits it above 1439, so from 1024 up it is four
   columns at every width. minmax(0, 1fr), not 1fr: at 1024 with the
   sidebar open the row is 640px and a tile's min-content must not widen
   its column past the row. Below 1024 the 2 x 2 grid stands.
   ========================================================================== */

@media (min-width: 1024px) {
  .dashboard-metrics-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));   /* was 2 columns at 1024-1279 */
  }
}

/* ==========================================================================
   32. KEYBOARD ACCESS                          round 9 · E · #68, #96, #97
   Three mouse-only controls are buttons now, with the same ids and classes:
   the sidebar's Export (left_bar.html), the sort header's label row on both
   lists (.header-sort-flex, inside th.sortable-column-header, whose click
   listener the button's click bubbles to), and Upload Status's two Review
   boxes (the .disabled-box variants are disabled buttons, enabled by
   updateUI). Each rule takes away only what a button adds over the <div>
   it replaces, so every box measures as before, and gives it the one ring.
   ========================================================================== */

/* 32.1 Export: the nav rows' box. Same specificity as .icon-link-wrapper,
   later; :hover, .special and .left-bar.open's padding still win. */
button.icon-link-wrapper {
  margin: 0;
  padding: 0;
  border: 0;
  background-color: transparent;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-align: left;
}

/* Base's .icon-link-wrapper:hover fill, which the reset above would
   otherwise outrank. */
button.icon-link-wrapper:hover {
  background-color: var(--sm-surface-sunk);
}

/* 32.2 The sort header's label row: the full width of its th, as the <div>
   was. 5.7's .sortable-column-header:focus-visible ring now goes on the
   button, which is what takes focus. */
.sortable-column-header button.header-sort-flex {
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--sm-radius-control);
  background: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: inherit;
  cursor: inherit;
}

.sortable-column-header button.header-sort-flex:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

/* 32.3 Upload Status's Review boxes. The <div> was already border-box, as
   a button is, so only the border and the inherited text go. */
button.review-box {
  border: 0;
  font-family: inherit;
  line-height: inherit;
  letter-spacing: inherit;
}

button.review-box:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

/* ==========================================================================
   33. FOCUS TRAPS                                   round 9 · F · #83, #88
   The export modal and Review's side panel hold focus while open
   (smTrapFocus in left_bar.html). The panel moves focus to its close button
   as it opens, but review_styles.css:431 fades its visibility in over 0.3s,
   and a control under visibility: hidden cannot take focus. Opening, it is
   visible at once and only the opacity fades; closing, both still fade.
   ========================================================================== */

.relationship-overlay.active {
  transition: opacity 0.3s ease, visibility 0s;
}

/* ==========================================================================
   34. DONE'S OUTCOMES                       round 11 · B, C · ONHO 3, 7
   Done's popup says what happened to the record (review_page.html,
   showSuccessPopup): data-outcome on .success-popup is "success" (sync ok),
   "warning" (saved, the source system only partly updated), "error" (saved
   in Silk Matrix, not in the source system) or "pending" (no answer in
   time). Success keeps its own hue: it is completion and nothing else
   (tokens.css 4). There is no amber token, so a warning takes the Info
   treatment, an error Danger, and pending the neutrals. The backend's
   detail sits under the message in the small role.
   ========================================================================== */

.success-popup[data-outcome="warning"] .popup-checkmark {
  background-color: var(--sm-brand-tint);
  box-shadow: inset 0 0 0 1px var(--sm-info-border);
  color: var(--sm-brand-deep);
}

.success-popup[data-outcome="error"] .popup-checkmark {
  background-color: var(--sm-danger-tint);
  box-shadow: inset 0 0 0 1px var(--sm-danger-border);
  color: var(--sm-danger-solid);
}

.success-popup[data-outcome="pending"] .popup-checkmark {
  background-color: var(--sm-surface-sunk);
  box-shadow: inset 0 0 0 1px var(--sm-line);
  color: var(--sm-ink-muted);
}

.success-popup .popup-detail {
  margin-top: calc(var(--sm-space-3) - var(--sm-space-5));
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  overflow-wrap: anywhere;
}

.success-popup .popup-detail[hidden] {
  display: none;
}

/* ==========================================================================
   35. A LIST THAT FAILED TO LOAD                    round 11 · C · ONHO 3
   partials/list_empty.html's load_error branch: the list's call timed out
   or was refused, so the table says so instead of "No customers yet". The
   empty state's layout, with the Danger disc for the icon.
   ========================================================================== */

.sm-list-empty__icon--error {
  background-color: var(--sm-danger-tint);
  box-shadow: inset 0 0 0 1px var(--sm-danger-border);
  color: var(--sm-danger-solid);
}

/* ==========================================================================
   36. THE FILE PREVIEW'S ROW COUNT                  round 11 · I · ONHO 16
   "Showing the first 10 of 15 rows" under the modal's title (home.html,
   #previewCount). The title keeps its 16px bottom margin
   (home_styles.css:513, !important), so the line pulls up into it by 8px:
   8px from the title, 16px to the table.
   ========================================================================== */

#filePreviewModal .sm-preview-count {
  margin: calc(-1 * var(--sm-space-2)) 0 var(--sm-space-4);
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
}

#filePreviewModal .sm-preview-count[hidden] {
  display: none;
}

/* ==========================================================================
   37. MORE THAN ONE ADDRESS IN A MATCH CARD          round 11 · I · CLRI N8
   The incoming record now shows every address (matching.html), each its own
   .address-section inside .address-container, as a candidate's are. 7.6
   flattens the sections, so a second one is divided from the first by the
   rows' own line.
   ========================================================================== */

:is(.attributes-sections, .potential-match) > .address-container > .address-section + .address-section {
  margin-top: var(--sm-space-1);
  border-top: 1px solid var(--sm-line);
}

/* ==========================================================================
   38. THE ERROR PAGE FILLS THE WINDOW                round 11 · I · ONHO 18
   .main-content stops at its 75vh min-height and paints the page grey;
   html and body are transparent, so the last quarter of the window showed
   white under the 404 card. On the error page only, the content area and
   the card's wrapper (error_styles.css: min-height 70vh) take the full
   window, and the card centres in it.
   ========================================================================== */

.main-content:has(> .error-page-wrapper),
.main-content > .error-page-wrapper {
  min-height: 100vh;
}

/* ==========================================================================
   39. WHAT A WRITE DID, AND THE RELATIONSHIP QUEUE      round 12 · A, B, C, D
   --------------------------------------------------------------------------
   39.1 The outcome popup (partials/outcome_popup.html), on Matching (Reject)
   and Relationships (the submit). It is 8.3's panel: the overlay's own box
   comes from review_styles.css / tagging_styles.css, which Matching does not
   load, so it is given here for .sm-outcome only. Visible at once on open,
   as 33's panel, so its Continue link can take focus.
   ========================================================================== */

.success-popup-overlay.sm-outcome {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  visibility: hidden;
  opacity: 0;
}

.success-popup-overlay.sm-outcome.active {
  visibility: visible;
  opacity: 1;
  transition: opacity 200ms ease-out, visibility 0s;
}

/* matching_styles.css:293 sets every <p> but the record heading to
   white-space: nowrap (and flex, which 8.3 already undoes): the message ran
   out of the panel. */
.sm-outcome .success-popup p {
  white-space: normal;
}

/* not_supported (sync.status): neutral information, as pending (34). */
.success-popup[data-outcome="info"] .popup-checkmark {
  background-color: var(--sm-surface-sunk);
  box-shadow: inset 0 0 0 1px var(--sm-line);
  color: var(--sm-ink-muted);
}

/* One row per relationship: its mark, its label, the source system's words. */
.sm-outcome .sm-outcome__items {
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-3);
  max-height: 40vh;
  margin: calc(var(--sm-space-2) - var(--sm-space-5)) 0 var(--sm-space-5);
  padding: var(--sm-space-3) 0 0;
  overflow-y: auto;
  border-top: 1px solid var(--sm-line);
  list-style: none;
  text-align: left;
}

.sm-outcome .sm-outcome__items[hidden] {
  display: none;
}

.sm-outcome .sm-outcome__item {
  display: flex;
  align-items: flex-start;
  gap: var(--sm-space-2);
}

.sm-outcome .sm-outcome__mark {
  display: inline-flex;
  flex: none;
  margin-top: 2px;
  color: var(--sm-ink-muted);
}

.sm-outcome .sm-outcome__item[data-outcome="success"] .sm-outcome__mark { color: var(--sm-success-ink); }
.sm-outcome .sm-outcome__item[data-outcome="warning"] .sm-outcome__mark { color: var(--sm-brand-deep); }
.sm-outcome .sm-outcome__item[data-outcome="error"] .sm-outcome__mark { color: var(--sm-danger-solid); }

.sm-outcome .sm-outcome__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  color: var(--sm-ink-muted);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  overflow-wrap: anywhere;
}

.sm-outcome .sm-outcome__text strong {
  color: var(--sm-ink);
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
}

/* --------------------------------------------------------------------------
   39.2 Skip (standalone Relationships, item C): a secondary button between
   Back and Next. 5.3 makes every .bottom-buttons button primary, so Skip
   takes 5.2's outline back, and the UA's button font.
   -------------------------------------------------------------------------- */

.bottom-buttons button.sm-rel-skip {
  background-color: var(--sm-surface);
  background-image: none;
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
  font: inherit;
  font-size: var(--sm-type-body-size);             /* the links beside it: 14px, 500 */
  line-height: var(--sm-type-body-leading);
  font-weight: var(--sm-weight-medium);
  cursor: pointer;
}

.bottom-buttons button.sm-rel-skip:hover {
  background-color: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  color: var(--sm-brand-strong);
}

/* The queue after a Skip: empty (404) or refused (400). 20's empty state,
   in the record container's place. tagging_styles.css:294 sets 12px and
   inline padding on every <p> in the container, so the type is restated. */
.relationship-record-container .sm-rel-queue {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sm-space-3);
  padding: var(--sm-space-8) var(--sm-space-5);
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);
  text-align: center;
}

.relationship-record-container .sm-rel-queue p {
  padding: 0;
}

.relationship-record-container .sm-rel-queue .sm-list-empty__title {
  font-size: var(--sm-type-subtitle-size);
}

.relationship-record-container .sm-rel-queue .sm-list-empty__body {
  font-size: var(--sm-type-small-size);
}

.relationship-record-container .sm-rel-queue .process-btn {
  margin-top: var(--sm-space-2);
}

/* --------------------------------------------------------------------------
   39.3 The group a suggestion would join (item D): existing_parent's name
   and members, under the card's title. Neutral, so the card's own hue
   stays the only colour.
   -------------------------------------------------------------------------- */

.relationship-record-container .sm-rel-group {
  margin: 0 0 var(--sm-space-3);
  padding: var(--sm-space-3);
  background-color: var(--sm-surface-sunk);
  border-radius: var(--sm-radius-control);
}

.relationship-record-container .sm-rel-group p,
.relationship-record-container .sm-rel-group li {
  display: block;                                  /* tagging_styles.css:249: every <p> is flex */
  min-height: 0;
  margin: 0;
  padding: 0;
  color: var(--sm-ink-body);
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
}

.relationship-record-container .sm-rel-group p strong {
  color: var(--sm-ink);
  font-weight: var(--sm-weight-semibold);
}

.relationship-record-container .sm-rel-group .sm-rel-group__label {
  margin-top: var(--sm-space-2);
  color: var(--sm-ink-muted);
}

.relationship-record-container .sm-rel-group__members {
  margin: var(--sm-space-1) 0 0;
  padding-left: var(--sm-space-4);
}

.relationship-record-container .sm-rel-group .sm-rel-group__more {
  color: var(--sm-ink-muted);
  list-style: none;
}

/* ==========================================================================
   40. THE CUSTOMER LIST SORTS ON THE SERVER                 round 12 · F
   Every column the backend can sort by is a sort header now
   (partials/sort_header.html), and the sort covers the whole list. While a
   name search is active the backend will not sort, so the headers are
   disabled: no hover fill, the not-allowed cursor, and the th's title says
   why.
   ========================================================================== */

.sortable-column-header button.header-sort-flex:disabled {
  cursor: not-allowed;
}

/* The rest colour is --sm-brand-tint (6.5): the hover fill does not come. */
.sortable-column-header:has(> button.header-sort-flex:disabled):hover {
  background-color: var(--sm-brand-tint) !important;
}

/* At <= 768 the headers are the sort bar above the cards (10.x): four sort
   headers now, which ran 23px past a 390 screen in one row. They wrap, two
   to a row on a phone. */
@media (max-width: 768px) {
  .customers-table-container thead tr {
    flex-wrap: wrap;
  }

  .customers-table-container thead th.sortable-column-header {
    flex: 1 1 140px;
  }
}

/* ==========================================================================
   41. MAPPING: FIELD GROUPS AND NAME FORMS                 round 13 · ONHO 1
   "Mapping · field groups and name forms", variant 1b (selectable cards),
   Internal 3 of the design system. Four groups, each with its rule on the
   heading; Name has two forms as cards, and only the chosen form's rows
   exist. The design's First value column was left out on purpose, so the
   card is the design's card less that column, plus the 44px that keeps each
   form card's line on one row: the rows keep the design's 240 / 32 / 300
   proportions (as fr: 260 / 32 / 324 inside a 720 card, and they shrink
   with the card). At <= 768 mapping_styles.css's own one-row grid stands.
   ========================================================================== */

.mapping-container {
  max-width: 720px;                                /* was 750 */
  padding: 40px;                                   /* was 2.5rem 3rem */
  border-radius: var(--sm-radius-card);
}

.mapping-header h2 {
  font-size: var(--sm-type-display-size);
  line-height: var(--sm-type-display-leading);
  font-weight: var(--sm-type-display-weight);
  letter-spacing: var(--sm-type-display-tracking);
}

.mapping-filename {
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  color: var(--sm-ink-muted);
}

.mapping-container .mapping-col-header {
  margin-bottom: var(--sm-space-5);
  padding: 0;
  font-size: var(--sm-type-small-size);
  line-height: 18px;
  font-weight: var(--sm-weight-semibold);
  color: var(--sm-ink-muted);
}

.mapping-container .mapping-col-header > span:first-child {
  padding-left: 14px;
}

#mappingRows {
  gap: var(--sm-space-5);                          /* was 0.8rem: the card's 24px rhythm */
}

@media (min-width: 769px) {
  .mapping-container .mapping-col-header,
  #mappingRows .mapping-row {
    grid-template-columns: minmax(0, 240fr) 32px minmax(0, 300fr);
    column-gap: var(--sm-space-3);
  }
}

@media (max-width: 1023px) {
  .mapping-container {
    padding: var(--sm-space-6);                    /* 32, the design's 1024 card */
  }
}

@media (max-width: 768px) {
  .mapping-container {
    padding: var(--sm-space-5) var(--sm-space-4);
  }
}

/* A group: heading row, then its rows. Every group after the first is ruled
   off with 24px above its heading. */
.sm-map-group {
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-3);
}

.sm-map-group + .sm-map-group {
  padding-top: var(--sm-space-5);
  border-top: 1px solid var(--sm-line);
}

.sm-map-group__head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--sm-space-2) var(--sm-space-4);
}

.sm-map-group__title {
  margin: 0;
  font-size: var(--sm-type-subtitle-size);
  line-height: var(--sm-type-subtitle-leading);
  font-weight: var(--sm-type-subtitle-weight);
  color: var(--sm-ink);
}

.sm-map-group__rule {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--sm-type-small-size);
  line-height: 18px;
  color: var(--sm-ink-muted);
}

.sm-map-group__rule[hidden] {
  display: none;
}

.sm-map-group__rule.is-error {
  color: var(--sm-danger-ink);
  font-weight: var(--sm-weight-medium);
}

.sm-map-group__rule.is-error .sm-icon {
  flex: none;
  color: var(--sm-danger-solid);
}

.sm-map-group__rows {
  display: flex;
  flex-direction: column;
  gap: var(--sm-space-3);
}

.sm-map-group__rows[hidden] {
  display: none;
}

/* The row: a 40px field name and a 40px trigger, the design's sizes. */
#mappingRows .mapping-field {
  display: flex;
  align-items: center;
  gap: var(--sm-space-1);
  box-sizing: border-box;
  height: 40px;
  padding: 0 14px;
  border-width: 1px;
  border-radius: var(--sm-radius-control);
  font-size: 15px;
  line-height: 20px;
  font-weight: var(--sm-weight-medium);
  white-space: nowrap;
  overflow: hidden;
}

#mappingRows .mapping-field .required-star {
  margin-left: 0;
  color: var(--sm-danger-mark);
}

.sm-map-optional {
  margin-left: auto;
  padding-left: var(--sm-space-2);
  font-size: 12px;                                 /* the design's tag; the label beside it carries the meaning */
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-quiet);
}

#mappingRows .custom-dropdown-selected {
  box-sizing: border-box;
  min-height: 40px;
  padding-top: 0;
  padding-bottom: 0;
  border-width: 1px;
  border-radius: var(--sm-radius-control);
  font-size: 15px;
}

#mappingRows .custom-dropdown-selected .dropdown-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#mappingRows .custom-dropdown.select-required .custom-dropdown-selected {
  background-color: var(--sm-danger-tint);
  border-color: var(--sm-danger-mark);
}

/* ---- The name form switch: two selectable cards ---------------------------- */

.sm-name-switch {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: var(--sm-space-1);
}

.sm-name-forms {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sm-space-3);
}

@media (max-width: 600px) {
  .sm-name-forms {
    grid-template-columns: minmax(0, 1fr);
  }
}

.sm-name-form {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--sm-space-3);
  box-sizing: border-box;
  padding: var(--sm-space-3) 14px;
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line-strong);
  border-radius: var(--sm-radius-control);
  cursor: pointer;
  transition: border-color 120ms ease-out, background-color 120ms ease-out;
}

.sm-name-form:hover {
  border-color: var(--sm-brand);
}

.sm-name-form:has(input:checked) {
  background-color: var(--sm-brand-wash);
  border-color: var(--sm-brand);
  box-shadow: inset 0 0 0 1px var(--sm-brand);
}

.sm-name-form:has(input:focus-visible) {
  box-shadow: var(--sm-focus);
}

.sm-name-form:has(input:checked:focus-visible) {
  box-shadow: inset 0 0 0 1px var(--sm-brand), var(--sm-focus);
}

.sm-name-form__radio {
  flex: none;
  box-sizing: border-box;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  border: 2px solid var(--sm-ink-faint);
  border-radius: var(--sm-radius-pill);
  background-color: var(--sm-surface);
}

.sm-name-form:has(input:checked) .sm-name-form__radio {
  border: 5px solid var(--sm-brand-strong);
}

.sm-name-form__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sm-name-form__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sm-space-2);
}

.sm-name-form__label {
  font-size: 14px;
  line-height: 20px;
  font-weight: var(--sm-weight-semibold);
  color: var(--sm-ink);
}

.sm-name-form__badge {
  padding: 1px 6px;
  border-radius: var(--sm-radius-inline);
  background-color: var(--sm-brand-tint);
  color: var(--sm-brand-deep);
  font-size: 11px;                                 /* the design's badge (DEFERRED-FIXES #116's call) */
  line-height: 16px;
  font-weight: var(--sm-weight-semibold);
}

.sm-name-form__sub {
  font-size: var(--sm-type-small-size);
  line-height: 18px;
  color: var(--sm-ink-muted);
}

/* The detection line under the cards, and the note under the name rows. */
.sm-map-detect,
.sm-map-note {
  display: flex;
  align-items: flex-start;
  gap: var(--sm-space-2);
  margin: 0;
  font-size: var(--sm-type-small-size);
  line-height: 18px;
  color: var(--sm-ink-body);
  text-wrap: pretty;
}

.sm-map-note {
  padding-left: 14px;
  color: var(--sm-ink-muted);
}

.sm-map-note[hidden] {
  display: none;
}

.sm-map-detect .sm-icon,
.sm-map-note .sm-icon {
  flex: none;
  margin-top: 1px;
  color: var(--sm-brand-deep);
}

.sm-map-hint {
  padding: var(--sm-space-4);
  border: 1px dashed var(--sm-line-strong);
  border-radius: var(--sm-radius-control);
  font-size: 14px;
  line-height: 20px;
  color: var(--sm-ink-muted);
  text-align: center;
}

.sm-map-hint[hidden] {
  display: none;
}

/* Add Field and the footer: dashed rule, then the solid one, 20px above each. */
.mapping-container .add-field-row {
  margin-top: 0;
  padding-top: 20px;
  border-top: 1px dashed var(--sm-line-strong);
}

.mapping-container .mapping-footer {
  margin-top: var(--sm-space-5);
  padding-top: 20px;
}

/* The toast: two sentences when both rules fail, one per line. */
.sm-toast-line {
  display: block;
}

/* ---- Switching with name fields mapped ---------------------------------------
   Centred on the content, right of the 72px rail (the toast's axis). */
.sm-map-confirm {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  padding: var(--sm-space-4) var(--sm-space-4) var(--sm-space-4) calc(72px + var(--sm-space-4));
  background-color: var(--sm-scrim);
}

.sm-map-confirm[hidden] {
  display: none;
}

@media (max-width: 768px) {
  .sm-map-confirm {
    padding-left: var(--sm-space-4);
  }
}

.sm-map-confirm__panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-sizing: border-box;
  width: 460px;
  max-width: 100%;
  padding: var(--sm-space-5);
  background-color: var(--sm-surface);
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e3);
}

.sm-map-confirm__title {
  margin: 0;
  font-size: 18px;
  line-height: 26px;
  font-weight: var(--sm-weight-semibold);
  color: var(--sm-ink);
}

.sm-map-confirm__text {
  margin: 0;
  font-size: 14px;
  line-height: 21px;
  color: var(--sm-ink-body);
  text-wrap: pretty;
}

.sm-map-confirm__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sm-space-3);
  margin-top: 10px;
}

.sm-map-confirm__actions .btn-back,
.sm-map-confirm__actions .btn-next {
  min-height: 40px;
  padding: 0 20px;
}

.sm-map-confirm__actions .btn-back:focus-visible,
.sm-map-confirm__actions .btn-next:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

/* ==========================================================================
   42. UPLOAD STATUS: ROWS THAT WERE NOT UPLOADED            DEFERRED-FIXES #145
   --------------------------------------------------------------------------
   The backend refuses rows one by one (an unrecognised customer_type, an
   empty row, the max-rows limit) and lists them, when the job ends, in
   job-status's summary.parsing_errors. upload_status.html's renderRefused()
   prints each line as it comes, under the report rows, the first 10 with a
   control for the rest. Hidden while the job runs and when the list is
   empty, so a clean upload looks exactly as before.

   It is the last child of .four-rectangular-row (a flex column), so it
   takes that column's left edge and width. upload_styles.css gives each
   .rect-box margin-left: 20px from 1024 up (11.2 zeroes it below), and the
   panel follows the same edge.
   ========================================================================== */

.four-rectangular-row > .sm-refused {
  box-sizing: border-box;
  width: 100%;
  margin: var(--sm-space-3) 0 0 20px;              /* + the row's 15px gap; 20px = .rect-box's edge */
  padding: var(--sm-space-5);
  background-color: var(--sm-surface);
  border: 1px solid var(--sm-line);
  border-radius: var(--sm-radius-card);
  box-shadow: var(--sm-e1);
  color: var(--sm-ink-body);
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
}

.four-rectangular-row > .sm-refused[hidden] {
  display: none;
}

.sm-refused .sm-refused__title {
  display: flex;
  align-items: center;
  gap: var(--sm-space-2);
  margin: 0 0 var(--sm-space-3);
  color: var(--sm-ink);
  font-size: var(--sm-type-subtitle-size);
  line-height: var(--sm-type-subtitle-leading);
  font-weight: var(--sm-type-subtitle-weight);
  font-variant-numeric: tabular-nums;
}

.sm-refused .sm-refused__title .sm-icon {
  flex: none;
  color: var(--sm-danger-solid);
}

.sm-refused .sm-refused__list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--sm-line);
}

.sm-refused .sm-refused__line {
  padding: var(--sm-space-2) 0;
  border-bottom: 1px solid var(--sm-line);
  color: var(--sm-ink-body);
  font-size: var(--sm-type-body-size);
  line-height: var(--sm-type-body-leading);
  overflow-wrap: anywhere;                         /* a long refusal message or value list wraps */
}

.sm-refused .sm-refused__line[hidden] {
  display: none;
}

.sm-refused .sm-refused__toggle {
  margin: var(--sm-space-3) 0 0;
  padding: var(--sm-space-1) var(--sm-space-2);
  margin-left: calc(-1 * var(--sm-space-2));       /* the text lines up with the lines above */
  background: none;
  border: 0;
  border-radius: var(--sm-radius-control);
  color: var(--sm-brand-strong);
  font-family: inherit;
  font-size: var(--sm-type-body-strong-size);
  line-height: var(--sm-type-body-strong-leading);
  font-weight: var(--sm-type-body-strong-weight);
  cursor: pointer;
}

.sm-refused .sm-refused__toggle:hover {
  color: var(--sm-brand-deep);
  text-decoration: underline;
}

.sm-refused .sm-refused__toggle:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

.sm-refused .sm-refused__toggle[hidden] {
  display: none;
}

@media (max-width: 1023px) {
  .four-rectangular-row > .sm-refused {
    margin-left: 0;                                /* 11.2 zeroes .rect-box's 20px here */
  }
}

/* ==========================================================================
   43. MAPPING: A FILE COLUMN IS USED ONCE                  DEFERRED-FIXES #147
   A column mapped in one slot shows in every other slot's list as
   unavailable, with a note naming where it is used. mapping.html's
   refreshTaken() sets .sm-map-taken and aria-disabled, and a click on it
   does nothing. The option's text is now a span (.sm-map-option) so the
   note can sit under it.
   ========================================================================== */

.mapping-rows .custom-dropdown-item.sm-map-taken {
  cursor: not-allowed;
  color: var(--sm-ink-muted);                      /* readable: the column name still matters */
  background-color: var(--sm-surface-sunk);
}

.mapping-rows .custom-dropdown-item.sm-map-taken:hover {
  background-color: var(--sm-surface-sunk);        /* no hover wash: it cannot be chosen */
}

.mapping-rows .custom-dropdown-item .sm-map-option {
  display: block;
  overflow-wrap: anywhere;
}

.mapping-rows .sm-map-taken-note {
  display: block;
  margin-top: 2px;
  font-size: var(--sm-type-small-size);
  line-height: var(--sm-type-small-leading);
  font-weight: var(--sm-weight-regular);
  color: var(--sm-ink-muted);
}

/* ==========================================================================
   44. ADDRESS: OPEN IN GOOGLE MAPS                       DEFERRED-FIXES #149
   Ported from service-titan-prod 4d32483 (live on ClearRiver), whose CSS
   sat in name_selection_styles.css. The link sits inside the Address 1
   value cell, so each .line keeps two children: the page script reads
   .line-1 p:nth-child(2)'s textContent, and the icon adds no text. The
   cell is the flex row, so the icon parks at the right edge. A sprite icon
   in currentColor replaces the white PNG and its brightness(0) filter (§9).
   ========================================================================== */

.line p.address-1-value {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sm-space-2);
}

.line p.address-1-value .map-link {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--sm-brand-strong);                   /* interactive: brand-strong */
  border-radius: var(--sm-radius-control);
}

.line p.address-1-value .map-link:hover {
  color: var(--sm-brand-deep);
}

.line p.address-1-value .map-link:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

/* ==========================================================================
   45. MAPPING: REMOVE AN ADDED ROW                        DEFERRED-FIXES #150
   Ported from service-titan-prod 425ad16 (live on ClearRiver), whose CSS sat
   in mapping_styles.css. Added Email / Phone rows and the Address 1 row of
   an added address group carry .row-remove-btn (mapping.html). From 769 up
   it is centred in the card's right padding, so no grid column moves; at
   768 and below there is no padding to use, so once an added row exists
   every row and the column header take a fourth 24px track.
   ========================================================================== */

#mappingRows .mapping-row {
  position: relative;                              /* anchor for the button */
}

#mappingRows .row-remove-btn {
  position: absolute;
  top: 50%;
  right: -32px;                                    /* centred in the 40px padding */
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--sm-radius-control);
  background: transparent;
  color: var(--sm-ink-quiet);
  cursor: pointer;
}

#mappingRows .row-remove-btn:hover {
  color: var(--sm-danger-ink);
  background-color: var(--sm-danger-tint);
}

#mappingRows .row-remove-btn:focus-visible {
  outline: none;
  box-shadow: var(--sm-focus);
}

@media (max-width: 1023px) {
  #mappingRows .row-remove-btn {
    right: -28px;                                  /* centred in the 32px padding */
  }
}

@media (max-width: 768px) {
  #mappingRows:has(.row-remove-btn) .mapping-row,
  .mapping-container:has(#mappingRows .row-remove-btn) .mapping-col-header {
    grid-template-columns: 1fr 30px 1fr 24px;      /* mapping_styles.css's three, plus the button */
  }

  #mappingRows .row-remove-btn {
    position: static;
    transform: none;
  }
}

/* ==========================================================================
   46. PAGE GROUND BESIDE THE CAPPED CONTENT                 DEFERRED-FIXES #154
   From service-titan-prod b6b136b, which painted body in base_styles.css
   and mapping_styles.css. On Home, Matching, Name, Address and Mapping the
   content is capped at 1680px and body was transparent, so at 2400px (or
   zoomed out on a 1440 screen) white bands showed beside the page. One rule
   here instead: body takes the page background on every page. It also
   replaces upload_styles.css's #f5f5f5 with the token; login's own rule
   (body:has(.login-container), section 24) is the same value.
   ========================================================================== */

body {
  background-color: var(--sm-surface-page);
}
