/* ============================================================================
   cand-header.css — stylesheet for app/cand-header.jsx: the candidate record's
   IDENTITY HEADER (pieces 1 and 2 of docs/CANDIDATE-CARD-REFERENCE.md).

   WHY THE LAYOUT IS A GRID AND NOT A FLEX ROW. What shipped was one flex row -
   [avatar][grow column] - whose inner row wrapped freely. At 390 that produced
   the four fragments the reference doc measured: the name on one line, the
   score chip on the next, the 56px avatar vertically centred against the
   whole 172px stack (so it sat mid-left, BELOW the name), and the owner chip
   parked at the far right of whichever line it wrapped onto, because
   `.e8-owner-id` carries `margin-left: auto`. Nothing was wrong with any one
   rule; the composition had no plan for narrow.

   A 2-column grid - mark | everything else - cannot produce that. The mark is
   `align-self: start`, so it is always beside the FIRST line whatever the
   second column does, and all wrapping is pushed down into the chip row and
   the fact row, where wrapping is the intended behaviour.

   SIZE-RESPONSIVE VIA @container, NEVER @media. The resizable rail squeezes
   `.e8-content.e8-rbody-content` without touching the viewport, and that
   element is `container-type: inline-size` - so an UNNAMED container query
   here resolves against the content column itself, which is the thing this
   header actually has to fit. (`e8rec` names `.e8-recordbody`, one level out,
   which still includes the rail's width. Nearest-container is the more
   accurate target of the two.) Measured: content column = 380px at a 390
   viewport, 824 at 834, 778 at 1280 with the rail at its 192px default.

   TWO QUERIES, BECAUSE THEY ASK DIFFERENT QUESTIONS. LAYOUT asks how much room
   this header has, which is the content column: unnamed, at 560. TOUCH TARGETS
   ask which shell is being served, and the content column cannot answer that -
   it is WIDER on a tablet (824 at 834) than on a desktop (778 at 1280), so any
   threshold on it either makes the desktop thumb-sized or the tablet
   mouse-sized. The record area does answer it, because the sidebar only exists
   on the desktop shell: `e8rec` is 834 on the tablet and 1248 at 1440. Hence
   `@container e8rec (min-width: 901px)` at the bottom, and nothing else in this
   file keys on it. Both measured in-browser on `#/candidate/c-okafor`.

   ORDER. Every conditional block sits AFTER the rules it overrides - a
   conditional at-rule adds no specificity, so one written above its own base
   rule is dead code (three shipped that way before the lint gate).

   Prefix: `.e8-cand-hdr-` was the placeholder this file reserved; the module's
   markup went out under `.e8-cand-header-` and that is what is used here.
   Both were grepped: no other definition of either in app/app.css.

   Icon rules are written `.material-symbols-outlined.e8-cand-header-*` on
   purpose. `product.css:267 .elev8 .material-symbols-outlined` is (0,2,0) and
   sets font-size AND color on every glyph in the app; a single class at (0,1,0)
   loses to it no matter how late this file loads.
   ============================================================================ */

/* ---- the block -------------------------------------------------------- */
/* Narrow is the BASE. Everything below is what a phone gets; the container
   query at the bottom is the widening. */
.e8-cand-header {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 12px;
  row-gap: 6px;
  align-items: start;
  margin: 0 0 12px;
}

/* The mark. Sized to the type, not to the space - 40px against an 18px name.
   The DS `Avatar` component is deliberately not used here: it writes
   width/height/font-size as INLINE styles, which no class rule can override,
   and this avatar has to change size with the container. `.e8-avatar` still
   supplies the shape, tint and ink, so it is the DS avatar in everything but
   its measurements. */
.e8-cand-header-av {
  align-self: start;
  width: 40px;
  height: 40px;
  font-size: var(--ui-text-md);
  line-height: 1;
}

.e8-cand-header-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* ---- line 1: name + the chips that say where this person stands -------- */
.e8-cand-header-idline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 8px;
  row-gap: 6px;
  min-width: 0;
}

/* `h1.` and not `.` alone: product.css's `.elev8 h1` is (0,1,1) and sets
   font-size, line-height and letter-spacing. A bare class is (0,1,0) and would
   lose. Matching its specificity and loading later is what wins - this file is
   linked after styles.css in "ELEV8 ATS.html". */
h1.e8-cand-header-name {
  margin: 0;
  min-width: 0;
  font-size: var(--ui-text-xl);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.018em;
  color: var(--ui-text);
  overflow-wrap: break-word;
}

.e8-cand-header-stage {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: var(--ui-radius-pill);
  background: var(--ui-fill);
  color: var(--ui-text-secondary);
  font-size: var(--ui-text-sm);
  font-weight: 600;
  white-space: nowrap;
}

/* ---- the score chip, which is a button ---------------------------------
   Tier colour arrives as three custom properties set inline on the element
   (`--e8-cand-header-fit*`), because the tier is DATA - it is the record's
   score band, not a style decision. Fallbacks keep the chip legible if the
   tier tokens ever move. */
.e8-cand-header-score {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  min-height: 44px; /* the mobile touch target; the wide container relaxes it below */
  padding: 4px 8px 4px 11px;
  border: 1px solid var(--e8-cand-header-fitbr, var(--ui-border));
  border-radius: var(--ui-radius-pill);
  background: var(--e8-cand-header-fitbg, var(--ui-fill));
  color: var(--e8-cand-header-fit, var(--ui-text));
  font-family: inherit;
  cursor: pointer;
  text-align: left;
}
.e8-cand-header-score:hover { border-color: var(--e8-cand-header-fit, var(--ui-border)); }
/* NO `:focus-visible` rule here, on purpose, and this file had one until it was
   measured. The house ring is app-wide and lives in product.css:259 -
   `.elev8 :is(button, a, input, select, textarea, [tabindex]):focus-visible
   { outline: none; box-shadow: var(--ui-focus-ring) }` - at (0,2,0); the
   earlier note here cited app/app.css and (0,2,1), which is the wrong file and
   the wrong count (`:is()` takes the specificity of its most specific
   argument, and every argument here is a type selector). The conclusion is
   unchanged and is why there is still no local rule: a
   `.e8-cand-header-score:focus-visible` outline is (0,1,0), it loses, the
   `outline: none` wins, and the rule would be dead code that also disagrees
   with the house ring. Verified on the focused button: box-shadow computes to
   `rgb(255,255,255) 0 0 0 2px, rgb(82,21,252) 0 0 0 4px`. Same for the pin and
   the popover's breakdown link - both are native <button>. */
.e8-cand-header-scoren {
  align-self: center;
  font-size: var(--ui-text-xl);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.01em;
}
/* The band as WORDS. Colour is the second signal, never the only one. */
.e8-cand-header-scoreband {
  align-self: center;
  font-size: var(--ui-text-sm);
  font-weight: 600;
  white-space: nowrap;
}
.material-symbols-outlined.e8-cand-header-scoreic {
  align-self: center;
  font-size: var(--ui-icon-xs);
  color: inherit;
  opacity: 0.75;
}

/* ---- the SAME SLOT, when there is no score yet -------------------------
   `CandHeaderScore` renders either the score chip above or `.e8-nomatch-chip`
   (app.css:5873) - one slot, one question ("where does this person stand on
   fit"), and the two answers were built as different objects. Measured on
   #/candidate/c-silva at 834 against #/candidate/c-okafor: 26.1px tall,
   transparent, tertiary, against 44px, tinted, bordered, secondary-or-tier ink.
   So the header line changed height depending on whether a match had been run,
   and the unscored state - the one that is asking you to do something - was the
   quietest thing on the line, at 2.72:1 in light mode.

   The DASHED border stays: dashed means "provisional, not filled in yet" all
   over this record (`.e8-cand-overview-psmini`, `.e8-cand-rec-msgearlier`,
   `.e8-cand-rec-msgdraft`, `.e8-cand-overview-psneed-more`), and it is the one
   thing that should differ between a score and the absence of one. What is
   unified is the BOX and the INK.

   Scoped on this module's own row rather than overriding `.e8-nomatch-chip`
   bare. Reach: `.e8-cand-header-idline` is emitted by `CandIdentityHeader` and
   nowhere else, and `grep -rn e8-nomatch-chip app components` returns exactly
   one call site - the one inside it. (0,2,0) beats app.css's (0,1,0). */
.e8-cand-header-idline .e8-nomatch-chip {
  min-height: 44px;
  padding: 4px 11px;
  color: var(--ui-text-secondary);
}

/* ---- the pin, sized as a TOUCH target ----------------------------------
   Anchored on this module's own wrapper class, never a bare `.e8-btn` rule:
   the pin is a DS ghost icon button and `.e8-btn-icononly.e8-btn-sm` fixes it
   at 24x24 app-wide (components/ats-button/Button.jsx), which is fine under a
   mouse and half a thumb. Growing the box here costs the header nothing,
   because the score chip already sets this row's height at 44. The wide-shell
   block at the bottom takes it back to pointer size. */
.e8-cand-header-pin { display: inline-flex; align-items: center; }
.e8-cand-header-pin .e8-btn.e8-btn-icononly.e8-btn-sm { width: 44px; height: 44px; }

/* ---- line 2: the qualifying facts -------------------------------------- */
.e8-cand-header-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 13px;
  row-gap: 2px;
  min-width: 0;
  /* Load-bearing, not tidiness: this is what clips a separator that lands at
     the START OF A WRAPPED LINE. See the separator rule below. */
  overflow: hidden;
  font-size: var(--ui-text-base);
  line-height: 1.45;
  color: var(--ui-text-secondary);
}
.e8-cand-header-fact {
  position: relative;
  min-width: 0;
  /* The facts stay wrappable - a nowrap fact would overflow the column on a
     long title. `anywhere` rather than `break-word` because the row now clips
     its overflow, so an unbreakable token would be cut off instead of spilling
     visibly. */
  overflow-wrap: anywhere;
}
/* THE ORPHANED INTERPUNCT, fixed geometrically.
   A generated separator cannot outlive its content, but it CAN outlive its
   line: the previous rule glued "·\00a0" to the front of every fact after the
   first, so at 390 the third fact wrapped and line 2 opened with
   "· 8y experience" flush against the column edge (measured on c-okafor and
   c-mills). Binding the dot to the PRECEDING fact instead just mirrors the
   defect into a dangling "·" at the end of line 1, which is the older
   `Screening ·` bug again. No inline separator survives a wrap in either
   direction, and CSS has no "first on its line" selector.
   So the dot is not inline at all. It is absolutely positioned OUTSIDE the
   fact's box (`inset-inline-end: 100%` - the logical form, so the dot stays on
   the reading-order side), sitting in the flex `column-gap`. A fact that
   begins a line has its dot at negative x relative to the row's padding box -
   i.e. outside it - where the row's `overflow: hidden` clips it. A fact that
   sits mid-line has its dot inside the gap, where it is meant to be. One rule,
   both line ends, no JS measuring. */
.e8-cand-header-fact + .e8-cand-header-fact::before {
  content: '·';
  position: absolute;
  inset-inline-end: 100%;
  margin-inline-end: 5px;
  color: var(--ui-text-tertiary);
}

/* ---- the popover footer (portaled to <body>, so no container context) --- */
.e8-cand-header-whyfoot {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
/* Says what the +/- column IS. WhyPopover renders every reason as a signed
   number, so a "Why 94" title over +3 +2 +2 +1 -1 reads as a ledger that is 87
   short. It is not a ledger: the score comes from the ranking row and the
   weights from e8ReasonWeight's tier grading (see the JSX). */
/* SECONDARY, not the tertiary its neighbours inherit. `.e8-whypop-foot` sets
   tertiary at 11.5px, which measures 2.72:1 on the popover's white in light
   mode - fine for a job-id stamp nobody has to read, wrong for the one line
   that stops the +/- column being misread as a total. Secondary measures
   5.03:1 on white and 7.33:1 on the dark popover, both AA at this size. (The
   DS foot's own 2.72 is pre-existing and lives with WhyPopover, not here.)
   All four ratios measured in-browser on #/candidate/c-okafor, 1440x900. */
.e8-cand-header-whynote {
  color: var(--ui-text-secondary);
  font-size: var(--ui-text-xs);
  line-height: 1.4;
}
/* `baseline`, not `center`: the job label wraps to two lines on a real record
   (measured: "JO-10864 · Technical Project Manager" at 390), and centring the
   link against a two-line block floats it opposite the gap between them. */
.e8-cand-header-whyrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.e8-cand-header-whyjob { min-width: 0; }
.e8-cand-header-whylink {
  flex: none;
  display: inline-flex;
  align-items: center;
  /* 17px tall was a link pretending to be a control. The padding is the whole
     fix - the type stays 11.5px, the row grows to >=24. */
  min-height: 24px;
  padding: 4px 0;
  border: 0;
  background: none;
  color: var(--ui-accent);
  font-family: inherit;
  font-size: var(--ui-text-xs);
  font-weight: 600;
  text-align: right;
  cursor: pointer;
}
.e8-cand-header-whylink:hover { text-decoration: underline; }

/* ---- WIDER CONTENT COLUMN ---------------------------------------------
   Unnamed query => nearest container => `.e8-content.e8-rbody-content`, the
   content column, which is what this header lives in. 560px is where the name
   line and the fact line both stop wrapping on the measured record. AFTER the
   base rules, deliberately: a conditional at-rule adds no specificity, so
   above them it would never apply. */
@container (min-width: 560px) {
  .e8-cand-header {
    column-gap: 14px;
    row-gap: 2px;
    margin-bottom: 14px;
  }
  .e8-cand-header-main { gap: 2px; }
  .e8-cand-header-av {
    width: 48px;
    height: 48px;
    font-size: var(--ui-text-xl);
  }
  .e8-cand-header-idline { column-gap: 10px; }
}

/* ---- DESKTOP SHELL: relax the touch targets ---------------------------
   Deliberately keyed on the NAMED `e8rec` container (`.e8-recordbody`) and not
   on the content column, because the content column CANNOT tell a tablet from a
   desktop: it measures 824px inside an 834 viewport and 778px at 1280, where
   the 192px sidebar and the rail take the difference back. The record area does
   separate them - 834 on the touch shell (which is also where the app hands out
   a 58px bottom tab bar and a FAB) against 1248 at a 1440 viewport.
   Everything above stays thumb-sized; this is the only place that shrinks a
   target, and it shrinks it only where the shell itself has stopped being a
   touch shell. AFTER the base rules, as every conditional block here must be.

   THE NUMBER IS 841, NOT THE 901 THIS SHIPPED WITH, BECAUSE THE RECORD HAS ONE
   TOUCH SWITCH AND THIS WAS THE ONLY MODULE NOT USING IT. cand-overview.css and
   cand-tabs.css both key their touch geometry on `@container e8rec (max-width:
   840px)`, and cand-record.css reads `.e8-recordbody.is-narrow`, which
   record-shell.jsx sets at `<= 840` - the same line. 901 left an e8rec band of
   841-900 (roughly a 1050-1110px window, an ordinary laptop) in which the tab
   strip, the prescreen CTA and the "+N more" chip had already gone to mouse
   sizes while the header's score chip was still 44px tall and its pin still a
   44px square beside them. One number, four files. (Sub-pixel caveat, stated
   rather than hidden: a container measuring between 840.0 and 841.0 exclusive
   matches neither query, and falls to the thumb-sized base - the safe side.) */
@container e8rec (min-width: 841px) {
  .e8-cand-header-score {
    min-height: 30px;
    padding: 2px 8px 2px 11px;
  }
  /* The unscored state relaxes with the scored one, so the header line is the same
     height whichever of the two the record has. */
  .e8-cand-header-idline .e8-nomatch-chip {
    min-height: 30px;
    padding: 2px 11px;
  }
  .e8-cand-header-pin .e8-btn.e8-btn-icononly.e8-btn-sm { width: 28px; height: 28px; }
}

/* ---- THE RECORD TOOLBAR, ON THE TOUCH SHELL ----------------------------
   The two header groups this module contributes to the Topbar - the prev/next
   navigator (`CandHeaderNav`) and the list / Actions / primary trio
   (`CandHeaderActions`) - were built entirely from `size="sm"`, which the DS
   fixes at 24px tall (`.e8-btn-sm`, components/ats-button/Button.jsx:15) and
   24x24 square for the icon-only case. Measured on #/candidate/c-okafor:
   "Submit to job" 114x24 and "Actions" 82x24 at BOTH 390x844 and 834x1112, with
   the two chevrons at 24x24 beside them. §2.8 and §3 row 15 of
   docs/CANDIDATE-CARD-REFERENCE.md set 44px as the floor for touch, and §2.2
   singles this row out as "the primary action is always reachable" - so the one
   row that must never be missed was the smallest target on the record. Every
   control INSIDE the card already clears it (score chip 44, pin 44, tabs 44).

   WHY `@media` AND NOT `@container`, IN A FILE WHOSE OTHER TWO CONDITIONALS ARE
   CONTAINER QUERIES. The Topbar is not in the record. `.e8-topbar` is a sibling
   of `.e8-recordbody` inside `.e8-main` (shell.jsx:2588), so it is OUTSIDE the
   only `container-type` on this page (`.e8-recordbody`, app.css:2322) and
   outside the content column - an unnamed container query here would resolve
   against no container at all and never match, and `e8rec` is not an ancestor.
   The rail also cannot squeeze this bar, which is the reason the container rule
   exists: the bar spans `.e8-main` whatever the rail does. So the viewport IS
   the honest question here, and the answer has to come from a media query.

   840 IS THE APP'S OWN TOUCH SWITCH, NOT A NEW NUMBER. app.css:3420
   `@media (max-width: 840px)` is where the sidebar is dropped (`.e8-side {
   display: none }`), the 58px bottom tab bar and the FAB appear, and the topbar
   pills are hidden. At 834 the app has already decided this is a phone; a 24px
   toolbar on top of a bottom tab bar is the incoherence the 834 report named.
   It is also the same boundary as the `min-width: 841px` block above and as
   cand-overview.css / cand-tabs.css / `.e8-recordbody.is-narrow` - one number,
   now five files. Above 840 nothing here applies and the toolbar is unchanged.

   REACH. `.e8-cand-header-act`, `-actwrap` and `-nav` are emitted by
   cand-header.jsx and nowhere else (grepped across app/ and components/), so
   these rules cannot touch the client, contact, consultant or job records that
   share `.e8-btn-sm`, `.e8-rec-nav` and `.e8-menubtn-wrap`. That is why they
   exist: `.e8-btn-sm { height: 24px }` and `.e8-rec-nav { width: 24px }` are
   app-wide, and the only safe way to beat them is a local anchor class.
   Specificity: `.e8-btn.e8-cand-header-act` is (0,2,0) against `.e8-btn-sm`'s
   (0,1,0); the icon-only rule needs (0,3,0) because
   `.e8-btn-icononly.e8-btn-sm` is itself (0,2,0) and injected into <head> by the
   DS at runtime, i.e. after this linked sheet, so a tie would lose.

   AFTER every rule it overrides, as every conditional block in this file is. */

/* Unconditional, because the wrapper element is new at every width: it must be
   the same flex item the bare MenuButton was, or `.e8-topbar-actions`
   (app.css:644, `display: flex; align-items: center`) would lay out an inline
   box with a baseline instead of a centred control. */
.e8-cand-header-actwrap { display: inline-flex; align-items: center; }

@media (max-width: 840px) {
  /* `min-height`, not `height`: it clamps the DS's `height: 24px` upward
     without this file having to restate a size the DS owns. */
  .e8-btn.e8-cand-header-act,
  .e8-cand-header-actwrap .e8-btn {
    min-height: 44px;
    padding-inline: 12px;
  }
  .e8-btn.e8-btn-icononly.e8-cand-header-act { width: 44px; padding-inline: 0; }
  .e8-rec-nav.e8-cand-header-nav { width: 44px; height: 44px; }
  /* NO icon-size rule here, and this block had one until it was measured. The
     glyphs looked small against a 44px box, so a `font-size: var(--ui-icon-xs)`
     pair went in at (0,3,0) to beat Button.jsx's hard-coded 14px - and computed
     to exactly what was already there. `.elev8 .e8-btn-sm.e8-btn
     .material-symbols-outlined` (app.css) is ALSO (0,3,0), already sets
     --ui-icon-xs, and already wins app-wide: read back off the live button, the
     glyph is 16px at 1440 (rule inactive) and 16px at 390 (rule active). A
     declaration that cannot change a computed value is dead code whatever it
     looks like in review, so it is gone rather than kept "for clarity". */
}

/* ---- the popover's breakdown link, on the touch shell ------------------
   24px was sized for a mouse (see the base rule) and the 390 report is right
   that it is the one sub-44 control left in the popover. `@media` again, and
   here it is forced rather than chosen: `DSc.WhyPopover` PORTALS to <body>
   (components/ats-badge/Badge.jsx), so the popover has no container ancestor at
   all - `.e8-recordbody` is not above it in the tree. AFTER the base rule. */
@media (max-width: 840px) {
  .e8-cand-header-whylink { min-height: 44px; }
}
